From a6f0fcdda906daf81d9598a0304596957ebb1570 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 27 Mar 2020 23:36:14 +0100 Subject: [PATCH 01/23] Starting with webpack --- shared/js/{ui => channel-tree}/channel.ts | 0 shared/js/{ui => channel-tree}/client.ts | 0 shared/js/{ui => channel-tree}/server.ts | 0 shared/js/{ui => channel-tree}/view.ts | 0 shared/js/crypto/uid.ts | 0 shared/js/ui/channel-tree/channel.scss | 0 shared/js/ui/channel-tree/channel.tsx | 0 shared/js/ui/channel-tree/colors.scss | 0 shared/js/ui/channel-tree/tree.scss | 0 shared/js/ui/channel-tree/tree.tsx | 0 shared/js/ui/elements/tab.ts | 161 ---------------------- webpack.config.js | 76 ++++++++++ 12 files changed, 76 insertions(+), 161 deletions(-) rename shared/js/{ui => channel-tree}/channel.ts (100%) rename shared/js/{ui => channel-tree}/client.ts (100%) rename shared/js/{ui => channel-tree}/server.ts (100%) rename shared/js/{ui => channel-tree}/view.ts (100%) create mode 100644 shared/js/crypto/uid.ts create mode 100644 shared/js/ui/channel-tree/channel.scss create mode 100644 shared/js/ui/channel-tree/channel.tsx create mode 100644 shared/js/ui/channel-tree/colors.scss create mode 100644 shared/js/ui/channel-tree/tree.scss create mode 100644 shared/js/ui/channel-tree/tree.tsx delete mode 100644 shared/js/ui/elements/tab.ts create mode 100644 webpack.config.js diff --git a/shared/js/ui/channel.ts b/shared/js/channel-tree/channel.ts similarity index 100% rename from shared/js/ui/channel.ts rename to shared/js/channel-tree/channel.ts diff --git a/shared/js/ui/client.ts b/shared/js/channel-tree/client.ts similarity index 100% rename from shared/js/ui/client.ts rename to shared/js/channel-tree/client.ts diff --git a/shared/js/ui/server.ts b/shared/js/channel-tree/server.ts similarity index 100% rename from shared/js/ui/server.ts rename to shared/js/channel-tree/server.ts diff --git a/shared/js/ui/view.ts b/shared/js/channel-tree/view.ts similarity index 100% rename from shared/js/ui/view.ts rename to shared/js/channel-tree/view.ts diff --git a/shared/js/crypto/uid.ts b/shared/js/crypto/uid.ts new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/channel-tree/channel.scss b/shared/js/ui/channel-tree/channel.scss new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/channel-tree/channel.tsx b/shared/js/ui/channel-tree/channel.tsx new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/channel-tree/colors.scss b/shared/js/ui/channel-tree/colors.scss new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/channel-tree/tree.scss b/shared/js/ui/channel-tree/tree.scss new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/channel-tree/tree.tsx b/shared/js/ui/channel-tree/tree.tsx new file mode 100644 index 00000000..e69de29b diff --git a/shared/js/ui/elements/tab.ts b/shared/js/ui/elements/tab.ts deleted file mode 100644 index fb34577c..00000000 --- a/shared/js/ui/elements/tab.ts +++ /dev/null @@ -1,161 +0,0 @@ -/// - -interface JQuery { - asTabWidget(copy?: boolean) : JQuery; - tabify(copy?: boolean) : this; - - changeElementType(type: string) : JQuery; -} - - -if(typeof (customElements) !== "undefined") { - try { - class X_Tab extends HTMLElement {} - class X_Entry extends HTMLElement {} - class X_Tag extends HTMLElement {} - class X_Content extends HTMLElement {} - - customElements.define('x-tab', X_Tab, { extends: 'div' }); - customElements.define('x-entry', X_Entry, { extends: 'div' }); - customElements.define('x-tag', X_Tag, { extends: 'div' }); - customElements.define('x-content', X_Content, { extends: 'div' }); - } catch(error) { - console.warn("failed to define costum elements"); - } -} else { - console.warn(tr("Could not defied tab customElements!")); -} - -var TabFunctions = { - tabify(template: JQuery, copy: boolean = true) : JQuery { - console.log("Tabify: copy=" + copy); - console.log(template); - - let tag = $.spawn("div"); - tag.addClass("tab"); - - let header = $.spawn("div"); - header.addClass("tab-header"); - - let content = $.spawn("div"); - content.addClass("tab-content"); - - content.append($.spawn("div").addClass("height-watcher")); - - let silentContent = $.spawn("div"); - silentContent.addClass("tab-content-invisible"); - - /* add some kind of min height */ - const update_height = () => { - const height_watcher = tag.find("> .tab-content .height-watcher"); - const entries: JQuery = tag.find("> .tab-content-invisible x-content, > .tab-content x-content"); - console.error(entries); - let max_height = 0; - - entries.each((_, _e) => { - const entry = $(_e); - const height = entry.visible_height(); - if(height > max_height) - max_height = height; - }); - - height_watcher.css('min-height', max_height + "px"); - tag.find(".window-resize-listener").trigger('resize'); - }; - - template.find("x-entry").each( (_, _entry) => { - const entry = $(_entry); - - const tag_header = $.spawn("div").addClass("entry"); - const tag_content = copy ? entry.find("x-content").clone(true, true) : entry.find("x-content"); - - { - const header_tag = entry.find("x-tag"); - const header_data = copy ? header_tag.contents().clone(true, true) : header_tag.contents(); - - if(header_tag.attr("x-entry-class")) - tag_header.addClass(header_tag.attr("x-entry-class")); - if(header_tag.attr("x-entry-id")) - tag_header.attr("x-id", header_tag.attr("x-entry-id")); - - tag_header.append(header_data); - - /* listener if the tab might got removed */ - tag_header.addClass("window-resize-listener"); - tag_header.on('resize', event => { - if(!tag_header.is(':visible') && tag_header.hasClass('selected')) { - let element = tag_header.next('.entry:visible'); - if(element.length == 0) - element = tag_header.prev('.entry:visible'); - if(element.length == 0) { - tag_header.removeClass("selected"); - tag_content.hide(); - } else { - element.first().trigger('click'); - } - console.log("Next: %o", tag_header.next('.entry:visible')); - console.log("prev: %o", tag_header.prev('.entry:visible')); - } - }); - } - - content.append(tag_content.hide()); - - tag_header.on("click", () => { - if(tag_header.hasClass("selected")) return; - - tag.find(".tab-header .selected").removeClass("selected"); - tag_header.addClass("selected"); - - content.find("> x-content").hide(); - /* don't show many nodes at once */ - let entries = tag_content.find(".tab-show-partitional"); - entries.hide(); - const show_next = index => { - console.log("Show " + index); - if(index >= entries.length) return; - entries.eq(index).show(); - - setTimeout(show_next.bind(undefined, index + 1), 0); - }; - show_next(0); - - tag_content.trigger('show'); - tag_content.show(); - }); - - console.log(this); - header.append(tag_header); - }); - - setTimeout(() => header.find(".entry").first().trigger("click"), 0); - - tag.append(header); - tag.append(content); - tag.append(silentContent); - - tag.on('tab.resize', update_height); - return tag; - } -}; - -if(!$.fn.asTabWidget) { - $.fn.asTabWidget = function (copy?: boolean) : JQuery { - if($(this).prop("tagName") == "X-TAB") - return TabFunctions.tabify($(this), typeof(copy) === "boolean" ? copy : true); - else { - throw "Invalid tag! " + $(this).prop("tagName"); - } - } -} - -if(!$.fn.tabify) { - $.fn.tabify = function (this: JQuery, copy?: boolean) { - const wrapped_tag = $.spawn("div").append(this); - wrapped_tag.find("x-tab").each((_, _element) => { - const element = $(_element); - element.replaceWith(element.asTabWidget(copy)); - }); - return wrapped_tag.children(); - } -} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..3a71062c --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,76 @@ +const path = require('path'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +const isDevelopment = process.env.NODE_ENV === 'development'; +module.exports = { + entry: './src/index.tsx', + devtool: 'inline-source-map', + mode: "development", + plugins: [ + new MiniCssExtractPlugin({ + filename: isDevelopment ? '[name].css' : '[name].[hash].css', + chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }) + ], + module: { + rules: [ + { + test: /\.s[ac]ss$/, + loader: [ + //isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + sourceMap: isDevelopment + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: isDevelopment + } + } + ] + }, + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js', ".scss"], + }, + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist'), + }, + optimization: { + /* + splitChunks: { + chunks: 'async', + minSize: 1, + maxSize: 500000, + minChunks: 1, + maxAsyncRequests: 6, + maxInitialRequests: 4, + automaticNameDelimiter: '~', + automaticNameMaxLength: 30, + cacheGroups: { + defaultVendors: { + test: /[\\/]node_modules[\\/]/, + priority: -10 + }, + default: { + minChunks: 2, + priority: -20, + reuseExistingChunk: true + } + } + } + */ + } +}; \ No newline at end of file From 13b65a1f3534096796be8c888ef44ce57988dc5c Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 27 Mar 2020 23:36:57 +0100 Subject: [PATCH 02/23] Starting with webpack --- dist/bundle.js | 6186 +++++++++++++++++ package-lock.json | 3889 ++++++++++- package.json | 22 +- shared/css/static/channel-tree.scss | 123 - shared/js/BrowserIPC.ts | 4 +- shared/js/ConnectionHandler.ts | 26 +- shared/js/FileManager.ts | 19 +- shared/js/MessageFormatter.ts | 8 +- shared/js/PPTListener.ts | 4 +- shared/js/bookmarks.ts | 12 +- shared/js/channel-tree/channel.ts | 13 +- shared/js/channel-tree/client.ts | 25 +- shared/js/channel-tree/server.ts | 12 +- shared/js/channel-tree/view.ts | 6 +- shared/js/connection/CommandHandler.ts | 2208 +++--- shared/js/connection/CommandHelper.ts | 822 +-- shared/js/connection/ConnectionBase.ts | 372 +- shared/js/connection/HandshakeHandler.ts | 19 +- .../connection/ServerConnectionDeclaration.ts | 22 +- shared/js/crypto/asn1.ts | 2 +- shared/js/crypto/crc32.ts | 2 +- shared/js/crypto/hex.ts | 2 +- shared/js/crypto/sha.ts | 2 +- shared/js/crypto/uid.ts | 8 + shared/js/dns.ts | 2 +- shared/js/events.ts | 6 +- shared/js/i18n/country.ts | 3 +- shared/js/i18n/localize.ts | 15 +- shared/js/log.ts | 6 +- shared/js/main.ts | 46 +- shared/js/permission/GroupManager.ts | 21 +- shared/js/permission/PermissionManager.ts | 27 +- shared/js/profiles/ConnectionProfile.ts | 439 +- shared/js/profiles/Identity.ts | 188 +- shared/js/profiles/identities/NameIdentity.ts | 142 +- .../profiles/identities/TeaForumIdentity.ts | 216 +- .../profiles/identities/TeamSpeakIdentity.ts | 1567 ++--- .../js/profiles/identities/teaspeak-forum.ts | 23 +- shared/js/proto.ts | 2 - shared/js/settings.ts | 15 +- shared/js/sound/Sounds.ts | 8 +- shared/js/stats.ts | 4 +- shared/js/ui/channel-tree/channel.css | 81 + shared/js/ui/channel-tree/channel.css.map | 1 + shared/js/ui/channel-tree/channel.scss | 106 + shared/js/ui/channel-tree/channel.tsx | 120 + shared/js/ui/channel-tree/colors.css | 3 + shared/js/ui/channel-tree/colors.css.map | 1 + shared/js/ui/channel-tree/colors.scss | 3 + shared/js/ui/channel-tree/tree.css | 38 + shared/js/ui/channel-tree/tree.css.map | 1 + shared/js/ui/channel-tree/tree.scss | 50 + shared/js/ui/channel-tree/tree.tsx | 5 + shared/js/ui/client_move.ts | 4 +- shared/js/ui/elements/context_divider.ts | 6 +- shared/js/ui/elements/context_menu.ts | 2 +- shared/js/ui/elements/modal.ts | 94 +- shared/js/ui/elements/net_graph.ts | 2 +- shared/js/ui/elements/slider.ts | 6 +- shared/js/ui/elements/tooltip.ts | 4 +- shared/js/ui/frames/ControlBar.ts | 27 +- shared/js/ui/frames/MenuBar.ts | 10 +- shared/js/ui/frames/chat.ts | 6 +- shared/js/ui/frames/chat_frame.ts | 8 +- shared/js/ui/frames/connection_handlers.ts | 8 +- shared/js/ui/frames/hostbanner.ts | 6 +- shared/js/ui/frames/image_preview.ts | 2 +- shared/js/ui/frames/server_log.ts | 987 +-- shared/js/ui/frames/side/chat_box.ts | 2 +- shared/js/ui/frames/side/chat_helper.ts | 2 +- shared/js/ui/frames/side/client_info.ts | 9 +- shared/js/ui/frames/side/conversations.ts | 8 +- shared/js/ui/frames/side/music_info.ts | 5 +- .../ui/frames/side/private_conversations.ts | 6 +- shared/js/ui/htmltags.ts | 2 +- shared/js/ui/modal/ModalAbout.ts | 6 +- shared/js/ui/modal/ModalAvatar.ts | 6 +- shared/js/ui/modal/ModalAvatarList.ts | 6 +- shared/js/ui/modal/ModalBanClient.ts | 5 - shared/js/ui/modal/ModalBanList.ts | 7 +- shared/js/ui/modal/ModalBookmarks.ts | 6 +- shared/js/ui/modal/ModalChangeLatency.ts | 6 +- shared/js/ui/modal/ModalChangeVolume.ts | 6 +- shared/js/ui/modal/ModalChannelInfo.ts | 2 +- shared/js/ui/modal/ModalClientInfo.ts | 2 +- shared/js/ui/modal/ModalConnect.ts | 6 +- shared/js/ui/modal/ModalCreateChannel.ts | 4 +- shared/js/ui/modal/ModalGroupAssignment.ts | 2 +- shared/js/ui/modal/ModalIconSelect.ts | 6 +- shared/js/ui/modal/ModalIdentity.ts | 2 +- shared/js/ui/modal/ModalInvite.ts | 6 +- shared/js/ui/modal/ModalKeySelect.ts | 2 +- shared/js/ui/modal/ModalMusicManage.ts | 6 +- shared/js/ui/modal/ModalNewcomer.ts | 6 +- shared/js/ui/modal/ModalPlaylistEdit.ts | 6 +- shared/js/ui/modal/ModalPlaylistList.ts | 7 +- shared/js/ui/modal/ModalPoke.ts | 6 +- shared/js/ui/modal/ModalQuery.ts | 6 +- shared/js/ui/modal/ModalQueryManage.ts | 6 +- shared/js/ui/modal/ModalServerEdit.ts | 2 +- shared/js/ui/modal/ModalServerInfo.ts | 2 +- .../js/ui/modal/ModalServerInfoBandwidth.ts | 2 +- shared/js/ui/modal/ModalSettings.ts | 2 +- shared/js/ui/modal/ModalYesNo.ts | 4 +- .../permission/CanvasPermissionEditor.ts | 4 +- .../modal/permission/HTMLPermissionEditor.ts | 4 +- .../modal/permission/ModalPermissionEdit.ts | 6 +- .../modal/permission/SenselessPermissions.ts | 4 +- shared/js/utils/helpers.ts | 6 +- shared/js/voice/RecorderBase.ts | 2 +- shared/js/voice/RecorderProfile.ts | 10 +- shared/loader/app.ts | 8 +- tsconfig.json | 3 +- webpack.config.js | 12 +- 114 files changed, 14384 insertions(+), 3963 deletions(-) create mode 100644 dist/bundle.js create mode 100644 shared/js/ui/channel-tree/channel.css create mode 100644 shared/js/ui/channel-tree/channel.css.map create mode 100644 shared/js/ui/channel-tree/colors.css create mode 100644 shared/js/ui/channel-tree/colors.css.map create mode 100644 shared/js/ui/channel-tree/tree.css create mode 100644 shared/js/ui/channel-tree/tree.css.map diff --git a/dist/bundle.js b/dist/bundle.js new file mode 100644 index 00000000..7a101cb5 --- /dev/null +++ b/dist/bundle.js @@ -0,0 +1,6186 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./shared/js/main.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./node_modules/process/browser.js": +/*!*****************************************!*\ + !*** ./node_modules/process/browser.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + + +/***/ }), + +/***/ "./node_modules/webpack/buildin/amd-options.js": +/*!****************************************!*\ + !*** (webpack)/buildin/amd-options.js ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +/* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {/* globals __webpack_amd_options__ */ +module.exports = __webpack_amd_options__; + +/* WEBPACK VAR INJECTION */}.call(this, {})) + +/***/ }), + +/***/ "./node_modules/webpack/buildin/global.js": +/*!***********************************!*\ + !*** (webpack)/buildin/global.js ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), + +/***/ "./shared/js/BrowserIPC.ts": +/*!*********************************!*\ + !*** ./shared/js/BrowserIPC.ts ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var bipc; +(function (bipc) { + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + class BasicIPCHandler { + constructor() { + this._channels = []; + this._query_results = {}; + this._cert_accept_callbacks = {}; + this._cert_accept_succeeded = {}; + } + setup() { + this.unique_id = uuidv4(); + } + get_local_address() { return this.unique_id; } + handle_message(message) { + if (message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) { + if (message.type == "process-query") { + log.debug(LogCategory.IPC, tr("Received a device query from %s."), message.sender); + this.send_message("process-query-response", { + request_query_id: message.data.query_id, + request_timestamp: message.data.timestamp, + device_id: this.unique_id, + protocol: BasicIPCHandler.PROTOCOL_VERSION + }, message.sender); + return; + } + } + else if (message.receiver === this.unique_id) { + if (message.type == "process-query-response") { + const response = message.data; + if (this._query_results[response.request_query_id]) + this._query_results[response.request_query_id].push(response); + else { + log.warn(LogCategory.IPC, tr("Received a query response for an unknown request.")); + } + return; + } + else if (message.type == "certificate-accept-callback") { + const data = message.data; + if (!this._cert_accept_callbacks[data.request_id]) { + log.warn(LogCategory.IPC, tr("Received certificate accept callback for an unknown request ID.")); + return; + } + this._cert_accept_callbacks[data.request_id](); + delete this._cert_accept_callbacks[data.request_id]; + this.send_message("certificate-accept-succeeded", {}, message.sender); + return; + } + else if (message.type == "certificate-accept-succeeded") { + if (!this._cert_accept_succeeded[message.sender]) { + log.warn(LogCategory.IPC, tr("Received certificate accept succeeded, but haven't a callback.")); + return; + } + this._cert_accept_succeeded[message.sender](); + return; + } + } + if (message.type === "channel") { + const data = message.data; + let channel_invoked = false; + for (const channel of this._channels) + if (channel.channel_id === data.channel_id && (typeof (channel.target_id) === "undefined" || channel.target_id === message.sender)) { + if (channel.message_handler) + channel.message_handler(message.sender, message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID, data); + channel_invoked = true; + } + if (!channel_invoked) { + console.warn(tr("Received channel message for unknown channel (%s)"), data.channel_id); + } + } + } + create_channel(target_id, channel_id) { + let channel = { + target_id: target_id, + channel_id: channel_id || uuidv4(), + message_handler: undefined, + send_message: (type, data, target) => { + if (typeof target !== "undefined") { + if (typeof channel.target_id === "string" && target != channel.target_id) + throw "target id does not match channel target"; + } + this.send_message("channel", { + type: type, + data: data, + channel_id: channel.channel_id + }, target || channel.target_id || BasicIPCHandler.BROADCAST_UNIQUE_ID); + } + }; + this._channels.push(channel); + return channel; + } + channels() { return this._channels; } + delete_channel(channel) { + this._channels = this._channels.filter(e => e !== channel); + } + query_processes(timeout) { + return __awaiter(this, void 0, void 0, function* () { + const query_id = uuidv4(); + this._query_results[query_id] = []; + this.send_message("process-query", { + query_id: query_id, + timestamp: Date.now() + }); + yield new Promise(resolve => setTimeout(resolve, timeout || 250)); + const result = this._query_results[query_id]; + delete this._query_results[query_id]; + return result; + }); + } + register_certificate_accept_callback(callback) { + const id = uuidv4(); + this._cert_accept_callbacks[id] = callback; + return this.unique_id + ":" + id; + } + post_certificate_accpected(id, timeout) { + return new Promise((resolve, reject) => { + const data = id.split(":"); + const timeout_id = setTimeout(() => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + reject("timeout"); + }, timeout || 250); + this._cert_accept_succeeded[data[0]] = () => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + resolve(); + }; + this.send_message("certificate-accept-callback", { + request_id: data[1] + }, data[0]); + }); + } + } + BasicIPCHandler.BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000"; + BasicIPCHandler.PROTOCOL_VERSION = 1; + bipc.BasicIPCHandler = BasicIPCHandler; + class BroadcastChannelIPC extends BasicIPCHandler { + constructor() { + super(); + } + setup() { + super.setup(); + this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME); + this.channel.onmessage = this.on_message.bind(this); + this.channel.onmessageerror = this.on_error.bind(this); + } + on_message(event) { + if (typeof (event.data) !== "string") { + log.warn(LogCategory.IPC, tr("Received message with an invalid type (%s): %o"), typeof (event.data), event.data); + return; + } + let message; + try { + message = JSON.parse(event.data); + } + catch (error) { + log.error(LogCategory.IPC, tr("Received an invalid encoded message: %o"), event.data); + return; + } + super.handle_message(message); + } + on_error(event) { + log.warn(LogCategory.IPC, tr("Received error: %o"), event); + } + send_message(type, data, target) { + const message = {}; + message.sender = this.unique_id; + message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID; + message.timestamp = Date.now(); + message.type = type; + message.data = data; + this.channel.postMessage(JSON.stringify(message)); + } + } + BroadcastChannelIPC.CHANNEL_NAME = "TeaSpeak-Web"; + let connect; + (function (connect) { + class ConnectHandler { + constructor(ipc_handler) { + this.callback_available = () => false; + this.callback_execute = () => false; + this._pending_connect_offers = []; + this._pending_connects_requests = []; + this.ipc_handler = ipc_handler; + } + setup() { + this.ipc_channel = this.ipc_handler.create_channel(undefined, ConnectHandler.CHANNEL_NAME); + this.ipc_channel.message_handler = this.on_message.bind(this); + } + on_message(sender, broadcast, message) { + if (broadcast) { + if (message.type == "offer") { + const data = message.data; + const response = { + accepted: this.callback_available(data.data), + request_id: data.request_id + }; + if (response.accepted) { + log.debug(LogCategory.IPC, tr("Received new connect offer from %s: %s"), sender, data.request_id); + const ld = { + remote_handler: sender, + data: data.data, + id: data.request_id, + timeout: 0 + }; + this._pending_connect_offers.push(ld); + ld.timeout = setTimeout(() => { + log.debug(LogCategory.IPC, tr("Dropping connect request %s, because we never received an execute."), ld.id); + this._pending_connect_offers.remove(ld); + }, 120 * 1000); + } + this.ipc_channel.send_message("offer-answer", response, sender); + } + } + else { + if (message.type == "offer-answer") { + const data = message.data; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, tr("Received connect offer answer with unknown request id (%s)."), data.request_id); + return; + } + if (!data.accepted) { + log.debug(LogCategory.IPC, tr("Client %s rejected the connect offer (%s)."), sender, request.id); + return; + } + if (request.remote_handler) { + log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s), but offer has already been accepted."), sender, request.id); + return; + } + log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s). Request local acceptance."), sender, request.id); + request.remote_handler = sender; + clearTimeout(request.timeout); + request.callback_avail().then(flag => { + if (!flag) { + request.callback_failed("local avail rejected"); + return; + } + log.debug(LogCategory.IPC, tr("Executing connect with client %s"), request.remote_handler); + this.ipc_channel.send_message("execute", { + request_id: request.id + }, request.remote_handler); + request.timeout = setTimeout(() => { + request.callback_failed("connect execute timeout"); + }, 1000); + }).catch(error => { + log.error(LogCategory.IPC, tr("Local avail callback caused an error: %o"), error); + request.callback_failed(tr("local avail callback caused an error")); + }); + } + else if (message.type == "executed") { + const data = message.data; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, tr("Received connect executed with unknown request id (%s)."), data.request_id); + return; + } + if (request.remote_handler != sender) { + log.warn(LogCategory.IPC, tr("Received connect executed for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); + return; + } + log.debug(LogCategory.IPC, tr("Received connect executed response from client %s for request %s. Succeeded: %o (%s)"), sender, data.request_id, data.succeeded, data.message); + clearTimeout(request.timeout); + if (data.succeeded) + request.callback_success(); + else + request.callback_failed(data.message); + } + else if (message.type == "execute") { + const data = message.data; + const request = this._pending_connect_offers.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, tr("Received connect execute with unknown request id (%s)."), data.request_id); + return; + } + if (request.remote_handler != sender) { + log.warn(LogCategory.IPC, tr("Received connect execute for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); + return; + } + clearTimeout(request.timeout); + this._pending_connect_offers.remove(request); + log.debug(LogCategory.IPC, tr("Executing connect for %s"), data.request_id); + const cr = this.callback_execute(request.data); + const response = { + request_id: data.request_id, + succeeded: typeof (cr) !== "string" && cr, + message: typeof (cr) === "string" ? cr : "", + }; + this.ipc_channel.send_message("executed", response, request.remote_handler); + } + } + } + post_connect_request(data, callback_avail) { + return new Promise((resolve, reject) => { + const pd = { + data: data, + id: uuidv4(), + timeout: 0, + callback_success: () => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + resolve(); + }, + callback_failed: error => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + reject(error); + }, + callback_avail: callback_avail, + }; + this._pending_connects_requests.push(pd); + this.ipc_channel.send_message("offer", { + request_id: pd.id, + data: pd.data + }); + pd.timeout = setTimeout(() => { + pd.callback_failed("received no response to offer"); + }, 50); + }); + } + } + ConnectHandler.CHANNEL_NAME = "connect"; + connect.ConnectHandler = ConnectHandler; + })(connect = bipc.connect || (bipc.connect = {})); + let mproxy; + (function (mproxy) { + class MethodProxy { + constructor(ipc_handler, connect_params) { + this._proxied_methods = {}; + this._proxied_callbacks = {}; + this.ipc_handler = ipc_handler; + this._ipc_parameters = connect_params; + this._connected = false; + this._slave = typeof (connect_params) !== "undefined"; + this._local = typeof (connect_params) !== "undefined" && connect_params.channel_id === "local" && connect_params.client_id === "local"; + } + setup() { + if (this._local) { + this._connected = true; + this.on_connected(); + } + else { + if (this._slave) + this._ipc_channel = this.ipc_handler.create_channel(this._ipc_parameters.client_id, this._ipc_parameters.channel_id); + else + this._ipc_channel = this.ipc_handler.create_channel(); + this._ipc_channel.message_handler = this._handle_message.bind(this); + if (this._slave) + this._ipc_channel.send_message("initialize", {}); + } + } + finalize() { + if (!this._local) { + if (this._connected) + this._ipc_channel.send_message("finalize", {}); + this.ipc_handler.delete_channel(this._ipc_channel); + this._ipc_channel = undefined; + } + for (const promise of Object.values(this._proxied_callbacks)) + promise.reject("disconnected"); + this._proxied_callbacks = {}; + this._connected = false; + this.on_disconnected(); + } + register_method(method) { + let method_name; + if (typeof method === "function") { + log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method.name); + method_name = method.name; + } + else { + log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method); + method_name = method; + } + if (!this[method_name]) + throw "method is missing in current object"; + this._proxied_methods[method_name] = this[method_name]; + if (!this._local) { + this[method_name] = (...args) => { + if (!this._connected) + return Promise.reject("not connected"); + const proxy_callback = { + promise_id: uuidv4() + }; + this._proxied_callbacks[proxy_callback.promise_id] = proxy_callback; + proxy_callback.promise = new Promise((resolve, reject) => { + proxy_callback.resolve = resolve; + proxy_callback.reject = reject; + }); + this._ipc_channel.send_message("invoke", { + promise_id: proxy_callback.promise_id, + arguments: [...args], + method_name: method_name + }); + return proxy_callback.promise; + }; + } + } + _handle_message(remote_id, boradcast, message) { + if (message.type === "finalize") { + this._handle_finalize(); + } + else if (message.type === "initialize") { + this._handle_remote_callback(remote_id); + } + else if (message.type === "invoke") { + this._handle_invoke(message.data); + } + else if (message.type === "result") { + this._handle_result(message.data); + } + } + _handle_finalize() { + this.on_disconnected(); + this.finalize(); + this._connected = false; + } + _handle_remote_callback(remote_id) { + if (!this._ipc_channel.target_id) { + if (this._slave) + throw "initialize wrong state!"; + this._ipc_channel.target_id = remote_id; + this.on_connected(); + this._ipc_channel.send_message("initialize", true); + } + else { + if (!this._slave) + throw "initialize wrong state!"; + this.on_connected(); + } + this._connected = true; + } + _send_result(promise_id, success, message) { + this._ipc_channel.send_message("result", { + promise_id: promise_id, + result: message, + success: success + }); + } + _handle_invoke(data) { + if (this._proxied_methods[data.method_name]) + throw "we could not invoke a local proxied method!"; + if (!this[data.method_name]) { + this._send_result(data.promise_id, false, "missing method"); + return; + } + try { + log.info(LogCategory.IPC, tr("Invoking method %s with arguments: %o"), data.method_name, data.arguments); + const promise = this[data.method_name](...data.arguments); + promise.then(result => { + log.info(LogCategory.IPC, tr("Result: %o"), result); + this._send_result(data.promise_id, true, result); + }).catch(error => { + this._send_result(data.promise_id, false, error); + }); + } + catch (error) { + this._send_result(data.promise_id, false, error); + return; + } + } + _handle_result(data) { + if (!this._proxied_callbacks[data.promise_id]) { + console.warn(tr("Received proxy method result for unknown promise")); + return; + } + const callback = this._proxied_callbacks[data.promise_id]; + delete this._proxied_callbacks[data.promise_id]; + if (data.success) + callback.resolve(data.result); + else + callback.reject(data.result); + } + generate_connect_parameters() { + if (this._slave) + throw "only masters can generate connect parameters!"; + if (!this._ipc_channel) + throw "please call setup() before"; + return { + channel_id: this._ipc_channel.channel_id, + client_id: this.ipc_handler.get_local_address() + }; + } + is_slave() { return this._local || this._slave; } + is_master() { return this._local || !this._slave; } + } + mproxy.MethodProxy = MethodProxy; + })(mproxy = bipc.mproxy || (bipc.mproxy = {})); + let handler; + let connect_handler; + function setup() { + if (!supported()) + return; + handler = new BroadcastChannelIPC(); + handler.setup(); + connect_handler = new connect.ConnectHandler(handler); + connect_handler.setup(); + } + bipc.setup = setup; + function get_handler() { + return handler; + } + bipc.get_handler = get_handler; + function get_connect_handler() { + return connect_handler; + } + bipc.get_connect_handler = get_connect_handler; + function supported() { + return typeof (window.BroadcastChannel) !== "undefined"; + } + bipc.supported = supported; +})(bipc = exports.bipc || (exports.bipc = {})); + + +/***/ }), + +/***/ "./shared/js/PPTListener.ts": +/*!**********************************!*\ + !*** ./shared/js/PPTListener.ts ***! + \**********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var KeyCode; +(function (KeyCode) { + KeyCode[KeyCode["KEY_CANCEL"] = 3] = "KEY_CANCEL"; + KeyCode[KeyCode["KEY_HELP"] = 6] = "KEY_HELP"; + KeyCode[KeyCode["KEY_BACK_SPACE"] = 8] = "KEY_BACK_SPACE"; + KeyCode[KeyCode["KEY_TAB"] = 9] = "KEY_TAB"; + KeyCode[KeyCode["KEY_CLEAR"] = 12] = "KEY_CLEAR"; + KeyCode[KeyCode["KEY_RETURN"] = 13] = "KEY_RETURN"; + KeyCode[KeyCode["KEY_ENTER"] = 14] = "KEY_ENTER"; + KeyCode[KeyCode["KEY_SHIFT"] = 16] = "KEY_SHIFT"; + KeyCode[KeyCode["KEY_CONTROL"] = 17] = "KEY_CONTROL"; + KeyCode[KeyCode["KEY_ALT"] = 18] = "KEY_ALT"; + KeyCode[KeyCode["KEY_PAUSE"] = 19] = "KEY_PAUSE"; + KeyCode[KeyCode["KEY_CAPS_LOCK"] = 20] = "KEY_CAPS_LOCK"; + KeyCode[KeyCode["KEY_ESCAPE"] = 27] = "KEY_ESCAPE"; + KeyCode[KeyCode["KEY_SPACE"] = 32] = "KEY_SPACE"; + KeyCode[KeyCode["KEY_PAGE_UP"] = 33] = "KEY_PAGE_UP"; + KeyCode[KeyCode["KEY_PAGE_DOWN"] = 34] = "KEY_PAGE_DOWN"; + KeyCode[KeyCode["KEY_END"] = 35] = "KEY_END"; + KeyCode[KeyCode["KEY_HOME"] = 36] = "KEY_HOME"; + KeyCode[KeyCode["KEY_LEFT"] = 37] = "KEY_LEFT"; + KeyCode[KeyCode["KEY_UP"] = 38] = "KEY_UP"; + KeyCode[KeyCode["KEY_RIGHT"] = 39] = "KEY_RIGHT"; + KeyCode[KeyCode["KEY_DOWN"] = 40] = "KEY_DOWN"; + KeyCode[KeyCode["KEY_PRINTSCREEN"] = 44] = "KEY_PRINTSCREEN"; + KeyCode[KeyCode["KEY_INSERT"] = 45] = "KEY_INSERT"; + KeyCode[KeyCode["KEY_DELETE"] = 46] = "KEY_DELETE"; + KeyCode[KeyCode["KEY_0"] = 48] = "KEY_0"; + KeyCode[KeyCode["KEY_1"] = 49] = "KEY_1"; + KeyCode[KeyCode["KEY_2"] = 50] = "KEY_2"; + KeyCode[KeyCode["KEY_3"] = 51] = "KEY_3"; + KeyCode[KeyCode["KEY_4"] = 52] = "KEY_4"; + KeyCode[KeyCode["KEY_5"] = 53] = "KEY_5"; + KeyCode[KeyCode["KEY_6"] = 54] = "KEY_6"; + KeyCode[KeyCode["KEY_7"] = 55] = "KEY_7"; + KeyCode[KeyCode["KEY_8"] = 56] = "KEY_8"; + KeyCode[KeyCode["KEY_9"] = 57] = "KEY_9"; + KeyCode[KeyCode["KEY_SEMICOLON"] = 59] = "KEY_SEMICOLON"; + KeyCode[KeyCode["KEY_EQUALS"] = 61] = "KEY_EQUALS"; + KeyCode[KeyCode["KEY_A"] = 65] = "KEY_A"; + KeyCode[KeyCode["KEY_B"] = 66] = "KEY_B"; + KeyCode[KeyCode["KEY_C"] = 67] = "KEY_C"; + KeyCode[KeyCode["KEY_D"] = 68] = "KEY_D"; + KeyCode[KeyCode["KEY_E"] = 69] = "KEY_E"; + KeyCode[KeyCode["KEY_F"] = 70] = "KEY_F"; + KeyCode[KeyCode["KEY_G"] = 71] = "KEY_G"; + KeyCode[KeyCode["KEY_H"] = 72] = "KEY_H"; + KeyCode[KeyCode["KEY_I"] = 73] = "KEY_I"; + KeyCode[KeyCode["KEY_J"] = 74] = "KEY_J"; + KeyCode[KeyCode["KEY_K"] = 75] = "KEY_K"; + KeyCode[KeyCode["KEY_L"] = 76] = "KEY_L"; + KeyCode[KeyCode["KEY_M"] = 77] = "KEY_M"; + KeyCode[KeyCode["KEY_N"] = 78] = "KEY_N"; + KeyCode[KeyCode["KEY_O"] = 79] = "KEY_O"; + KeyCode[KeyCode["KEY_P"] = 80] = "KEY_P"; + KeyCode[KeyCode["KEY_Q"] = 81] = "KEY_Q"; + KeyCode[KeyCode["KEY_R"] = 82] = "KEY_R"; + KeyCode[KeyCode["KEY_S"] = 83] = "KEY_S"; + KeyCode[KeyCode["KEY_T"] = 84] = "KEY_T"; + KeyCode[KeyCode["KEY_U"] = 85] = "KEY_U"; + KeyCode[KeyCode["KEY_V"] = 86] = "KEY_V"; + KeyCode[KeyCode["KEY_W"] = 87] = "KEY_W"; + KeyCode[KeyCode["KEY_X"] = 88] = "KEY_X"; + KeyCode[KeyCode["KEY_Y"] = 89] = "KEY_Y"; + KeyCode[KeyCode["KEY_Z"] = 90] = "KEY_Z"; + KeyCode[KeyCode["KEY_LEFT_CMD"] = 91] = "KEY_LEFT_CMD"; + KeyCode[KeyCode["KEY_RIGHT_CMD"] = 93] = "KEY_RIGHT_CMD"; + KeyCode[KeyCode["KEY_CONTEXT_MENU"] = 93] = "KEY_CONTEXT_MENU"; + KeyCode[KeyCode["KEY_NUMPAD0"] = 96] = "KEY_NUMPAD0"; + KeyCode[KeyCode["KEY_NUMPAD1"] = 97] = "KEY_NUMPAD1"; + KeyCode[KeyCode["KEY_NUMPAD2"] = 98] = "KEY_NUMPAD2"; + KeyCode[KeyCode["KEY_NUMPAD3"] = 99] = "KEY_NUMPAD3"; + KeyCode[KeyCode["KEY_NUMPAD4"] = 100] = "KEY_NUMPAD4"; + KeyCode[KeyCode["KEY_NUMPAD5"] = 101] = "KEY_NUMPAD5"; + KeyCode[KeyCode["KEY_NUMPAD6"] = 102] = "KEY_NUMPAD6"; + KeyCode[KeyCode["KEY_NUMPAD7"] = 103] = "KEY_NUMPAD7"; + KeyCode[KeyCode["KEY_NUMPAD8"] = 104] = "KEY_NUMPAD8"; + KeyCode[KeyCode["KEY_NUMPAD9"] = 105] = "KEY_NUMPAD9"; + KeyCode[KeyCode["KEY_MULTIPLY"] = 106] = "KEY_MULTIPLY"; + KeyCode[KeyCode["KEY_ADD"] = 107] = "KEY_ADD"; + KeyCode[KeyCode["KEY_SEPARATOR"] = 108] = "KEY_SEPARATOR"; + KeyCode[KeyCode["KEY_SUBTRACT"] = 109] = "KEY_SUBTRACT"; + KeyCode[KeyCode["KEY_DECIMAL"] = 110] = "KEY_DECIMAL"; + KeyCode[KeyCode["KEY_DIVIDE"] = 111] = "KEY_DIVIDE"; + KeyCode[KeyCode["KEY_F1"] = 112] = "KEY_F1"; + KeyCode[KeyCode["KEY_F2"] = 113] = "KEY_F2"; + KeyCode[KeyCode["KEY_F3"] = 114] = "KEY_F3"; + KeyCode[KeyCode["KEY_F4"] = 115] = "KEY_F4"; + KeyCode[KeyCode["KEY_F5"] = 116] = "KEY_F5"; + KeyCode[KeyCode["KEY_F6"] = 117] = "KEY_F6"; + KeyCode[KeyCode["KEY_F7"] = 118] = "KEY_F7"; + KeyCode[KeyCode["KEY_F8"] = 119] = "KEY_F8"; + KeyCode[KeyCode["KEY_F9"] = 120] = "KEY_F9"; + KeyCode[KeyCode["KEY_F10"] = 121] = "KEY_F10"; + KeyCode[KeyCode["KEY_F11"] = 122] = "KEY_F11"; + KeyCode[KeyCode["KEY_F12"] = 123] = "KEY_F12"; + KeyCode[KeyCode["KEY_F13"] = 124] = "KEY_F13"; + KeyCode[KeyCode["KEY_F14"] = 125] = "KEY_F14"; + KeyCode[KeyCode["KEY_F15"] = 126] = "KEY_F15"; + KeyCode[KeyCode["KEY_F16"] = 127] = "KEY_F16"; + KeyCode[KeyCode["KEY_F17"] = 128] = "KEY_F17"; + KeyCode[KeyCode["KEY_F18"] = 129] = "KEY_F18"; + KeyCode[KeyCode["KEY_F19"] = 130] = "KEY_F19"; + KeyCode[KeyCode["KEY_F20"] = 131] = "KEY_F20"; + KeyCode[KeyCode["KEY_F21"] = 132] = "KEY_F21"; + KeyCode[KeyCode["KEY_F22"] = 133] = "KEY_F22"; + KeyCode[KeyCode["KEY_F23"] = 134] = "KEY_F23"; + KeyCode[KeyCode["KEY_F24"] = 135] = "KEY_F24"; + KeyCode[KeyCode["KEY_NUM_LOCK"] = 144] = "KEY_NUM_LOCK"; + KeyCode[KeyCode["KEY_SCROLL_LOCK"] = 145] = "KEY_SCROLL_LOCK"; + KeyCode[KeyCode["KEY_COMMA"] = 188] = "KEY_COMMA"; + KeyCode[KeyCode["KEY_PERIOD"] = 190] = "KEY_PERIOD"; + KeyCode[KeyCode["KEY_SLASH"] = 191] = "KEY_SLASH"; + KeyCode[KeyCode["KEY_BACK_QUOTE"] = 192] = "KEY_BACK_QUOTE"; + KeyCode[KeyCode["KEY_OPEN_BRACKET"] = 219] = "KEY_OPEN_BRACKET"; + KeyCode[KeyCode["KEY_BACK_SLASH"] = 220] = "KEY_BACK_SLASH"; + KeyCode[KeyCode["KEY_CLOSE_BRACKET"] = 221] = "KEY_CLOSE_BRACKET"; + KeyCode[KeyCode["KEY_QUOTE"] = 222] = "KEY_QUOTE"; + KeyCode[KeyCode["KEY_META"] = 224] = "KEY_META"; +})(KeyCode = exports.KeyCode || (exports.KeyCode = {})); +var ppt; +(function (ppt) { + let EventType; + (function (EventType) { + EventType[EventType["KEY_PRESS"] = 0] = "KEY_PRESS"; + EventType[EventType["KEY_RELEASE"] = 1] = "KEY_RELEASE"; + EventType[EventType["KEY_TYPED"] = 2] = "KEY_TYPED"; + })(EventType = ppt.EventType || (ppt.EventType = {})); + let SpecialKey; + (function (SpecialKey) { + SpecialKey[SpecialKey["CTRL"] = 0] = "CTRL"; + SpecialKey[SpecialKey["WINDOWS"] = 1] = "WINDOWS"; + SpecialKey[SpecialKey["SHIFT"] = 2] = "SHIFT"; + SpecialKey[SpecialKey["ALT"] = 3] = "ALT"; + })(SpecialKey = ppt.SpecialKey || (ppt.SpecialKey = {})); + function key_description(key) { + let result = ""; + if (key.key_shift) + result += " + " + tr("Shift"); + if (key.key_alt) + result += " + " + tr("Alt"); + if (key.key_ctrl) + result += " + " + tr("CTRL"); + if (key.key_windows) + result += " + " + tr("Win"); + if (!result && !key.key_code) + return tr("unset"); + if (key.key_code) + result += " + " + key.key_code; + return result.substr(3); + } + ppt.key_description = key_description; +})(ppt = exports.ppt || (exports.ppt = {})); + + +/***/ }), + +/***/ "./shared/js/connection/CommandHelper.ts": +/*!***********************************************!*\ + !*** ./shared/js/connection/CommandHelper.ts ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ServerConnectionDeclaration_1 = __webpack_require__(/*! ./ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); +const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); +const ConnectionBase_1 = __webpack_require__(/*! ./ConnectionBase */ "./shared/js/connection/ConnectionBase.ts"); +const log_1 = __webpack_require__(/*! ../log */ "./shared/js/log.ts"); +class CommandHelper extends ConnectionBase_1.AbstractCommandHandler { + constructor(connection) { + super(connection); + this._awaiters_unique_ids = {}; + this._awaiters_unique_dbid = {}; + this.volatile_handler_boss = false; + this.ignore_consumed = true; + } + initialize() { + this.connection.command_handler_boss().register_handler(this); + } + destroy() { + if (this.connection) { + const hboss = this.connection.command_handler_boss(); + hboss && hboss.unregister_handler(this); + } + this._awaiters_unique_ids = undefined; + } + handle_command(command) { + if (command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + if (command.command == "notifyclientgetnamefromdbid") + this.handle_notifyclientgetnamefromdbid(command.arguments); + else + return false; + return true; + } + joinChannel(channel, password) { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); + } + sendMessage(message, type, target) { + if (type == chat_1.ChatType.SERVER) + return this.connection.send_command("sendtextmessage", { "targetmode": 3, "target": 0, "msg": message }); + else if (type == chat_1.ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", { "targetmode": 2, "target": target.getChannelId(), "msg": message }); + else if (type == chat_1.ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", { "targetmode": 1, "target": target.clientId(), "msg": message }); + } + updateClient(key, value) { + let data = {}; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } + info_from_uid(..._unique_ids) { + return __awaiter(this, void 0, void 0, function* () { + const response = []; + const request = []; + const unique_ids = new Set(_unique_ids); + if (!unique_ids.size) + return []; + const unique_id_resolvers = {}; + for (const unique_id of unique_ids) { + request.push({ 'cluid': unique_id }); + (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) + .push(unique_id_resolvers[unique_id] = info => response.push(info)); + } + try { + yield this.connection.send_command("clientgetnamefromuid", request); + } + catch (error) { + if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + } + else { + throw error; + } + } + finally { + for (const unique_id of Object.keys(unique_id_resolvers)) + (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); + } + return response; + }); + } + handle_notifyclientgetnamefromdbid(json) { + for (const entry of json) { + const info = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + const functions = this._awaiters_unique_dbid[info.client_database_id] || []; + delete this._awaiters_unique_dbid[info.client_database_id]; + for (const fn of functions) + fn(info); + } + } + info_from_cldbid(..._cldbid) { + return __awaiter(this, void 0, void 0, function* () { + const response = []; + const request = []; + const unique_cldbid = new Set(_cldbid); + if (!unique_cldbid.size) + return []; + const unique_cldbid_resolvers = {}; + for (const cldbid of unique_cldbid) { + request.push({ 'cldbid': cldbid }); + (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) + .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); + } + try { + yield this.connection.send_command("clientgetnamefromdbid", request); + } + catch (error) { + if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + } + else { + throw error; + } + } + finally { + for (const cldbid of Object.keys(unique_cldbid_resolvers)) + (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); + } + return response; + }); + } + handle_notifyclientnamefromuid(json) { + for (const entry of json) { + const info = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + const functions = this._awaiters_unique_ids[entry["cluid"]] || []; + delete this._awaiters_unique_ids[entry["cluid"]]; + for (const fn of functions) + fn(info); + } + } + request_query_list(server_id = undefined) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + const result = {}; + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + for (const entry of json) { + const rentry = {}; + rentry.bounded_server = parseInt(entry["client_bound_server"]); + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + result.queries.push(rentry); + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + let data = {}; + if (server_id !== undefined) + data["server_id"] = server_id; + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) { + if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } + } + reject(error); + }); + }); + } + request_playlist_list() { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result = []; + for (const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } + catch (error) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); + } + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) { + if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }); + }); + } + request_playlist_songs(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; + if (json[0]["playlist_id"] != playlist_id) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); + return false; + } + const result = []; + for (const entry of json) { + try { + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] + }); + } + catch (error) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); + } + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistsonglist", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) { + if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }); + }); + } + request_playlist_client_list(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistclientlist", + function: command => { + const json = command.arguments; + if (json[0]["playlist_id"] != playlist_id) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); + return false; + } + const result = []; + for (const entry of json) + result.push(parseInt(entry["cldbid"])); + resolve(result.filter(e => !isNaN(e))); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistclientlist", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + reject(error); + }); + }); + } + request_clients_by_server_group(group_id) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyservergroupclientlist", + function: command => { + if (command.arguments[0]["sgid"] != group_id) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); + return false; + } + try { + const result = []; + for (const entry of command.arguments) + result.push({ + client_database_id: parseInt(entry["cldbid"]), + client_nickname: entry["client_nickname"], + client_unique_identifier: entry["client_unique_identifier"] + }); + resolve(result); + } + catch (error) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); + reject("failed to parse info"); + } + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("servergroupclientlist", { sgid: group_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + }); + } + request_playlist_info(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + return; + } + try { + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + playlist_max_songs: parseInt(json["playlist_max_songs"]) + }); + } + catch (error) { + log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); + reject("failed to parse info"); + } + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistinfo", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } + current_virtual_server_id() { + if (this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + return new Promise((resolve, reject) => { + const single_handler = { + function: command => { + if (command.command != "" && command.command.indexOf("=") == -1) + return false; + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } +} +exports.CommandHelper = CommandHelper; + + +/***/ }), + +/***/ "./shared/js/connection/ConnectionBase.ts": +/*!************************************************!*\ + !*** ./shared/js/connection/ConnectionBase.ts ***! + \************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const CommandHelper_1 = __webpack_require__(/*! ./CommandHelper */ "./shared/js/connection/CommandHelper.ts"); +exports.CommandOptionDefaults = { + flagset: [], + process_result: true, + timeout: 1000 +}; +class AbstractServerConnection { + constructor(client) { + this.client = client; + this.command_helper = new CommandHelper_1.CommandHelper(this); + } +} +exports.AbstractServerConnection = AbstractServerConnection; +var voice; +(function (voice) { + let PlayerState; + (function (PlayerState) { + PlayerState[PlayerState["PREBUFFERING"] = 0] = "PREBUFFERING"; + PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING"; + PlayerState[PlayerState["BUFFERING"] = 2] = "BUFFERING"; + PlayerState[PlayerState["STOPPING"] = 3] = "STOPPING"; + PlayerState[PlayerState["STOPPED"] = 4] = "STOPPED"; + })(PlayerState = voice.PlayerState || (voice.PlayerState = {})); + class AbstractVoiceConnection { + constructor(connection) { + this.connection = connection; + } + } + voice.AbstractVoiceConnection = AbstractVoiceConnection; +})(voice = exports.voice || (exports.voice = {})); +class ServerCommand { +} +exports.ServerCommand = ServerCommand; +class AbstractCommandHandler { + constructor(connection) { + this.volatile_handler_boss = false; + this.ignore_consumed = false; + this.connection = connection; + } +} +exports.AbstractCommandHandler = AbstractCommandHandler; +class AbstractCommandHandlerBoss { + constructor(connection) { + this.command_handlers = []; + this.single_command_handler = []; + this.connection = connection; + } + destroy() { + this.command_handlers = undefined; + this.single_command_handler = undefined; + } + register_handler(handler) { + if (!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; + this.command_handlers.remove(handler); + this.command_handlers.push(handler); + handler.handler_boss = this; + } + unregister_handler(handler) { + if (!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); + return; + } + this.command_handlers.remove(handler); + handler.handler_boss = undefined; + } + register_single_handler(handler) { + this.single_command_handler.push(handler); + } + remove_single_handler(handler) { + this.single_command_handler.remove(handler); + } + handlers() { + return this.command_handlers; + } + invoke_handle(command) { + let flag_consumed = false; + for (const handler of this.command_handlers) { + try { + if (!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } + catch (error) { + console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); + } + } + for (const handler of [...this.single_command_handler]) { + if (handler.command && handler.command != command.command) + continue; + try { + if (handler.function(command)) + this.single_command_handler.remove(handler); + } + catch (error) { + console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); + } + } + return flag_consumed; + } +} +exports.AbstractCommandHandlerBoss = AbstractCommandHandlerBoss; + + +/***/ }), + +/***/ "./shared/js/connection/ServerConnectionDeclaration.ts": +/*!*************************************************************!*\ + !*** ./shared/js/connection/ServerConnectionDeclaration.ts ***! + \*************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var ErrorID; +(function (ErrorID) { + ErrorID[ErrorID["NOT_IMPLEMENTED"] = 2] = "NOT_IMPLEMENTED"; + ErrorID[ErrorID["COMMAND_NOT_FOUND"] = 256] = "COMMAND_NOT_FOUND"; + ErrorID[ErrorID["PERMISSION_ERROR"] = 2568] = "PERMISSION_ERROR"; + ErrorID[ErrorID["EMPTY_RESULT"] = 1281] = "EMPTY_RESULT"; + ErrorID[ErrorID["PLAYLIST_IS_IN_USE"] = 8451] = "PLAYLIST_IS_IN_USE"; + ErrorID[ErrorID["FILE_ALREADY_EXISTS"] = 2050] = "FILE_ALREADY_EXISTS"; + ErrorID[ErrorID["CLIENT_INVALID_ID"] = 512] = "CLIENT_INVALID_ID"; + ErrorID[ErrorID["CONVERSATION_INVALID_ID"] = 8704] = "CONVERSATION_INVALID_ID"; + ErrorID[ErrorID["CONVERSATION_MORE_DATA"] = 8705] = "CONVERSATION_MORE_DATA"; + ErrorID[ErrorID["CONVERSATION_IS_PRIVATE"] = 8706] = "CONVERSATION_IS_PRIVATE"; +})(ErrorID = exports.ErrorID || (exports.ErrorID = {})); +class CommandResult { + constructor(json) { + this.json = json; + this.id = parseInt(json["id"]); + this.message = json["msg"]; + this.extra_message = ""; + if (json["extra_msg"]) + this.extra_message = json["extra_msg"]; + this.success = this.id == 0; + } +} +exports.CommandResult = CommandResult; + + +/***/ }), + +/***/ "./shared/js/crypto/asn1.ts": +/*!**********************************!*\ + !*** ./shared/js/crypto/asn1.ts ***! + \**********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var asn1; +(function (asn1) { + const ellipsis = "\u2026"; + function string_cut(str, len) { + if (str.length > len) + str = str.substring(0, len) + ellipsis; + return str; + } + class Stream { + constructor(data, position) { + if (data instanceof Stream) + this.data = data.data; + else + this.data = data; + this.position = position; + } + length() { + if (this.data instanceof ArrayBuffer) + return this.data.byteLength; + return this.data.length; + } + get(position) { + if (position === undefined) + position = this.position++; + if (position >= this.length()) + throw 'Requesting byte offset ' + this.position + ' on a stream of length ' + this.length(); + return (typeof (this.data) === "string") ? this.data.charCodeAt(position) : this.data[position]; + } + hexByte(byte) { + return Stream.HEX_DIGITS.charAt((byte >> 4) & 0xF) + Stream.HEX_DIGITS.charAt(byte & 0xF); + } + parseStringISO(start, end) { + let s = ""; + for (let i = start; i < end; ++i) + s += String.fromCharCode(this.get(i)); + return s; + } + parseStringUTF(start, end) { + let s = ""; + for (let i = start; i < end;) { + let c = this.get(i++); + if (c < 128) + s += String.fromCharCode(c); + else if ((c > 191) && (c < 224)) + s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); + else + s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); + } + return s; + } + parseStringBMP(start, end) { + let str = "", hi, lo; + for (let i = start; i < end;) { + hi = this.get(i++); + lo = this.get(i++); + str += String.fromCharCode((hi << 8) | lo); + } + return str; + } + parseTime(start, end, shortYear) { + let s = this.parseStringISO(start, end), m = (shortYear ? Stream.reTimeS : Stream.reTimeL).exec(s); + if (!m) + return "Unrecognized time: " + s; + if (shortYear) { + throw "fixme!"; + } + s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; + if (m[5]) { + s += ":" + m[5]; + if (m[6]) { + s += ":" + m[6]; + if (m[7]) + s += "." + m[7]; + } + } + if (m[8]) { + s += " UTC"; + if (m[8] != 'Z') { + s += m[8]; + if (m[9]) + s += ":" + m[9]; + } + } + return s; + } + ; + parseInteger(start, end) { + let current = this.get(start); + let negative = (current > 127); + let padding = negative ? 255 : 0; + let length; + let descriptor; + while (current == padding && ++start < end) + current = this.get(start); + length = end - start; + if (length === 0) + return negative ? '-1' : '0'; + if (length > 4) { + descriptor = current; + length <<= 3; + while (((descriptor ^ padding) & 0x80) == 0) { + descriptor <<= 1; + --length; + } + descriptor = "(" + length + " bit)\n"; + } + if (negative) + current = current - 256; + let number = ""; + if (typeof (Int10) !== "undefined") { + let n = new Int10(current); + for (let i = start + 1; i < end; ++i) + n.mulAdd(256, this.get(i)); + number = n.toString(); + } + else { + let n = 0; + for (let i = start + 1; i < end; ++i) { + n <<= 8; + n += this.get(i); + } + number = n.toString(); + } + return descriptor + number; + } + ; + isASCII(start, end) { + for (let i = start; i < end; ++i) { + const c = this.get(i); + if (c < 32 || c > 176) + return false; + } + return true; + } + ; + parseBitString(start, end, maxLength) { + let unusedBit = this.get(start), lenBit = ((end - start - 1) << 3) - unusedBit, intro = "(" + lenBit + " bit)\n", s = ""; + for (let i = start + 1; i < end; ++i) { + let b = this.get(i), skip = (i == end - 1) ? unusedBit : 0; + for (let j = 7; j >= skip; --j) + s += (b >> j) & 1 ? "1" : "0"; + if (s.length > maxLength) + return intro + string_cut(s, maxLength); + } + return intro + s; + } + ; + parseOctetString(start, end, maxLength) { + if (this.isASCII(start, end)) + return string_cut(this.parseStringISO(start, end), maxLength); + let len = end - start, s = "(" + len + " byte)\n"; + maxLength /= 2; + if (len > maxLength) + end = start + maxLength; + for (let i = start; i < end; ++i) + s += this.hexByte(this.get(i)); + if (len > maxLength) + s += ellipsis; + return s; + } + ; + parseOID(start, end, maxLength) { + let s = '', n = new Int10(), bits = 0; + for (let i = start; i < end; ++i) { + let v = this.get(i); + n.mulAdd(128, v & 0x7F); + bits += 7; + if (!(v & 0x80)) { + if (s === '') { + n = n.simplify(); + if (n instanceof Int10) { + n.sub(80); + s = "2." + n.toString(); + } + else { + let m = n < 80 ? n < 40 ? 0 : 1 : 2; + s = m + "." + (n - m * 40); + } + } + else + s += "." + n.toString(); + if (s.length > maxLength) + return string_cut(s, maxLength); + n = new Int10(); + bits = 0; + } + } + if (bits > 0) + s += ".incomplete"; + return s; + } + ; + } + Stream.HEX_DIGITS = "0123456789ABCDEF"; + Stream.reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + Stream.reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + asn1.Stream = Stream; + let TagClass; + (function (TagClass) { + TagClass[TagClass["UNIVERSAL"] = 0] = "UNIVERSAL"; + TagClass[TagClass["APPLICATION"] = 1] = "APPLICATION"; + TagClass[TagClass["CONTEXT"] = 2] = "CONTEXT"; + TagClass[TagClass["PRIVATE"] = 3] = "PRIVATE"; + })(TagClass = asn1.TagClass || (asn1.TagClass = {})); + let TagType; + (function (TagType) { + TagType[TagType["EOC"] = 0] = "EOC"; + TagType[TagType["BOOLEAN"] = 1] = "BOOLEAN"; + TagType[TagType["INTEGER"] = 2] = "INTEGER"; + TagType[TagType["BIT_STRING"] = 3] = "BIT_STRING"; + TagType[TagType["OCTET_STRING"] = 4] = "OCTET_STRING"; + TagType[TagType["NULL"] = 5] = "NULL"; + TagType[TagType["OBJECT_IDENTIFIER"] = 6] = "OBJECT_IDENTIFIER"; + TagType[TagType["ObjectDescriptor"] = 7] = "ObjectDescriptor"; + TagType[TagType["EXTERNAL"] = 8] = "EXTERNAL"; + TagType[TagType["REAL"] = 9] = "REAL"; + TagType[TagType["ENUMERATED"] = 10] = "ENUMERATED"; + TagType[TagType["EMBEDDED_PDV"] = 11] = "EMBEDDED_PDV"; + TagType[TagType["UTF8String"] = 12] = "UTF8String"; + TagType[TagType["SEQUENCE"] = 16] = "SEQUENCE"; + TagType[TagType["SET"] = 17] = "SET"; + TagType[TagType["NumericString"] = 18] = "NumericString"; + TagType[TagType["PrintableString"] = 19] = "PrintableString"; + TagType[TagType["TeletextString"] = 20] = "TeletextString"; + TagType[TagType["VideotexString"] = 21] = "VideotexString"; + TagType[TagType["IA5String"] = 22] = "IA5String"; + TagType[TagType["UTCTime"] = 23] = "UTCTime"; + TagType[TagType["GeneralizedTime"] = 24] = "GeneralizedTime"; + TagType[TagType["GraphicString"] = 25] = "GraphicString"; + TagType[TagType["VisibleString"] = 26] = "VisibleString"; + TagType[TagType["GeneralString"] = 27] = "GeneralString"; + TagType[TagType["UniversalString"] = 28] = "UniversalString"; + TagType[TagType["BMPString"] = 30] = "BMPString"; + })(TagType = asn1.TagType || (asn1.TagType = {})); + class ASN1Tag { + constructor(stream) { + let buf = stream.get(); + this.tagClass = buf >> 6; + this.tagConstructed = ((buf & 0x20) !== 0); + this.tagNumber = buf & 0x1F; + if (this.tagNumber == 0x1F) { + let n = new Int10(); + do { + buf = stream.get(); + n.mulAdd(128, buf & 0x7F); + } while (buf & 0x80); + this.tagNumber = n.simplify(); + } + } + isUniversal() { + return this.tagClass === 0x00; + } + ; + isEOC() { + return this.tagClass === 0x00 && this.tagNumber === 0x00; + } + ; + } + class ASN1 { + constructor(stream, header, length, tag, children) { + this.stream = stream; + this.header = header; + this.length = length; + this.tag = tag; + this.children = children; + } + content(max_length, type) { + if (this.tag === undefined) + return null; + if (max_length === undefined) + max_length = Infinity; + let content = this.posContent(), len = Math.abs(this.length); + if (!this.tag.isUniversal()) { + if (this.children !== null) + return "(" + this.children.length + " elem)"; + return this.stream.parseOctetString(content, content + len, max_length); + } + switch (type || this.tag.tagNumber) { + case 0x01: + return (this.stream.get(content) === 0) ? "false" : "true"; + case 0x02: + return this.stream.parseInteger(content, content + len); + case 0x03: + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseBitString(content, content + len, max_length); + case 0x04: + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseOctetString(content, content + len, max_length); + case 0x06: + return this.stream.parseOID(content, content + len, max_length); + case 0x10: + case 0x11: + if (this.children !== null) + return "(" + this.children.length + " elem)"; + else + return "(no elem)"; + case 0x0C: + return string_cut(this.stream.parseStringUTF(content, content + len), max_length); + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x1A: + return string_cut(this.stream.parseStringISO(content, content + len), max_length); + case 0x1E: + return string_cut(this.stream.parseStringBMP(content, content + len), max_length); + case 0x17: + case 0x18: + return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); + } + return null; + } + ; + typeName() { + switch (this.tag.tagClass) { + case 0: + return TagType[this.tag.tagNumber] || ("Universal_" + this.tag.tagNumber.toString()); + case 1: + return "Application_" + this.tag.tagNumber.toString(); + case 2: + return "[" + this.tag.tagNumber.toString() + "]"; + case 3: + return "Private_" + this.tag.tagNumber.toString(); + } + } + ; + toString() { + return this.typeName() + "@" + this.stream.position + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.children === null) ? 'null' : this.children.length) + "]"; + } + toPrettyString(indent) { + if (indent === undefined) + indent = ''; + let s = indent + this.typeName() + " @" + this.stream.position; + if (this.length >= 0) + s += "+"; + s += this.length; + if (this.tag.tagConstructed) + s += " (constructed)"; + else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.children !== null)) + s += " (encapsulates)"; + let content = this.content(); + if (content) + s += ": " + content.replace(/\n/g, '|'); + s += "\n"; + if (this.children !== null) { + indent += ' '; + for (let i = 0, max = this.children.length; i < max; ++i) + s += this.children[i].toPrettyString(indent); + } + return s; + } + ; + posStart() { + return this.stream.position; + } + ; + posContent() { + return this.stream.position + this.header; + } + ; + posEnd() { + return this.stream.position + this.header + Math.abs(this.length); + } + ; + static decodeLength(stream) { + let buf = stream.get(); + const len = buf & 0x7F; + if (len == buf) + return len; + if (len > 6) + throw "Length over 48 bits not supported at position " + (stream.position - 1); + if (len === 0) + return null; + buf = 0; + for (let i = 0; i < len; ++i) + buf = (buf << 8) + stream.get(); + return buf; + } + ; + static encodeLength(buffer, offset, length) { + if (length < 0x7F) { + buffer[offset] = length; + } + else { + buffer[offset] = 0x80; + let index = 1; + while (length > 0) { + buffer[offset + index++] = length & 0xFF; + length >>= 8; + buffer[offset] += 1; + } + } + } + } + asn1.ASN1 = ASN1; + function decode0(stream) { + const streamStart = new Stream(stream, 0); + const tag = new ASN1Tag(stream); + let len = ASN1.decodeLength(stream); + const start = stream.position; + const length_header = start - streamStart.position; + let children = null; + const query_children = () => { + children = []; + if (len !== null) { + const end = start + len; + if (end > stream.length()) + throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; + while (stream.position < end) + children[children.length] = decode0(stream); + if (stream.position != end) + throw 'Content size is not correct for container at offset ' + start; + } + else { + try { + while (true) { + const s = decode0(stream); + if (s.tag.isEOC()) + break; + children[children.length] = s; + } + len = start - stream.position; + } + catch (e) { + throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e; + } + } + }; + if (tag.tagConstructed) { + query_children(); + } + else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { + try { + if (tag.tagNumber == 0x03) + if (stream.get() != 0) + throw "BIT STRINGs with unused bits cannot encapsulate."; + query_children(); + for (let i = 0; i < children.length; ++i) + if (children[i].tag.isEOC()) + throw 'EOC is not supposed to be actual content.'; + } + catch (e) { + children = null; + } + } + if (children === null) { + if (len === null) + throw "We can't skip over an invalid tag with undefined length at offset " + start; + stream.position = start + Math.abs(len); + } + return new ASN1(streamStart, length_header, len, tag, children); + } + function decode(stream) { + return decode0(new Stream(stream, 0)); + } + asn1.decode = decode; +})(asn1 = exports.asn1 || (exports.asn1 = {})); + + +/***/ }), + +/***/ "./shared/js/crypto/sha.ts": +/*!*********************************!*\ + !*** ./shared/js/crypto/sha.ts ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(process, global) {var __WEBPACK_AMD_DEFINE_RESULT__; +Object.defineProperty(exports, "__esModule", { value: true }); +var sha; +(function (sha) { + (function () { + 'use strict'; + let root = typeof window === 'object' ? window : {}; + let NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } + let COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports; + let AMD = true && __webpack_require__(/*! !webpack amd options */ "./node_modules/webpack/buildin/amd-options.js"); + let HEX_CHARS = '0123456789abcdef'.split(''); + let EXTRA = [-2147483648, 8388608, 32768, 128]; + let SHIFT = [24, 16, 8, 0]; + let OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + let blocks = []; + let createOutputMethod = function (outputType) { + return function (message) { + return new Sha1(true).update(message)[outputType](); + }; + }; + let createMethod = function () { + let method = createOutputMethod('hex'); + if (NODE_JS) { + method = nodeWrap(method); + } + method.create = function () { + return new Sha1(); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type); + } + return method; + }; + var nodeWrap = function (method) { + var crypto = eval("require('crypto')"); + var Buffer = eval("require('buffer').Buffer"); + var nodeMethod = function (message) { + if (typeof message === 'string') { + return crypto.createHash('sha1').update(message, 'utf8').digest('hex'); + } + else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + else if (message.length === undefined) { + return method(message); + } + return crypto.createHash('sha1').update(new Buffer(message)).digest('hex'); + }; + return nodeMethod; + }; + function Sha1(sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } + else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + this.h0 = 0x67452301; + this.h1 = 0xEFCDAB89; + this.h2 = 0x98BADCFE; + this.h3 = 0x10325476; + this.h4 = 0xC3D2E1F0; + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + } + Sha1.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString = typeof (message) !== 'string'; + if (notString && message.constructor === root.ArrayBuffer) { + message = new Uint8Array(message); + } + var code, index = 0, i, length = message.length || 0, blocks = this.blocks; + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; + } + } + else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >> 2] |= code << SHIFT[i++ & 3]; + } + else if (code < 0x800) { + blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + else if (code < 0xd800 || code >= 0xe000) { + blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } + else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; + Sha1.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + var blocks = this.blocks, i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + Sha1.prototype.hash = function () { + var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4; + var f, j, t, blocks = this.blocks; + for (j = 16; j < 80; ++j) { + t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; + blocks[j] = (t << 1) | (t >>> 31); + } + for (j = 0; j < 20; j += 5) { + f = (b & c) | ((~b) & d); + t = (a << 5) | (a >>> 27); + e = t + f + e + 1518500249 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = (a & b) | ((~a) & c); + t = (e << 5) | (e >>> 27); + d = t + f + d + 1518500249 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = (e & a) | ((~e) & b); + t = (d << 5) | (d >>> 27); + c = t + f + c + 1518500249 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = (d & e) | ((~d) & a); + t = (c << 5) | (c >>> 27); + b = t + f + b + 1518500249 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = (c & d) | ((~c) & e); + t = (b << 5) | (b >>> 27); + a = t + f + a + 1518500249 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 40; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e + 1859775393 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d + 1859775393 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c + 1859775393 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b + 1859775393 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a + 1859775393 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 60; j += 5) { + f = (b & c) | (b & d) | (c & d); + t = (a << 5) | (a >>> 27); + e = t + f + e - 1894007588 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = (a & b) | (a & c) | (b & c); + t = (e << 5) | (e >>> 27); + d = t + f + d - 1894007588 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = (e & a) | (e & b) | (a & b); + t = (d << 5) | (d >>> 27); + c = t + f + c - 1894007588 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = (d & e) | (d & a) | (e & a); + t = (c << 5) | (c >>> 27); + b = t + f + b - 1894007588 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = (c & d) | (c & e) | (d & e); + t = (b << 5) | (b >>> 27); + a = t + f + a - 1894007588 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 80; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e - 899497514 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d - 899497514 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c - 899497514 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b - 899497514 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a - 899497514 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + }; + Sha1.prototype.hex = function () { + this.finalize(); + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F]; + }; + Sha1.prototype.toString = Sha1.prototype.hex; + Sha1.prototype.digest = function () { + this.finalize(); + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + return [ + (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, + (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, + (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, + (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, + (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF + ]; + }; + Sha1.prototype.array = Sha1.prototype.digest; + Sha1.prototype.arrayBuffer = function () { + this.finalize(); + var buffer = new ArrayBuffer(20); + var dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + return buffer; + }; + var exports = createMethod(); + if (COMMON_JS) { + module.exports = exports; + } + else { + root._sha1 = exports; + if (AMD) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () { + return exports; + }).call(exports, __webpack_require__, exports, module), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } + } + })(); + function encode_text(buffer) { + if (window.TextEncoder) { + return new TextEncoder().encode(buffer).buffer; + } + let utf8 = unescape(encodeURIComponent(buffer)); + let result = new Uint8Array(utf8.length); + for (let i = 0; i < utf8.length; i++) { + result[i] = utf8.charCodeAt(i); + } + return result.buffer; + } + sha.encode_text = encode_text; + function sha1(message) { + if (!(typeof (message) === "string" || message instanceof ArrayBuffer)) + throw "Invalid type!"; + let buffer = message instanceof ArrayBuffer ? message : encode_text(message); + if (!crypto || !crypto.subtle || !crypto.subtle.digest || /Edge/.test(navigator.userAgent)) + return new Promise(resolve => { + resolve(_sha1.arrayBuffer(buffer)); + }); + else + return crypto.subtle.digest("SHA-1", buffer); + } + sha.sha1 = sha1; +})(sha = exports.sha || (exports.sha = {})); + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/process/browser.js */ "./node_modules/process/browser.js"), __webpack_require__(/*! ./../../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) + +/***/ }), + +/***/ "./shared/js/crypto/uid.ts": +/*!*********************************!*\ + !*** ./shared/js/crypto/uid.ts ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} +exports.guid = guid; + + +/***/ }), + +/***/ "./shared/js/i18n/localize.ts": +/*!************************************!*\ + !*** ./shared/js/i18n/localize.ts ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const uid_1 = __webpack_require__(/*! ../crypto/uid */ "./shared/js/crypto/uid.ts"); +const log_1 = __webpack_require__(/*! ../log */ "./shared/js/log.ts"); +const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); +const settings_1 = __webpack_require__(/*! ../settings */ "./shared/js/settings.ts"); +const modal_1 = __webpack_require__(/*! ../ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); +var i18n; +(function (i18n) { + let translations = []; + let fast_translate = {}; + function tr(message, key) { + const sloppy = fast_translate[message]; + if (sloppy) + return sloppy; + log_1.log.info(log_1.LogCategory.I18N, "Translating \"%s\". Default: \"%s\"", key, message); + let translated = message; + for (const translation of translations) { + if (translation.key.message == message) { + translated = translation.translated; + break; + } + } + fast_translate[message] = translated; + return translated; + } + i18n.tr = tr; + function tra(message, ...args) { + message = tr(message); + return chat_1.MessageHelper.formatMessage(message, ...args); + } + i18n.tra = tra; + function load_translation_file(url, path) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + $.ajax({ + url: url, + async: true, + success: result => { + try { + const file = (typeof (result) === "string" ? JSON.parse(result) : result); + if (!file) { + reject("Invalid json"); + return; + } + file.full_url = url; + file.path = path; + resolve(file); + } + catch (error) { + log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to load translation file %s. Failed to parse or process json: %o"), url, error); + reject(tr("Failed to process or parse json!")); + } + }, + error: (xhr, error) => { + reject(tr("Failed to load file: ") + error); + } + }); + }); + }); + } + function load_file(url, path) { + return load_translation_file(url, path).then((result) => __awaiter(this, void 0, void 0, function* () { + try { + tr("Dummy translation test"); + } + catch (error) { + throw "dummy test failed"; + } + log_1.log.info(log_1.LogCategory.I18N, tr("Successfully initialized up translation file from %s"), url); + translations = result.translations; + })).catch(error => { + log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to load translation file from \"%s\". Error: %o"), url, error); + return Promise.reject(error); + }); + } + i18n.load_file = load_file; + function load_repository0(repo, reload) { + return __awaiter(this, void 0, void 0, function* () { + if (!repo.load_timestamp || repo.load_timestamp < 1000 || reload) { + const info_json = yield new Promise((resolve, reject) => { + $.ajax({ + url: repo.url + "/info.json", + async: true, + cache: !reload, + success: result => { + const file = (typeof (result) === "string" ? JSON.parse(result) : result); + if (!file) { + reject("Invalid json"); + return; + } + resolve(file); + }, + error: (xhr, error) => { + reject(tr("Failed to load file: ") + error); + } + }); + }); + Object.assign(repo, info_json); + } + if (!repo.unique_id) + repo.unique_id = uid_1.guid(); + repo.translations = repo.translations || []; + repo.load_timestamp = Date.now(); + }); + } + function load_repository(url) { + return __awaiter(this, void 0, void 0, function* () { + const result = {}; + result.url = url; + yield load_repository0(result, false); + return result; + }); + } + i18n.load_repository = load_repository; + let config; + (function (config_1) { + const repository_config_key = "i18n.repository"; + let _cached_repository_config; + function repository_config() { + if (_cached_repository_config) + return _cached_repository_config; + const config_string = localStorage.getItem(repository_config_key); + let config; + try { + config = config_string ? JSON.parse(config_string) : {}; + } + catch (error) { + log_1.log.error(log_1.LogCategory.I18N, tr("Failed to parse repository config: %o"), error); + } + config.repositories = config.repositories || []; + for (const repo of config.repositories) + (repo.repository || { load_timestamp: 0 }).load_timestamp = 0; + if (config.repositories.length == 0) { + load_repository(settings_1.StaticSettings.instance.static("i18n.default_repository", "https://web.teaspeak.de/i18n/")).then(repo => { + log_1.log.info(log_1.LogCategory.I18N, tr("Successfully added default repository from \"%s\"."), repo.url); + register_repository(repo); + }).catch(error => { + log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to add default repository. Error: %o"), error); + }); + } + return _cached_repository_config = config; + } + config_1.repository_config = repository_config; + function save_repository_config() { + localStorage.setItem(repository_config_key, JSON.stringify(_cached_repository_config)); + } + config_1.save_repository_config = save_repository_config; + const translation_config_key = "i18n.translation"; + let _cached_translation_config; + function translation_config() { + if (_cached_translation_config) + return _cached_translation_config; + const config_string = localStorage.getItem(translation_config_key); + try { + _cached_translation_config = config_string ? JSON.parse(config_string) : {}; + } + catch (error) { + log_1.log.error(log_1.LogCategory.I18N, tr("Failed to initialize translation config. Using default one. Error: %o"), error); + _cached_translation_config = {}; + } + return _cached_translation_config; + } + config_1.translation_config = translation_config; + function save_translation_config() { + localStorage.setItem(translation_config_key, JSON.stringify(_cached_translation_config)); + } + config_1.save_translation_config = save_translation_config; + })(config = i18n.config || (i18n.config = {})); + function register_repository(repository) { + if (!repository) + return; + for (const repo of config.repository_config().repositories) + if (repo.url == repository.url) + return; + config.repository_config().repositories.push(repository); + config.save_repository_config(); + } + i18n.register_repository = register_repository; + function registered_repositories() { + return config.repository_config().repositories.map(e => e.repository || { url: e.url, load_timestamp: 0 }); + } + i18n.registered_repositories = registered_repositories; + function delete_repository(repository) { + if (!repository) + return; + for (const repo of [...config.repository_config().repositories]) + if (repo.url == repository.url) { + config.repository_config().repositories.remove(repo); + } + config.save_repository_config(); + } + i18n.delete_repository = delete_repository; + function iterate_repositories(callback_entry) { + return __awaiter(this, void 0, void 0, function* () { + const promises = []; + for (const repository of registered_repositories()) { + promises.push(load_repository0(repository, false).then(() => callback_entry(repository)).catch(error => { + log_1.log.warn(log_1.LogCategory.I18N, "Failed to fetch repository %s. error: %o", repository.url, error); + })); + } + yield Promise.all(promises); + }); + } + i18n.iterate_repositories = iterate_repositories; + function select_translation(repository, entry) { + const cfg = config.translation_config(); + if (entry && repository) { + cfg.current_language = entry.name; + cfg.current_repository_url = repository.url; + cfg.current_translation_url = repository.url + entry.path; + cfg.current_translation_path = entry.path; + } + else { + cfg.current_language = undefined; + cfg.current_repository_url = undefined; + cfg.current_translation_url = undefined; + cfg.current_translation_path = undefined; + } + config.save_translation_config(); + } + i18n.select_translation = select_translation; + function initialize() { + return __awaiter(this, void 0, void 0, function* () { + const rcfg = config.repository_config(); + const cfg = config.translation_config(); + if (cfg.current_translation_url) { + try { + yield load_file(cfg.current_translation_url, cfg.current_translation_path); + } + catch (error) { + console.error(tr("Failed to initialize selected translation: %o"), error); + const show_error = () => { + modal_1.createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open(); + }; + if (loader.running()) + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + priority: 10, + function: () => __awaiter(this, void 0, void 0, function* () { return show_error(); }), + name: "I18N error display" + }); + else + show_error(); + } + } + }); + } + i18n.initialize = initialize; +})(i18n = exports.i18n || (exports.i18n = {})); +const tr = i18n.tr; +const tra = i18n.tra; +window.tr = i18n.tr; +window.tra = i18n.tra; + + +/***/ }), + +/***/ "./shared/js/log.ts": +/*!**************************!*\ + !*** ./shared/js/log.ts ***! + \**************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +throw new Error("Module parse failed: Binding arguments in strict mode (151:43)\nFile was processed with these loaders:\n * ./node_modules/ts-loader/index.js\nYou may need an additional loader to handle the result of these loaders.\n| }\n| log_1.group = group;\n> function table(level, category, title, arguments) {\n| if (group_mode == GroupMode.NATIVE) {\n| console.groupCollapsed(title);"); + +/***/ }), + +/***/ "./shared/js/main.ts": +/*!***************************!*\ + !*** ./shared/js/main.ts ***! + \***************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var spawnYesNo = ModalConnect_1.Modals.spawnYesNo; +const BrowserIPC_1 = __webpack_require__(/*! ./BrowserIPC */ "./shared/js/BrowserIPC.ts"); +const log_1 = __webpack_require__(/*! ./log */ "./shared/js/log.ts"); +const ConnectionProfile_1 = __webpack_require__(/*! ./profiles/ConnectionProfile */ "./shared/js/profiles/ConnectionProfile.ts"); +const ModalConnect_1 = __webpack_require__(/*! ./ui/modal/ModalConnect */ "./shared/js/ui/modal/ModalConnect.ts"); +const settings_1 = __webpack_require__(/*! ./settings */ "./shared/js/settings.ts"); +const localize_1 = __webpack_require__(/*! ./i18n/localize */ "./shared/js/i18n/localize.ts"); +const modal_1 = __webpack_require__(/*! ./ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); +const chat_1 = __webpack_require__(/*! ./ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); +exports.js_render = window.jsrender || $; +exports.native_client = window.require !== undefined; +function getUserMediaFunctionPromise() { + if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) + return constraints => navigator.mediaDevices.getUserMedia(constraints); + const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + if (!_callbacked_function) + return undefined; + return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); +} +exports.getUserMediaFunctionPromise = getUserMediaFunctionPromise; +function setup_close() { + window.onbeforeunload = event => { + if (ConnectionProfile_1.profiles.requires_save()) + ConnectionProfile_1.profiles.save(); + if (!settings_1.settings.static(settings_1.Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) { + const active_connections = server_connections.server_connection_handlers().filter(e => e.connected); + if (active_connections.length == 0) + return; + if (!exports.native_client) { + event.returnValue = "Are you really sure?
You're still connected!"; + } + else { + const do_exit = () => { + const dp = server_connections.server_connection_handlers().map(e => { + if (e.serverConnection.connected()) + return e.serverConnection.disconnect(tr("client closed")); + return Promise.resolve(); + }).map(e => e.catch(error => { + console.warn(tr("Failed to disconnect from server on client close: %o"), e); + })); + const exit = () => { + const { remote } = exports.nodeRequire('electron'); + remote.getCurrentWindow().close(); + }; + Promise.all(dp).then(exit); + setTimeout(exit, 2500); + }; + if (window.open_connected_question) { + event.preventDefault(); + event.returnValue = "question"; + window.open_connected_question().then(result => { + if (result) { + window.onbeforeunload = e => e.preventDefault(); + setTimeout(() => window.onbeforeunload, 5000); + do_exit(); + } + }); + } + else { + do_exit(); + } + } + } + }; +} +exports.setup_close = setup_close; +function setup_jsrender() { + if (!exports.js_render) { + loader.critical_error("Missing jsrender extension!"); + return false; + } + if (!exports.js_render.views) { + loader.critical_error("Missing jsrender viewer extension!"); + return false; + } + exports.js_render.views.settings.allowCode(true); + exports.js_render.views.tags("rnd", (argument) => { + let min = parseInt(argument.substr(0, argument.indexOf('~'))); + let max = parseInt(argument.substr(argument.indexOf('~') + 1)); + return (Math.round(Math.random() * (min + max + 1) - min)).toString(); + }); + exports.js_render.views.tags("fmt_date", (...args) => { + return moment(args[0]).format(args[1]); + }); + exports.js_render.views.tags("tr", (...args) => { + return tr(args[0]); + }); + $(".jsrender-template").each((idx, _entry) => { + if (!exports.js_render.templates(_entry.id, _entry.innerHTML)) { + log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to setup cache for js renderer template %s!"), _entry.id); + } + else + log_1.log.info(log_1.LogCategory.GENERAL, tr("Successfully loaded jsrender template %s"), _entry.id); + }); + return true; +} +exports.setup_jsrender = setup_jsrender; +function initialize() { + return __awaiter(this, void 0, void 0, function* () { + settings_1.Settings.initialize(); + try { + yield localize_1.i18n.initialize(); + } + catch (error) { + console.error(tr("Failed to initialized the translation system!\nError: %o"), error); + loader.critical_error("Failed to setup the translation system"); + return; + } + BrowserIPC_1.bipc.setup(); + }); +} +exports.initialize = initialize; +function initialize_app() { + return __awaiter(this, void 0, void 0, function* () { + try { + const main = $("#tmpl_main").renderTag({ + multi_session: !settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_MULTI_SESSION), + app_version: app.ui_version() + }).dividerfy(); + $("body").append(main); + } + catch (error) { + log_1.log.error(log_1.LogCategory.GENERAL, error); + loader.critical_error(tr("Failed to setup main page!")); + return; + } + control_bar = new ControlBar($("#control_bar")); + if (!audio.player.initialize()) + console.warn(tr("Failed to initialize audio controller!")); + audio.player.on_ready(() => { + if (audio.player.set_master_volume) + audio.player.on_ready(() => audio.player.set_master_volume(settings_1.settings.global(settings_1.Settings.KEY_SOUND_MASTER) / 100)); + else + log_1.log.warn(log_1.LogCategory.GENERAL, tr("Client does not support audio.player.set_master_volume()... May client is too old?")); + if (audio.recorder.device_refresh_available()) + audio.recorder.refresh_devices(); + }); + default_recorder = new RecorderProfile("default"); + default_recorder.initialize().catch(error => { + log_1.log.error(log_1.LogCategory.AUDIO, tr("Failed to initialize default recorder: %o"), error); + }); + sound.initialize().then(() => { + log_1.log.info(log_1.LogCategory.AUDIO, tr("Sounds initialized")); + }); + sound.set_master_volume(settings_1.settings.global(settings_1.Settings.KEY_SOUND_MASTER_SOUNDS) / 100); + yield ConnectionProfile_1.profiles.load(); + try { + yield ppt.initialize(); + } + catch (error) { + log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to initialize ppt!\nError: %o"), error); + loader.critical_error(tr("Failed to initialize ppt!")); + return; + } + setup_close(); + }); +} +exports.initialize_app = initialize_app; +function str2ab8(str) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} +exports.str2ab8 = str2ab8; +function arrayBufferBase64(base64) { + base64 = atob(base64); + const buf = new ArrayBuffer(base64.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = base64.length; i < strLen; i++) { + bufView[i] = base64.charCodeAt(i); + } + return buf; +} +exports.arrayBufferBase64 = arrayBufferBase64; +function base64_encode_ab(source) { + const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let base64 = ""; + const bytes = new Uint8Array(source); + const byte_length = bytes.byteLength; + const byte_reminder = byte_length % 3; + const main_length = byte_length - byte_reminder; + let a, b, c, d; + let chunk; + for (let i = 0; i < main_length; i = i + 3) { + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + a = (chunk & 16515072) >> 18; + b = (chunk & 258048) >> 12; + c = (chunk & 4032) >> 6; + d = (chunk & 63) >> 0; + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; + } + if (byte_reminder == 1) { + chunk = bytes[main_length]; + a = (chunk & 252) >> 2; + b = (chunk & 3) << 4; + base64 += encodings[a] + encodings[b] + '=='; + } + else if (byte_reminder == 2) { + chunk = (bytes[main_length] << 8) | bytes[main_length + 1]; + a = (chunk & 64512) >> 10; + b = (chunk & 1008) >> 4; + c = (chunk & 15) << 2; + base64 += encodings[a] + encodings[b] + encodings[c] + '='; + } + return base64; +} +exports.base64_encode_ab = base64_encode_ab; +function handle_connect_request(properties, connection) { + const profile_uuid = properties.profile || (ConnectionProfile_1.profiles.default_profile() || { id: 'default' }).id; + const profile = ConnectionProfile_1.profiles.find_profile(profile_uuid) || ConnectionProfile_1.profiles.default_profile(); + const username = properties.username || profile.connect_username(); + const password = properties.password ? properties.password.value : ""; + const password_hashed = properties.password ? properties.password.hashed : false; + if (profile && profile.valid()) { + connection.startConnection(properties.address, profile, true, { + nickname: username, + password: password.length > 0 ? { + password: password, + hashed: password_hashed + } : undefined + }); + server_connections.set_active_connection_handler(connection); + } + else { + ModalConnect_1.Modals.spawnConnectModal({}, { + url: properties.address, + enforce: true + }, { + profile: profile, + enforce: true + }); + } +} +function main() { + { + const font = settings_1.settings.static_global(settings_1.Settings.KEY_FONT_SIZE, 14); + $(document.body).css("font-size", font + "px"); + } + $(document).on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + if (!settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU)) + event.preventDefault(); + }); + top_menu.initialize(); + server_connections = new ServerConnectionManager($("#connection-handlers")); + control_bar.initialise(); + const initial_handler = server_connections.spawn_server_connection_handler(); + initial_handler.acquire_recorder(default_recorder, false); + control_bar.set_connection_handler(initial_handler); + ConnectionProfile_1.profiles.identities.update_forum(); + let _resize_timeout; + $(window).on('resize', event => { + if (event.target !== window) + return; + if (_resize_timeout) + clearTimeout(_resize_timeout); + _resize_timeout = setTimeout(() => { + for (const connection of server_connections.server_connection_handlers()) + connection.invoke_resized_on_activate = true; + const active_connection = server_connections.active_connection_handler(); + if (active_connection) + active_connection.resize_elements(); + $(".window-resize-listener").trigger('resize'); + }, 1000); + }); + stats.initialize({ + verbose: true, + anonymize_ip_addresses: true, + volatile_collection_only: false + }); + stats.register_user_count_listener(status => { + log_1.log.info(log_1.LogCategory.STATISTICS, tr("Received user count update: %o"), status); + }); + server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]); + window.test_upload = (message) => { + message = message || "Hello World"; + const connection = server_connections.active_connection_handler(); + connection.fileManager.upload_file({ + size: message.length, + overwrite: true, + channel: connection.getClient().currentChannel(), + name: '/HelloWorld.txt', + path: '' + }).then(key => { + const upload = new RequestFileUpload(key); + const buffer = new Uint8Array(message.length); + { + for (let index = 0; index < message.length; index++) + buffer[index] = message.charCodeAt(index); + } + upload.put_data(buffer).catch(error => { + console.error(error); + }); + }); + }; + setTimeout(() => { + const connection = server_connections.active_connection_handler(); + }, 4000); + if (settings_1.settings.static_global(settings_1.Settings.KEY_USER_IS_NEW)) { + const modal = ModalConnect_1.Modals.openModalNewcomer(); + modal.close_listener.push(() => settings_1.settings.changeGlobal(settings_1.Settings.KEY_USER_IS_NEW, false)); + } +} +const task_teaweb_starter = { + name: "voice app starter", + function: () => __awaiter(void 0, void 0, void 0, function* () { + try { + yield initialize_app(); + main(); + if (!audio.player.initialized()) { + log_1.log.info(log_1.LogCategory.VOICE, tr("Initialize audio controller later!")); + if (!audio.player.initializeFromGesture) { + console.error(tr("Missing audio.player.initializeFromGesture")); + } + else + $(document).one('click', event => audio.player.initializeFromGesture()); + } + } + catch (ex) { + console.error(ex.stack); + if (ex instanceof ReferenceError || ex instanceof TypeError) + ex = ex.name + ": " + ex.message; + loader.critical_error("Failed to invoke main function:
" + ex); + } + }), + priority: 10 +}; +const task_connect_handler = { + name: "Connect handler", + function: () => __awaiter(void 0, void 0, void 0, function* () { + const address = settings_1.settings.static(settings_1.Settings.KEY_CONNECT_ADDRESS, ""); + const chandler = BrowserIPC_1.bipc.get_connect_handler(); + if (settings_1.settings.static(settings_1.Settings.KEY_FLAG_CONNECT_DEFAULT, false) && address) { + const connect_data = { + address: address, + profile: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_PROFILE, ""), + username: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_USERNAME, ""), + password: { + value: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_PASSWORD, ""), + hashed: settings_1.settings.static(settings_1.Settings.KEY_FLAG_CONNECT_PASSWORD, false) + } + }; + if (chandler) { + try { + yield chandler.post_connect_request(connect_data, () => new Promise((resolve, reject) => { + spawnYesNo(tr("Another TeaWeb instance is already running"), tra("Another TeaWeb instance is already running.{:br:}Would you like to connect there?"), response => { + resolve(response); + }, { + closeable: false + }).open(); + })); + log_1.log.info(log_1.LogCategory.CLIENT, tr("Executed connect successfully in another browser window. Closing this window")); + const message = "You're connecting to {0} within the other TeaWeb instance.{:br:}" + + "You could now close this page."; + modal_1.createInfoModal(tr("Connecting successfully within other instance"), chat_1.MessageHelper.formatMessage(tr(message), connect_data.address), { + closeable: false, + footer: undefined + }).open(); + return; + } + catch (error) { + log_1.log.info(log_1.LogCategory.CLIENT, tr("Failed to execute connect within other TeaWeb instance. Using this one. Error: %o"), error); + } + } + loader.register_task(loader.Stage.LOADED, { + priority: 0, + function: () => __awaiter(void 0, void 0, void 0, function* () { return handle_connect_request(connect_data, server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler()); }), + name: tr("default url connect") + }); + } + if (chandler) { + chandler.callback_available = data => { + return !settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_MULTI_SESSION); + }; + chandler.callback_execute = data => { + handle_connect_request(data, server_connections.spawn_server_connection_handler()); + return true; + }; + } + loader.register_task(loader.Stage.LOADED, task_teaweb_starter); + }), + priority: 10 +}; +const task_certificate_callback = { + name: "certificate accept tester", + function: () => __awaiter(void 0, void 0, void 0, function* () { + const certificate_accept = settings_1.settings.static_global(settings_1.Settings.KEY_CERTIFICATE_CALLBACK, undefined); + if (certificate_accept) { + log_1.log.info(log_1.LogCategory.IPC, tr("Using this instance as certificate callback. ID: %s"), certificate_accept); + try { + try { + yield BrowserIPC_1.bipc.get_handler().post_certificate_accpected(certificate_accept); + } + catch (e) { } + log_1.log.info(log_1.LogCategory.IPC, tr("Other instance has acknowledged out work. Closing this window.")); + const seconds_tag = $.spawn("a"); + let seconds = 5; + let interval_id; + interval_id = setInterval(() => { + seconds--; + seconds_tag.text(seconds.toString()); + if (seconds <= 0) { + clearTimeout(interval_id); + log_1.log.info(log_1.LogCategory.GENERAL, tr("Closing window")); + window.close(); + return; + } + }, 1000); + const message = "You've successfully accepted the certificate.{:br:}" + + "This page will close in {0} seconds."; + modal_1.createInfoModal(tr("Certificate acccepted successfully"), chat_1.MessageHelper.formatMessage(tr(message), seconds_tag), { + closeable: false, + footer: undefined + }).open(); + return; + } + catch (error) { + log_1.log.warn(log_1.LogCategory.IPC, tr("Failed to successfully post certificate accept status: %o"), error); + } + } + else { + log_1.log.info(log_1.LogCategory.IPC, tr("We're not used to accept certificated. Booting app.")); + } + loader.register_task(loader.Stage.LOADED, task_connect_handler); + }), + priority: 10 +}; +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "jrendere initialize", + function: () => __awaiter(void 0, void 0, void 0, function* () { + try { + if (!setup_jsrender()) + throw "invalid load"; + } + catch (error) { + loader.critical_error(tr("Failed to setup jsrender")); + console.error(tr("Failed to load jsrender! %o"), error); + return; + } + }), + priority: 100 +}); +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "app starter", + function: () => __awaiter(void 0, void 0, void 0, function* () { + try { + yield initialize(); + if (app.is_web()) { + loader.register_task(loader.Stage.LOADED, task_certificate_callback); + } + else { + loader.register_task(loader.Stage.LOADED, task_teaweb_starter); + } + } + catch (ex) { + if (ex instanceof Error || typeof (ex.stack) !== "undefined") + console.error((tr || (msg => msg))("Critical error stack trace: %o"), ex.stack); + if (ex instanceof ReferenceError || ex instanceof TypeError) + ex = ex.name + ": " + ex.message; + loader.critical_error("Failed to boot app function:
" + ex); + } + }), + priority: 1000 +}); + + +/***/ }), + +/***/ "./shared/js/profiles/ConnectionProfile.ts": +/*!*************************************************!*\ + !*** ./shared/js/profiles/ConnectionProfile.ts ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); +const modal_1 = __webpack_require__(/*! ../ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); +const uid_1 = __webpack_require__(/*! ../crypto/uid */ "./shared/js/crypto/uid.ts"); +const Identity_1 = __webpack_require__(/*! ./Identity */ "./shared/js/profiles/Identity.ts"); +const TeaForumIdentity_1 = __webpack_require__(/*! ./identities/TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); +const TeamSpeakIdentity_1 = __webpack_require__(/*! ./identities/TeamSpeakIdentity */ "./shared/js/profiles/identities/TeamSpeakIdentity.ts"); +class ConnectionProfile { + constructor(id) { + this.selected_identity_type = "unset"; + this.identities = {}; + this.id = id; + } + connect_username() { + if (this.default_username && this.default_username !== "Another TeaSpeak user") + return this.default_username; + let selected = this.selected_identity(); + let name = selected ? selected.fallback_name() : undefined; + return name || "Another TeaSpeak user"; + } + selected_identity(current_type) { + if (!current_type) + current_type = this.selected_type(); + if (current_type === undefined) + return undefined; + if (current_type == Identity_1.IdentitifyType.TEAFORO) { + return TeaForumIdentity_1.static_forum_identity(); + } + else if (current_type == Identity_1.IdentitifyType.TEAMSPEAK || current_type == Identity_1.IdentitifyType.NICKNAME) { + return this.identities[Identity_1.IdentitifyType[current_type].toLowerCase()]; + } + return undefined; + } + selected_type() { + return this.selected_identity_type ? Identity_1.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; + } + set_identity(type, identity) { + this.identities[Identity_1.IdentitifyType[type].toLowerCase()] = identity; + } + spawn_identity_handshake_handler(connection) { + const identity = this.selected_identity(); + if (!identity) + return undefined; + return identity.spawn_identity_handshake_handler(connection); + } + encode() { + const identity_data = {}; + for (const key in this.identities) + if (this.identities[key]) + identity_data[key] = this.identities[key].encode(); + return JSON.stringify({ + version: 1, + username: this.default_username, + password: this.default_password, + profile_name: this.profile_name, + identity_type: this.selected_identity_type, + identity_data: identity_data, + id: this.id + }); + } + valid() { + const identity = this.selected_identity(); + if (!identity || !identity.valid()) + return false; + return true; + } +} +exports.ConnectionProfile = ConnectionProfile; +function decode_profile(data) { + return __awaiter(this, void 0, void 0, function* () { + data = JSON.parse(data); + if (data.version !== 1) + return "invalid version"; + const result = new ConnectionProfile(data.id); + result.default_username = data.username; + result.default_password = data.password; + result.profile_name = data.profile_name; + result.selected_identity_type = (data.identity_type || "").toLowerCase(); + if (data.identity_data) { + for (const key in data.identity_data) { + const type = Identity_1.IdentitifyType[key.toUpperCase()]; + const _data = data.identity_data[key]; + if (type == undefined) + continue; + const identity = yield Identity_1.decode_identity(type, _data); + if (identity == undefined) + continue; + result.identities[key.toLowerCase()] = identity; + } + } + return result; + }); +} +let available_profiles = []; +function load() { + return __awaiter(this, void 0, void 0, function* () { + available_profiles = []; + const profiles_json = localStorage.getItem("profiles"); + let profiles_data = (() => { + try { + return profiles_json ? JSON.parse(profiles_json) : { version: 0 }; + } + catch (error) { + debugger; + console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); + modal_1.createErrorModal(tr("Profile data invalid"), chat_1.MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); + return { version: 0 }; + } + })(); + if (profiles_data.version === 0) { + profiles_data = { + version: 1, + profiles: [] + }; + } + if (profiles_data.version == 1) { + for (const profile_data of profiles_data.profiles) { + const profile = yield decode_profile(profile_data); + if (typeof (profile) === 'string') { + console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); + continue; + } + available_profiles.push(profile); + } + } + if (!find_profile("default")) { + { + const profile = create_new_profile("default", "default"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "Default Profile"; + try { + const identity = yield TeamSpeakIdentity_1.TeaSpeakIdentity.generate_new(); + let active = true; + setTimeout(() => { + active = false; + }, 1000); + yield identity.improve_level(8, 1, () => active); + profile.set_identity(Identity_1.IdentitifyType.TEAMSPEAK, identity); + profile.selected_identity_type = Identity_1.IdentitifyType[Identity_1.IdentitifyType.TEAMSPEAK]; + } + catch (error) { + modal_1.createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); + } + } + { + const profile = create_new_profile("TeaSpeak Forum", "teaforo"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "TeaSpeak Forum profile"; + profile.set_identity(Identity_1.IdentitifyType.TEAFORO, TeaForumIdentity_1.static_forum_identity()); + profile.selected_identity_type = Identity_1.IdentitifyType[Identity_1.IdentitifyType.TEAFORO]; + } + save(); + } + }); +} +exports.load = load; +function create_new_profile(name, id) { + const profile = new ConnectionProfile(id || uid_1.guid()); + profile.profile_name = name; + profile.default_username = ""; + available_profiles.push(profile); + return profile; +} +exports.create_new_profile = create_new_profile; +let _requires_save = false; +function save() { + const profiles = []; + for (const profile of available_profiles) + profiles.push(profile.encode()); + const data = JSON.stringify({ + version: 1, + profiles: profiles + }); + localStorage.setItem("profiles", data); +} +exports.save = save; +function mark_need_save() { + _requires_save = true; +} +exports.mark_need_save = mark_need_save; +function requires_save() { + return _requires_save; +} +exports.requires_save = requires_save; +function profiles() { + return available_profiles; +} +exports.profiles = profiles; +function find_profile(id) { + for (const profile of profiles()) + if (profile.id == id) + return profile; + return undefined; +} +exports.find_profile = find_profile; +function find_profile_by_name(name) { + name = name.toLowerCase(); + for (const profile of profiles()) + if ((profile.profile_name || "").toLowerCase() == name) + return profile; + return undefined; +} +exports.find_profile_by_name = find_profile_by_name; +function default_profile() { + return find_profile("default"); +} +exports.default_profile = default_profile; +function set_default_profile(profile) { + const old_default = default_profile(); + if (old_default && old_default != profile) { + old_default.id = uid_1.guid(); + } + profile.id = "default"; + return old_default; +} +exports.set_default_profile = set_default_profile; +function delete_profile(profile) { + available_profiles.remove(profile); +} +exports.delete_profile = delete_profile; + + +/***/ }), + +/***/ "./shared/js/profiles/Identity.ts": +/*!****************************************!*\ + !*** ./shared/js/profiles/Identity.ts ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const ConnectionBase_1 = __webpack_require__(/*! ../connection/ConnectionBase */ "./shared/js/connection/ConnectionBase.ts"); +const NameIdentity_1 = __webpack_require__(/*! ./identities/NameIdentity */ "./shared/js/profiles/identities/NameIdentity.ts"); +const TeaForumIdentity_1 = __webpack_require__(/*! ./identities/TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); +const TeamSpeakIdentity_1 = __webpack_require__(/*! ./identities/TeamSpeakIdentity */ "./shared/js/profiles/identities/TeamSpeakIdentity.ts"); +var IdentitifyType; +(function (IdentitifyType) { + IdentitifyType[IdentitifyType["TEAFORO"] = 0] = "TEAFORO"; + IdentitifyType[IdentitifyType["TEAMSPEAK"] = 1] = "TEAMSPEAK"; + IdentitifyType[IdentitifyType["NICKNAME"] = 2] = "NICKNAME"; +})(IdentitifyType = exports.IdentitifyType || (exports.IdentitifyType = {})); +function decode_identity(type, data) { + return __awaiter(this, void 0, void 0, function* () { + let identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity_1.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity_1.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeamSpeakIdentity_1.TeaSpeakIdentity(undefined, undefined); + break; + } + if (!identity) + return undefined; + try { + yield identity.decode(data); + } + catch (error) { + console.error(error); + return undefined; + } + return identity; + }); +} +exports.decode_identity = decode_identity; +function create_identity(type) { + let identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity_1.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity_1.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeamSpeakIdentity_1.TeaSpeakIdentity(undefined, undefined); + break; + } + return identity; +} +exports.create_identity = create_identity; +class HandshakeCommandHandler extends ConnectionBase_1.AbstractCommandHandler { + constructor(connection, handle) { + super(connection); + this.handle = handle; + } + handle_command(command) { + if ($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if (command.command == "error") { + return false; + } + else { + console.warn(tr("Received unknown command while handshaking (%o)"), command); + } + return true; + } +} +exports.HandshakeCommandHandler = HandshakeCommandHandler; +class AbstractHandshakeIdentityHandler { + constructor(connection) { + this.callbacks = []; + this.connection = connection; + } + register_callback(callback) { + this.callbacks.push(callback); + } + trigger_success() { + for (const callback of this.callbacks) + callback(true); + } + trigger_fail(message) { + for (const callback of this.callbacks) + callback(false, message); + } +} +exports.AbstractHandshakeIdentityHandler = AbstractHandshakeIdentityHandler; + + +/***/ }), + +/***/ "./shared/js/profiles/identities/NameIdentity.ts": +/*!*******************************************************!*\ + !*** ./shared/js/profiles/identities/NameIdentity.ts ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); +const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); +const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); +class NameHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new Identity_1.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + client_nickname: this.identity.name() + }).catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }).then(() => this.trigger_success()); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} +class NameIdentity { + constructor(name) { + this._name = name; + } + set_name(name) { this._name = name; } + name() { return this._name; } + fallback_name() { + return this._name; + } + uid() { + return btoa(this._name); + } + type() { + return Identity_1.IdentitifyType.NICKNAME; + } + valid() { + return this._name != undefined && this._name.length >= 5; + } + decode(data) { + data = JSON.parse(data); + if (data.version !== 1) + throw "invalid version"; + this._name = data["name"]; + return; + } + encode() { + return JSON.stringify({ + version: 1, + name: this._name + }); + } + spawn_identity_handshake_handler(connection) { + return new NameHandshakeHandler(connection, this); + } +} +exports.NameIdentity = NameIdentity; + + +/***/ }), + +/***/ "./shared/js/profiles/identities/TeaForumIdentity.ts": +/*!***********************************************************!*\ + !*** ./shared/js/profiles/identities/TeaForumIdentity.ts ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); +const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); +const teaspeak_forum_1 = __webpack_require__(/*! ./teaspeak-forum */ "./shared/js/profiles/identities/teaspeak-forum.ts"); +const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); +class TeaForumHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new Identity_1.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + data: this.identity.data().data_json() + }).catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + handle_proof(json) { + this.connection.send_command("handshakeindentityproof", { + proof: this.identity.data().data_sign() + }).catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} +class TeaForumIdentity { + constructor(data) { + this.identity_data = data; + } + valid() { + return !!this.identity_data && !this.identity_data.is_expired(); + } + data() { + return this.identity_data; + } + decode(data) { + data = JSON.parse(data); + if (data.version !== 1) + throw "invalid version"; + return; + } + encode() { + return JSON.stringify({ + version: 1 + }); + } + spawn_identity_handshake_handler(connection) { + return new TeaForumHandshakeHandler(connection, this); + } + fallback_name() { + return this.identity_data ? this.identity_data.name() : undefined; + } + type() { + return Identity_1.IdentitifyType.TEAFORO; + } + uid() { + return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); + } +} +exports.TeaForumIdentity = TeaForumIdentity; +let static_identity; +function set_static_identity(identity) { + static_identity = identity; +} +exports.set_static_identity = set_static_identity; +function update_forum() { + if (teaspeak_forum_1.forum.logged_in() && (!static_identity || static_identity.data() !== teaspeak_forum_1.forum.data())) { + static_identity = new TeaForumIdentity(teaspeak_forum_1.forum.data()); + } + else { + static_identity = undefined; + } +} +exports.update_forum = update_forum; +function valid_static_forum_identity() { + return static_identity && static_identity.valid(); +} +exports.valid_static_forum_identity = valid_static_forum_identity; +function static_forum_identity() { + return static_identity; +} +exports.static_forum_identity = static_forum_identity; + + +/***/ }), + +/***/ "./shared/js/profiles/identities/TeamSpeakIdentity.ts": +/*!************************************************************!*\ + !*** ./shared/js/profiles/identities/TeamSpeakIdentity.ts ***! + \************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const main_1 = __webpack_require__(/*! ../../main */ "./shared/js/main.ts"); +const sha_1 = __webpack_require__(/*! ../../crypto/sha */ "./shared/js/crypto/sha.ts"); +const asn1_1 = __webpack_require__(/*! ../../crypto/asn1 */ "./shared/js/crypto/asn1.ts"); +const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); +const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); +const settings_1 = __webpack_require__(/*! ../../settings */ "./shared/js/settings.ts"); +const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); +var CryptoHelper; +(function (CryptoHelper) { + function base64_url_encode(str) { + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); + } + CryptoHelper.base64_url_encode = base64_url_encode; + function base64_url_decode(str, pad) { + if (typeof (pad) === 'undefined' || pad) + str = (str + '===').slice(0, str.length + (str.length % 4)); + return str.replace(/-/g, '+').replace(/_/g, '/'); + } + CryptoHelper.base64_url_decode = base64_url_decode; + function arraybuffer_to_string(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } + CryptoHelper.arraybuffer_to_string = arraybuffer_to_string; + function export_ecc_key(crypto_key, public_key) { + return __awaiter(this, void 0, void 0, function* () { + const key_data = yield crypto.subtle.exportKey("jwk", crypto_key); + let index = 0; + const length = public_key ? 79 : 114; + const buffer = new Uint8Array(length); + { + buffer[index++] = 0x30; + buffer[index++] = 0x00; + } + { + buffer[index++] = 0x03; + buffer[index++] = 0x02; + buffer[index++] = 0x07; + buffer[index++] = public_key ? 0x00 : 0x80; + } + { + buffer[index++] = 0x02; + buffer[index++] = 0x01; + buffer[index++] = 0x20; + } + try { + buffer[index++] = 0x02; + buffer[index++] = 0x20; + const raw = atob(base64_url_decode(key_data.x, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse x coordinate (invalid base64)"; + throw error; + } + try { + buffer[index++] = 0x02; + buffer[index++] = 0x20; + const raw = atob(base64_url_decode(key_data.y, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; + } + if (!public_key) { + try { + buffer[index++] = 0x02; + buffer[index++] = 0x20; + const raw = atob(base64_url_decode(key_data.d, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; + } + } + buffer[1] = index - 2; + return main_1.base64_encode_ab(buffer.buffer.slice(0, index)); + }); + } + CryptoHelper.export_ecc_key = export_ecc_key; + const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; + function c_strlen(buffer, offset) { + let index = 0; + while (index + offset < buffer.length && buffer[index + offset] != 0) + index++; + return index; + } + function decrypt_ts_identity(buffer) { + return __awaiter(this, void 0, void 0, function* () { + const hash = new Uint8Array(yield sha_1.sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for (let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + const length = Math.min(buffer.length, 100); + for (let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + return arraybuffer_to_string(buffer); + }); + } + CryptoHelper.decrypt_ts_identity = decrypt_ts_identity; + function encrypt_ts_identity(buffer) { + return __awaiter(this, void 0, void 0, function* () { + const length = Math.min(buffer.length, 100); + for (let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + const hash = new Uint8Array(yield sha_1.sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for (let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + return main_1.base64_encode_ab(buffer); + }); + } + CryptoHelper.encrypt_ts_identity = encrypt_ts_identity; + function decode_tomcrypt_key(buffer) { + let decoded; + try { + decoded = asn1_1.asn1.decode(atob(buffer)); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse key buffer (invalid base64)"; + throw error; + } + let { x, y, k } = { + x: decoded.children[2].content(Infinity, asn1_1.asn1.TagType.VisibleString), + y: decoded.children[3].content(Infinity, asn1_1.asn1.TagType.VisibleString), + k: decoded.children[4].content(Infinity, asn1_1.asn1.TagType.VisibleString) + }; + if (x.length > 32) { + if (x.charCodeAt(0) != 0) + throw "Invalid X coordinate! (Too long)"; + x = x.substr(1); + } + if (y.length > 32) { + if (y.charCodeAt(0) != 0) + throw "Invalid Y coordinate! (Too long)"; + y = y.substr(1); + } + if (k.length > 32) { + if (k.charCodeAt(0) != 0) + throw "Invalid private coordinate! (Too long)"; + k = k.substr(1); + } + return { + crv: "P-256", + d: base64_url_encode(btoa(k)), + x: base64_url_encode(btoa(x)), + y: base64_url_encode(btoa(y)), + ext: true, + key_ops: ["deriveKey", "sign"], + kty: "EC", + }; + } + CryptoHelper.decode_tomcrypt_key = decode_tomcrypt_key; +})(CryptoHelper = exports.CryptoHelper || (exports.CryptoHelper = {})); +class TeaSpeakHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new Identity_1.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + publicKey: this.identity.public_key + }).catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + handle_proof(json) { + if (!json[0]["digest"]) { + this.trigger_fail("server too old"); + return; + } + this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { + this.connection.send_command("handshakeindentityproof", { proof: proof }).catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + if (error instanceof ServerConnectionDeclaration_1.CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + }).catch(error => { + this.trigger_fail("failed to sign message"); + }); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} +class IdentityPOWWorker { + initialize(key) { + return __awaiter(this, void 0, void 0, function* () { + this._worker = new Worker(settings_1.settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); + yield new Promise((resolve, reject) => { + const timeout_id = setTimeout(() => reject("timeout"), 1000); + this._worker.onmessage = event => { + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + this._worker.onerror = event => { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("POW Worker error %o"), event); + clearTimeout(timeout_id); + reject("Failed to load worker (" + event.message + ")"); + }; + }); + yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "set_data", + private_key: key, + code: "set_data" + }); + const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); + this._worker.onmessage = event => { + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + }); + }); + } + mine(hash, iterations, target, timeout) { + return __awaiter(this, void 0, void 0, function* () { + this._current_hash = hash; + if (target < this._best_level) + return true; + return yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "mine", + hash: this._current_hash, + iterations: iterations, + target: target, + code: "mine" + }); + const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + if (event.data.result) { + this._best_level = event.data.level; + this._current_hash = event.data.hash; + resolve(true); + } + else { + resolve(false); + } + }; + }); + }); + } + current_hash() { + return this._current_hash; + } + current_level() { + return this._best_level; + } + finalize(timeout) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + resolve(); + }; + }); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } + this._worker.terminate(); + this._worker = undefined; + }); + } + handle_message(message) { + log_1.log.info(log_1.LogCategory.IDENTITIES, tr("Received message: %o"), message); + } +} +class TeaSpeakIdentity { + constructor(private_key, hash, name, initialize) { + this.private_key = private_key; + this.hash_number = hash || "0"; + this._name = name; + if (this.private_key && (typeof (initialize) === "undefined" || initialize)) { + this.initialize().catch(error => { + log_1.log.error(log_1.LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); + this._initialized = false; + }); + } + } + static generate_new() { + return __awaiter(this, void 0, void 0, function* () { + let key; + try { + key = yield crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); + } + catch (e) { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); + throw "Failed to generate keypair"; + } + const private_key = yield CryptoHelper.export_ecc_key(key.privateKey, false); + const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); + yield identity.initialize(); + return identity; + }); + } + static import_ts(ts_string, ini) { + return __awaiter(this, void 0, void 0, function* () { + const parse_string = string => { + const V_index = string.indexOf('V'); + if (V_index == -1) + throw "invalid input (missing V)"; + return { + hash: string.substr(0, V_index), + data: string.substr(V_index + 1), + name: "TeaSpeak user" + }; + }; + const { hash, data, name } = (!ini ? () => parse_string(ts_string) : () => { + let identity, name; + for (const line of ts_string.split("\n")) { + if (line.startsWith("identity=")) + identity = line.substr(9); + else if (line.startsWith("nickname=")) + name = line.substr(9); + } + if (!identity) + throw "missing identity keyword"; + identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; + if (!identity) + throw "invalid identity key value"; + const result = parse_string(identity); + result.name = name || result.name; + return result; + })(); + if (!ts_string.match(/[0-9]+/g)) + throw "invalid hash!"; + let buffer; + try { + buffer = new Uint8Array(main_1.arrayBufferBase64(data)); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); + throw "failed to base data (base64 decode failed)"; + } + const key64 = yield CryptoHelper.decrypt_ts_identity(new Uint8Array(main_1.arrayBufferBase64(data))); + const identity = new TeaSpeakIdentity(key64, hash, name, false); + yield identity.initialize(); + return identity; + }); + } + fallback_name() { + return this._name; + } + uid() { + return this._unique_id; + } + type() { + return Identity_1.IdentitifyType.TEAMSPEAK; + } + valid() { + return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; + } + decode(data) { + return __awaiter(this, void 0, void 0, function* () { + const json = JSON.parse(data); + if (!json) + throw "invalid json"; + if (json.version == 2) { + this.private_key = json.key; + this.hash_number = json.hash; + this._name = json.name; + } + else if (json.version == 1) { + const key = json.key; + this._name = json.name; + const clone = yield TeaSpeakIdentity.import_ts(key, false); + this.private_key = clone.private_key; + this.hash_number = clone.hash_number; + } + else + throw "invalid version"; + yield this.initialize(); + }); + } + encode() { + return JSON.stringify({ + key: this.private_key, + hash: this.hash_number, + name: this._name, + version: 2 + }); + } + level() { + return __awaiter(this, void 0, void 0, function* () { + if (!this._initialized || !this.public_key) + throw "not initialized"; + const hash = new Uint8Array(yield sha_1.sha.sha1(this.public_key + this.hash_number)); + let level = 0; + while (level < hash.byteLength && hash[level] == 0) + level++; + if (level >= hash.byteLength) { + level = 256; + } + else { + let byte = hash[level]; + level <<= 3; + while ((byte & 0x1) == 0) { + level++; + byte >>= 1; + } + } + return level; + }); + } + string_add(a, b) { + const char_result = []; + const char_a = [...a].reverse().map(e => e.charCodeAt(0)); + const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + let carry = false; + while (char_b.length > 0) { + let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; + if ((carry = result > 57)) + result -= 10; + char_result.push(result); + } + while (char_a.length > 0) { + let result = char_a.pop_front() + (carry ? 1 : 0); + if ((carry = result > 57)) + result -= 10; + char_result.push(result); + } + if (carry) + char_result.push(49); + return String.fromCharCode.apply(null, char_result.slice().reverse()); + } + improve_level_for(time, threads) { + return __awaiter(this, void 0, void 0, function* () { + let active = true; + setTimeout(() => active = false, time); + return yield this.improve_level(-1, threads, () => active); + }); + } + improve_level(target, threads, active_callback, callback_level, callback_status) { + return __awaiter(this, void 0, void 0, function* () { + if (!this._initialized || !this.public_key) + throw "not initialized"; + if (target == -1) + target = 0; + else if (target <= (yield this.level())) + return true; + const workers = []; + const iterations = 100000; + let current_hash; + const next_hash = () => { + if (!current_hash) + return (current_hash = this.hash_number); + if (current_hash.length < iterations.toString().length) { + current_hash = this.string_add(iterations.toString(), current_hash); + } + else { + current_hash = this.string_add(current_hash, iterations.toString()); + } + return current_hash; + }; + { + const initialize_promise = []; + for (let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } + try { + yield Promise.all(initialize_promise); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } + } + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : (yield this.level()) + 1; + const worker_promise = []; + const hash_timestamps = []; + let last_hashrate_update = 0; + const update_hashrate = () => { + if (!callback_status) + return; + const now = Date.now(); + hash_timestamps.push(now); + if (last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + const timeout = now - 10 * 1000; + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))); + } + }; + try { + result = yield new Promise((resolve, reject) => { + let active = true; + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; + }; + for (const worker of workers) { + const worker_mine = () => { + if (!active) + return; + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + worker_promise.remove(p); + if (result.valueOf()) { + if (worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + log_1.log.info(log_1.LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if (callback_level) + callback_level(best_level); + } + if (active) { + if (target > 0) + exit(); + else + target_level = best_level + 1; + } + } + if (active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + log_1.log.warn(log_1.LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + return Promise.resolve(); + }); + worker_promise.push(p); + }; + worker_mine(); + } + }); + } + catch (error) { + } + { + const finalize_promise = []; + for (const worker of workers) + finalize_promise.push(worker.finalize(250)); + try { + yield Promise.all(finalize_promise); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, error); + throw "failed to finalize"; + } + } + return result; + }); + } + initialize() { + return __awaiter(this, void 0, void 0, function* () { + if (!this.private_key) + throw "Invalid private key"; + let jwk; + try { + jwk = yield CryptoHelper.decode_tomcrypt_key(this.private_key); + if (!jwk) + throw "result undefined"; + } + catch (error) { + throw "failed to parse key (" + error + ")"; + } + try { + this._crypto_key_sign = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ["sign"]); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, error); + throw "failed to create crypto sign key"; + } + try { + this._crypto_key = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, error); + throw "failed to create crypto key"; + } + try { + this.public_key = yield CryptoHelper.export_ecc_key(this._crypto_key, true); + this._unique_id = main_1.base64_encode_ab(yield sha_1.sha.sha1(this.public_key)); + } + catch (error) { + log_1.log.error(log_1.LogCategory.IDENTITIES, error); + throw "failed to calculate unique id"; + } + this._initialized = true; + }); + } + export_ts(ini) { + return __awaiter(this, void 0, void 0, function* () { + if (!this.private_key) + throw "Invalid private key"; + const identity = this.hash_number + "V" + (yield CryptoHelper.encrypt_ts_identity(new Uint8Array(main_1.str2ab8(this.private_key)))); + if (!ini) + return identity; + return "[Identity]\n" + + "id=TeaWeb-Exported\n" + + "identity=\"" + identity + "\"\n" + + "nickname=\"" + this.fallback_name() + "\"\n" + + "phonetic_nickname="; + }); + } + sign_message(message, hash = "SHA-256") { + return __awaiter(this, void 0, void 0, function* () { + const sign_buffer = yield crypto.subtle.sign({ + name: "ECDSA", + hash: hash + }, this._crypto_key_sign, main_1.str2ab8(message)); + const sign = new Uint8Array(sign_buffer); + const buffer = new Uint8Array(72); + let index = 0; + { + buffer[index++] = 0x30; + buffer[index++] = 0x00; + } + { + buffer[index++] = 0x02; + buffer[index++] = 0x20; + if (sign[0] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = sign[i]; + } + { + buffer[index++] = 0x02; + buffer[index++] = 0x20; + if (sign[32] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = sign[32 + i]; + } + buffer[1] = index - 2; + return main_1.base64_encode_ab(buffer.subarray(0, index)); + }); + } + spawn_identity_handshake_handler(connection) { + return new TeaSpeakHandshakeHandler(connection, this); + } +} +exports.TeaSpeakIdentity = TeaSpeakIdentity; + + +/***/ }), + +/***/ "./shared/js/profiles/identities/teaspeak-forum.ts": +/*!*********************************************************!*\ + !*** ./shared/js/profiles/identities/teaspeak-forum.ts ***! + \*********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const settings_1 = __webpack_require__(/*! ../../settings */ "./shared/js/settings.ts"); +const TeaForumIdentity_1 = __webpack_require__(/*! ./TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); +var forum; +(function (forum) { + let gcaptcha; + (function (gcaptcha) { + function initialize() { + return __awaiter(this, void 0, void 0, function* () { + if (typeof (window.grecaptcha) === "undefined") { + let script = document.createElement("script"); + script.async = true; + let timeout; + const callback_name = "captcha_callback_" + Math.random().toString().replace(".", ""); + try { + yield new Promise((resolve, reject) => { + script.onerror = reject; + window[callback_name] = resolve; + script.src = "https://www.google.com/recaptcha/api.js?onload=" + encodeURIComponent(callback_name) + "&render=explicit"; + document.body.append(script); + timeout = setTimeout(() => reject("timeout"), 15000); + }); + } + catch (error) { + script.remove(); + script = undefined; + console.error(tr("Failed to fetch recaptcha javascript source: %o"), error); + throw tr("failed to download source"); + } + finally { + if (script) + script.onerror = undefined; + delete window[callback_name]; + clearTimeout(timeout); + } + } + if (typeof (window.grecaptcha) === "undefined") + throw tr("failed to load recaptcha"); + }); + } + gcaptcha.initialize = initialize; + function spawn(container, key, callback_data) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield initialize(); + } + catch (error) { + console.error(tr("Failed to initialize G-Recaptcha. Error: %o"), error); + throw tr("initialisation failed"); + } + if (container.attr("captcha-uuid")) + window.grecaptcha.reset(container.attr("captcha-uuid")); + else { + container.attr("captcha-uuid", window.grecaptcha.render(container[0], { + "sitekey": key, + callback: callback_data + })); + } + }); + } + gcaptcha.spawn = spawn; + })(gcaptcha = forum.gcaptcha || (forum.gcaptcha = {})); + function api_url() { + return settings_1.settings.static_global(settings_1.Settings.KEY_TEAFORO_URL); + } + class Data { + constructor(auth, raw, sign) { + this.auth_key = auth; + this.raw = raw; + this.sign = sign; + this.parsed = JSON.parse(raw); + } + data_json() { return this.raw; } + data_sign() { return this.sign; } + name() { return this.parsed.user_name; } + user_id() { return this.parsed.user_id; } + user_group() { return this.parsed.user_group_id; } + is_stuff() { return this.parsed.is_staff; } + is_premium() { return this.parsed.user_groups.indexOf(5) != -1; } + data_age() { return new Date(this.parsed.data_age); } + is_expired() { return this.parsed.data_age + 48 * 60 * 60 * 1000 < Date.now(); } + should_renew() { return this.parsed.data_age + 24 * 60 * 60 * 1000 < Date.now(); } + } + forum.Data = Data; + let _data; + function logged_in() { + return !!_data && !_data.is_expired(); + } + forum.logged_in = logged_in; + function data() { return _data; } + forum.data = data; + function login(username, password, captcha) { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/login", + type: "POST", + cache: false, + data: { + username: username, + password: password, + remember: true, + "g-recaptcha-response": captcha + }, + crossDomain: true, + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Login request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }); + }); + } + catch (error) { + return { + status: "error", + error_message: tr("failed to send login request") + }; + } + if (response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + return { + status: "error", + error_message: (response["errors"] || [])[0] || tr("Unknown error") + }; + } + if (!response["success"]) { + console.error(tr("Login failed. Response %o"), response); + let message = tr("failed to login"); + let captcha; + if (response["code"] == 1 || response["code"] == 3) + message = tr("Invalid username or password"); + if (response["code"] == 2 || response["code"] == 3) { + captcha = { + type: response["captcha"]["type"], + data: response["captcha"]["siteKey"] + }; + if (response["code"] == 2) + message = tr("captcha required"); + } + return { + status: typeof (captcha) !== "undefined" ? "captcha" : "error", + error_message: message, + captcha: captcha + }; + } + try { + _data = new Data(response["auth-key"], response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); + TeaForumIdentity_1.update_forum(); + } + catch (error) { + console.error(tr("Failed to parse forum given data: %o"), error); + return { + status: "error", + error_message: tr("Failed to parse response data") + }; + } + return { + status: "success" + }; + }); + } + forum.login = login; + function renew_data() { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/renew-data", + type: "GET", + cache: false, + crossDomain: true, + data: { + "auth-key": _data.auth_key + }, + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Renew request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }); + }); + } + catch (error) { + throw tr("failed to send renew request"); + } + if (response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + throw (response["errors"] || [])[0] || tr("Unknown error"); + } + if (!response["success"]) { + if (response["code"] == 1) { + return "login-required"; + } + throw "invalid error code (" + response["code"] + ")"; + } + if (!response["data"] || !response["sign"]) + throw tr("response missing data"); + console.debug(tr("Renew succeeded. Parsing data.")); + try { + _data = new Data(_data.auth_key, response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + TeaForumIdentity_1.update_forum(); + } + catch (error) { + console.error(tr("Failed to parse forum given data: %o"), error); + throw tr("failed to parse data"); + } + return "success"; + }); + } + forum.renew_data = renew_data; + function logout() { + return __awaiter(this, void 0, void 0, function* () { + if (!logged_in()) + return; + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/logout", + type: "GET", + cache: false, + crossDomain: true, + data: { + "auth-key": _data.auth_key + }, + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Logout request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }); + }); + } + catch (error) { + throw tr("failed to send logout request"); + } + if (response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + throw (response["errors"] || [])[0] || tr("Unknown error"); + } + if (!response["success"]) { + if (response["code"] != 1) { + throw "invalid error code (" + response["code"] + ")"; + } + } + _data = undefined; + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + TeaForumIdentity_1.update_forum(); + }); + } + forum.logout = logout; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "TeaForo initialize", + priority: 10, + function: () => __awaiter(this, void 0, void 0, function* () { + const raw_data = localStorage.getItem("teaspeak-forum-data"); + const raw_sign = localStorage.getItem("teaspeak-forum-sign"); + const forum_auth = localStorage.getItem("teaspeak-forum-auth"); + if (!raw_data || !raw_sign || !forum_auth) { + console.log(tr("No TeaForo authentification found. TeaForo connection status: unconnected")); + return; + } + try { + _data = new Data(forum_auth, raw_data, raw_sign); + } + catch (error) { + console.error(tr("Failed to initialize TeaForo connection from local data. Error: %o"), error); + return; + } + if (_data.should_renew()) { + console.info(tr("TeaForo data should be renewed. Executing renew.")); + renew_data().then(status => { + if (status === "success") { + console.info(tr("TeaForo data has been successfully renewed.")); + } + else { + console.warn(tr("Failed to renew TeaForo data. New login required.")); + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + } + }).catch(error => { + console.warn(tr("Failed to renew TeaForo data. An error occurred: %o"), error); + }); + return; + } + if (_data && _data.is_expired()) { + console.error(tr("TeaForo data is expired. TeaForo connection isn't available!")); + } + }) + }); +})(forum = exports.forum || (exports.forum = {})); + + +/***/ }), + +/***/ "./shared/js/settings.ts": +/*!*******************************!*\ + !*** ./shared/js/settings.ts ***! + \*******************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const log_1 = __webpack_require__(/*! ./log */ "./shared/js/log.ts"); +const modal_1 = __webpack_require__(/*! ./ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); +if (typeof (customElements) !== "undefined") { + try { + class X_Properties extends HTMLElement { + } + class X_Property extends HTMLElement { + } + customElements.define('x-properties', X_Properties, { extends: 'div' }); + customElements.define('x-property', X_Property, { extends: 'div' }); + } + catch (error) { + console.warn("failed to define costum elements"); + } +} +class SettingsBase { + static transformStO(input, _default, default_type) { + default_type = default_type || typeof _default; + if (typeof input === "undefined") + return _default; + if (default_type === "string") + return input; + else if (default_type === "number") + return parseInt(input); + else if (default_type === "boolean") + return (input == "1" || input == "true"); + else if (default_type === "undefined") + return input; + return JSON.parse(input); + } + static transformOtS(input) { + if (typeof input === "string") + return input; + else if (typeof input === "number") + return input.toString(); + else if (typeof input === "boolean") + return input ? "1" : "0"; + else if (typeof input === "undefined") + return undefined; + return JSON.stringify(input); + } + static resolveKey(key, _default, resolver, default_type) { + let value = resolver(key.key); + if (!value) { + for (const fallback of key.fallback_keys || []) { + value = resolver(fallback); + if (typeof (value) === "string") { + const importer = (key.fallback_imports || {})[fallback]; + if (importer) + return importer(value); + break; + } + } + } + if (typeof (value) !== 'string') + return _default; + return SettingsBase.transformStO(value, _default, default_type); + } + static keyify(key) { + if (typeof (key) === "string") + return { key: key }; + if (typeof (key) === "object" && key.key) + return key; + throw "key is not a key"; + } +} +exports.SettingsBase = SettingsBase; +SettingsBase.UPDATE_DIRECT = true; +class StaticSettings extends SettingsBase { + constructor(_reserved = undefined) { + super(); + if (_reserved && !StaticSettings._instance) { + this._staticPropsTag = $("#properties"); + this.initializeStatic(); + } + else { + this._handle = StaticSettings.instance; + } + } + static get instance() { + if (!this._instance) + this._instance = new StaticSettings(true); + return this._instance; + } + initializeStatic() { + let search; + if (window.opener && window.opener !== window) { + search = new URL(window.location.href).search; + } + else { + search = location.search; + } + search.substr(1).split("&").forEach(part => { + let item = part.split("="); + $("") + .attr("key", item[0]) + .attr("value", item[1]) + .appendTo(this._staticPropsTag); + }); + } + static(key, _default, default_type) { + if (this._handle) + return this._handle.static(key, _default, default_type); + key = StaticSettings.keyify(key); + return StaticSettings.resolveKey(key, _default, key => { + let result = this._staticPropsTag.find("[key='" + key + "']"); + if (result.length > 0) + return decodeURIComponent(result.last().attr('value')); + return false; + }, default_type); + } + deleteStatic(key) { + if (this._handle) { + this._handle.deleteStatic(key); + return; + } + key = StaticSettings.keyify(key); + let result = this._staticPropsTag.find("[key='" + key.key + "']"); + if (result.length != 0) + result.detach(); + } +} +exports.StaticSettings = StaticSettings; +class Settings extends StaticSettings { + constructor() { + super(); + this.cacheGlobal = {}; + this.updated = false; + const json = localStorage.getItem("settings.global"); + try { + this.cacheGlobal = JSON.parse(json); + } + catch (error) { + log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to load global settings!\nJson: %s\nError: %o"), json, error); + const show_popup = () => { + modal_1.createErrorModal(tr("Failed to load global settings"), tr("Failed to load global client settings!\nLookup console for more information.")).open(); + }; + if (!loader.finished()) + loader.register_task(loader.Stage.LOADED, { + priority: 0, + name: "Settings error", + function: () => __awaiter(this, void 0, void 0, function* () { return show_popup(); }) + }); + else + show_popup(); + } + if (!this.cacheGlobal) + this.cacheGlobal = {}; + this.saveWorker = setInterval(() => { + if (this.updated) + this.save(); + }, 5 * 1000); + } + static initialize() { + exports.settings = new Settings(); + } + static_global(key, _default) { + const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; + const default_object = { seed: Math.random() }; + let _static = this.static(key, default_object, typeof _default); + if (_static !== default_object) + return StaticSettings.transformStO(_static, actual_default); + return this.global(key, actual_default); + } + global(key, _default) { + const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; + return StaticSettings.resolveKey(Settings.keyify(key), actual_default, key => this.cacheGlobal[key]); + } + changeGlobal(key, value) { + key = Settings.keyify(key); + if (this.cacheGlobal[key.key] == value) + return; + this.updated = true; + this.cacheGlobal[key.key] = StaticSettings.transformOtS(value); + if (Settings.UPDATE_DIRECT) + this.save(); + } + save() { + this.updated = false; + let global = JSON.stringify(this.cacheGlobal); + localStorage.setItem("settings.global", global); + if (localStorage.save) + localStorage.save(); + } +} +exports.Settings = Settings; +Settings.KEY_USER_IS_NEW = { + key: 'user_is_new_user', + default_value: true +}; +Settings.KEY_DISABLE_COSMETIC_SLOWDOWN = { + key: 'disable_cosmetic_slowdown', + description: 'Disable the cosmetic slowdows in some processes, like icon upload.' +}; +Settings.KEY_DISABLE_CONTEXT_MENU = { + key: 'disableContextMenu', + description: 'Disable the context menu for the channel tree which allows to debug the DOM easier' +}; +Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU = { + key: 'disableGlobalContextMenu', + description: 'Disable the general context menu prevention', + default_value: false +}; +Settings.KEY_DISABLE_UNLOAD_DIALOG = { + key: 'disableUnloadDialog', + description: 'Disables the unload popup on side closing' +}; +Settings.KEY_DISABLE_VOICE = { + key: 'disableVoice', + description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore' +}; +Settings.KEY_DISABLE_MULTI_SESSION = { + key: 'disableMultiSession', + default_value: false, + require_restart: true +}; +Settings.KEY_LOAD_DUMMY_ERROR = { + key: 'dummy_load_error', + description: 'Triggers a loading error at the end of the loading process.' +}; +Settings.KEY_CONTROL_MUTE_INPUT = { + key: 'mute_input' +}; +Settings.KEY_CONTROL_MUTE_OUTPUT = { + key: 'mute_output' +}; +Settings.KEY_CONTROL_SHOW_QUERIES = { + key: 'show_server_queries' +}; +Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL = { + key: 'channel_subscribe_all' +}; +Settings.KEY_FLAG_CONNECT_DEFAULT = { + key: 'connect_default' +}; +Settings.KEY_CONNECT_ADDRESS = { + key: 'connect_address' +}; +Settings.KEY_CONNECT_PROFILE = { + key: 'connect_profile', + default_value: 'default' +}; +Settings.KEY_CONNECT_USERNAME = { + key: 'connect_username' +}; +Settings.KEY_CONNECT_PASSWORD = { + key: 'connect_password' +}; +Settings.KEY_FLAG_CONNECT_PASSWORD = { + key: 'connect_password_hashed' +}; +Settings.KEY_CONNECT_HISTORY = { + key: 'connect_history' +}; +Settings.KEY_CONNECT_NO_DNSPROXY = { + key: 'connect_no_dnsproxy', + default_value: false +}; +Settings.KEY_CERTIFICATE_CALLBACK = { + key: 'certificate_callback' +}; +Settings.KEY_SOUND_MASTER = { + key: 'audio_master_volume', + default_value: 100 +}; +Settings.KEY_SOUND_MASTER_SOUNDS = { + key: 'audio_master_volume_sounds', + default_value: 100 +}; +Settings.KEY_CHAT_FIXED_TIMESTAMPS = { + key: 'chat_fixed_timestamps', + default_value: false, + description: 'Enables fixed timestamps for chat messages and disabled the updating once (2 seconds ago... etc)' +}; +Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS = { + key: 'chat_colloquial_timestamps', + default_value: true, + description: 'Enabled colloquial timestamp formatting like "Yesterday at ..." or "Today at ..."' +}; +Settings.KEY_CHAT_COLORED_EMOJIES = { + key: 'chat_colored_emojies', + default_value: true, + description: 'Enables colored emojies powered by Twemoji' +}; +Settings.KEY_CHAT_TAG_URLS = { + key: 'chat_tag_urls', + default_value: true, + description: 'Automatically link urls with [url]' +}; +Settings.KEY_CHAT_ENABLE_MARKDOWN = { + key: 'chat_enable_markdown', + default_value: true, + description: 'Enabled markdown chat support.' +}; +Settings.KEY_CHAT_ENABLE_BBCODE = { + key: 'chat_enable_bbcode', + default_value: false, + description: 'Enabled bbcode support in chat.' +}; +Settings.KEY_CHAT_IMAGE_WHITELIST_REGEX = { + key: 'chat_image_whitelist_regex', + default_value: JSON.stringify([]) +}; +Settings.KEY_SWITCH_INSTANT_CHAT = { + key: 'switch_instant_chat', + default_value: true, + description: 'Directly switch to channel chat on channel select' +}; +Settings.KEY_SWITCH_INSTANT_CLIENT = { + key: 'switch_instant_client', + default_value: true, + description: 'Directly switch to client info on client select' +}; +Settings.KEY_HOSTBANNER_BACKGROUND = { + key: 'hostbanner_background', + default_value: false, + description: 'Enables a default background begind the hostbanner' +}; +Settings.KEY_CHANNEL_EDIT_ADVANCED = { + key: 'channel_edit_advanced', + default_value: false, + description: 'Edit channels in advanced mode with a lot more settings' +}; +Settings.KEY_PERMISSIONS_SHOW_ALL = { + key: 'permissions_show_all', + default_value: false, + description: 'Show all permissions even thou they dont make sense for the server/channel group' +}; +Settings.KEY_TEAFORO_URL = { + key: "teaforo_url", + default_value: "https://forum.teaspeak.de/" +}; +Settings.KEY_FONT_SIZE = { + key: "font_size" +}; +Settings.KEY_ICON_SIZE = { + key: "icon_size", + default_value: 100 +}; +Settings.KEY_LAST_INVITE_LINK_TYPE = { + key: "last_invite_link_type", + default_value: "tea-web" +}; +Settings.FN_INVITE_LINK_SETTING = name => { + return { + key: 'invite_link_setting_' + name + }; +}; +Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE = channel => { + return { + key: 'channel_subscribe_mode_' + channel + }; +}; +Settings.FN_PROFILE_RECORD = name => { + return { + key: 'profile_record' + name + }; +}; +Settings.KEYS = (() => { + const result = []; + for (const key in Settings) { + if (!key.toUpperCase().startsWith("KEY_")) + continue; + if (key.toUpperCase() == "KEYS") + continue; + result.push(key); + } + return result; +})(); +class ServerSettings extends SettingsBase { + constructor() { + super(); + this.cacheServer = {}; + this._server_settings_updated = false; + this._destroyed = false; + this._server_save_worker = setInterval(() => { + if (this._server_settings_updated) + this.save(); + }, 5 * 1000); + } + destroy() { + this._destroyed = true; + this._server_unique_id = undefined; + this.cacheServer = undefined; + clearInterval(this._server_save_worker); + this._server_save_worker = undefined; + } + server(key, _default) { + if (this._destroyed) + throw "destroyed"; + return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]); + } + changeServer(key, value) { + if (this._destroyed) + throw "destroyed"; + key = Settings.keyify(key); + if (this.cacheServer[key.key] == value) + return; + this._server_settings_updated = true; + this.cacheServer[key.key] = StaticSettings.transformOtS(value); + if (Settings.UPDATE_DIRECT) + this.save(); + } + setServer(server_unique_id) { + if (this._destroyed) + throw "destroyed"; + if (this._server_unique_id) { + this.save(); + this.cacheServer = {}; + this._server_unique_id = undefined; + } + this._server_unique_id = server_unique_id; + if (this._server_unique_id) { + const json = localStorage.getItem("settings.server_" + server_unique_id); + try { + this.cacheServer = JSON.parse(json); + } + catch (error) { + log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to load server settings for server %s!\nJson: %s\nError: %o"), server_unique_id, json, error); + } + if (!this.cacheServer) + this.cacheServer = {}; + } + } + save() { + if (this._destroyed) + throw "destroyed"; + this._server_settings_updated = false; + if (this._server_unique_id) { + let server = JSON.stringify(this.cacheServer); + localStorage.setItem("settings.server_" + this._server_unique_id, server); + if (localStorage.save) + localStorage.save(); + } + } +} +exports.ServerSettings = ServerSettings; + + +/***/ }), + +/***/ "./shared/js/ui/elements/modal.ts": +/*!****************************************!*\ + !*** ./shared/js/ui/elements/modal.ts ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const PPTListener_1 = __webpack_require__(/*! ../../PPTListener */ "./shared/js/PPTListener.ts"); +var ElementType; +(function (ElementType) { + ElementType[ElementType["HEADER"] = 0] = "HEADER"; + ElementType[ElementType["BODY"] = 1] = "BODY"; + ElementType[ElementType["FOOTER"] = 2] = "FOOTER"; +})(ElementType = exports.ElementType || (exports.ElementType = {})); +exports.ModalFunctions = { + divify: function (val) { + if (val.length > 1) + return $.spawn("div").append(val); + return val; + }, + jqueriefy: function (val, type) { + if (typeof (val) === "function") + val = val(); + if (val instanceof jQuery) + return val; + if (Array.isArray(val)) { + if (val.length == 0) + return undefined; + return val.map(e => this.jqueriefy(e)); + } + switch (typeof val) { + case "string": + if (type == ElementType.HEADER) + return $.spawn("div").addClass("modal-title").text(val); + return $("
" + val + "
"); + case "object": return val; + case "undefined": + return undefined; + default: + console.error(("Invalid type %o"), typeof val); + return $(); + } + }, + warpProperties(data) { + if (data instanceof ModalProperties) { + return data; + } + else { + const props = new ModalProperties(); + for (const key of Object.keys(data)) + props[key] = data[key]; + return props; + } + } +}; +class ModalProperties { + constructor() { + this.header = () => "HEADER"; + this.body = () => "BODY"; + this.footer = () => "FOOTER"; + this.closeListener = () => { }; + this.height = "auto"; + this.closeable = true; + this.template_properties = {}; + this.trigger_tab = true; + this.full_size = false; + } + registerCloseListener(listener) { + if (this.closeListener) { + if ($.isArray(this.closeListener)) + this.closeListener.push(listener); + else + this.closeListener = [this.closeListener, listener]; + } + else + this.closeListener = listener; + return this; + } + triggerClose() { + if ($.isArray(this.closeListener)) + for (let listener of this.closeListener) + listener(); + else + this.closeListener(); + } +} +exports.ModalProperties = ModalProperties; +var modal; +(function (modal) { + function initialize_modals() { + register_global_events(); + } + modal.initialize_modals = initialize_modals; + const scrollSize = 18; + function scroll_bar_clicked(event) { + const x = event.pageX, y = event.pageY, e = $(event.target); + if (e.hasScrollBar("height")) { + const top = e.offset().top; + const right = e.offset().left + e.width(); + const bottom = top + e.height(); + const left = right - scrollSize; + if ((y >= top && y <= bottom) && (x >= left && x <= right)) + return true; + } + if (e.hasScrollBar("width")) { + const bottom = e.offset().top + e.height(); + const top = bottom - scrollSize; + const left = e.offset().left; + const right = left + e.width(); + if ((y >= top && y <= bottom) && (x >= left && x <= right)) + return true; + } + return false; + } + function register_global_events() { + $(document).on('mousedown', (event) => { + if (_global_modal_count == 0 || typeof (event.pageX) === "undefined" || typeof (event.pageY) === "undefined") + return; + let element = event.target; + const original = element; + do { + if (element.classList.contains('modal-content')) + break; + if (!element.classList.contains('modal')) + continue; + if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) + break; + if (element === original && scroll_bar_clicked(event)) { + _global_modal_last_time = Date.now(); + break; + } + $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); + break; + } while ((element = element.parentElement)); + }); + $(document).on('keyup', (event) => { + if (_global_modal_count == 0 || typeof (event.target) === "undefined") + return; + if (event.key !== "Escape") + return; + let element = event.target; + if (element.nodeName == "HTMLInputElement" || element.nodeName == "HTMLSelectElement" || element.nodeName == "HTMLTextAreaElement") + return; + do { + if (element.classList.contains('modal-content')) + break; + if (!element.classList.contains('modal')) + continue; + if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) + break; + $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); + break; + } while ((element = element.parentElement)); + }); + } +})(modal = exports.modal || (exports.modal = {})); +modal.initialize_modals(); +let _global_modal_count = 0; +let _global_modal_last; +let _global_modal_last_time; +class Modal { + constructor(props) { + this.open_listener = []; + this.close_listener = []; + this.properties = props; + this.shown = false; + } + get htmlTag() { + if (!this._htmlTag) + this._create(); + return this._htmlTag; + } + _create() { + const header = exports.ModalFunctions.jqueriefy(this.properties.header, ElementType.HEADER); + const body = exports.ModalFunctions.jqueriefy(this.properties.body, ElementType.BODY); + const footer = exports.ModalFunctions.jqueriefy(this.properties.footer, ElementType.FOOTER); + const template = $(this.properties.template || "#tmpl_modal"); + const properties = { + modal_header: header, + modal_body: body, + modal_footer: footer, + closeable: this.properties.closeable, + full_size: this.properties.full_size + }; + if (this.properties.template_properties) + Object.assign(properties, this.properties.template_properties); + const tag = template.renderTag(properties); + if (typeof (this.properties.width) !== "undefined" && typeof (this.properties.min_width) !== "undefined") + tag.find(".modal-content") + .css("min-width", this.properties.min_width) + .css("width", this.properties.width); + else if (typeof (this.properties.width) !== "undefined") + tag.find(".modal-content").css("min-width", this.properties.width); + else if (typeof (this.properties.min_width) !== "undefined") + tag.find(".modal-content").css("min-width", this.properties.min_width); + this.close_elements = tag.find(".button-modal-close"); + this.close_elements.toggle(this.properties.closeable).on('click', event => { + if (this.properties.closeable) + this.close(); + }); + this._htmlTag = tag; + this._htmlTag.find("input").on('change', event => { + $(event.target).parents(".form-group").toggleClass('is-filled', !!event.target.value); + }); + this._htmlTag.on('hide.bs.modal', event => !this.properties.closeable || this.close()); + this._htmlTag.on('hidden.bs.modal', event => this._htmlTag.remove()); + } + open() { + if (this.shown) + return; + _global_modal_last_time = Date.now(); + _global_modal_last = this.htmlTag[0]; + this.shown = true; + this.htmlTag.appendTo($("body")); + _global_modal_count++; + this.htmlTag.show(); + setTimeout(() => this.htmlTag.addClass('shown'), 0); + setTimeout(() => { + for (const listener of this.open_listener) + listener(); + this.htmlTag.find(".tab").trigger('tab.resize'); + }, 300); + } + close() { + if (!this.shown) + return; + _global_modal_count--; + if (_global_modal_last === this.htmlTag[0]) + _global_modal_last = undefined; + this.shown = false; + this.htmlTag.removeClass('shown'); + setTimeout(() => { + this.htmlTag.remove(); + this._htmlTag = undefined; + }, 300); + this.properties.triggerClose(); + for (const listener of this.close_listener) + listener(); + } + set_closeable(flag) { + if (flag === this.properties.closeable) + return; + this.properties.closeable = flag; + this.close_elements.toggle(flag); + } +} +exports.Modal = Modal; +function createModal(data) { + return new Modal(exports.ModalFunctions.warpProperties(data)); +} +exports.createModal = createModal; +class InputModalProperties extends ModalProperties { +} +exports.InputModalProperties = InputModalProperties; +function createInputModal(headMessage, question, validator, callback, props = {}) { + props = exports.ModalFunctions.warpProperties(props); + props.template_properties || (props.template_properties = {}); + props.template_properties.field_title = props.field_title; + props.template_properties.field_label = props.field_label; + props.template_properties.field_placeholder = props.field_placeholder; + props.template_properties.error_message = props.error_message; + props.template = "#tmpl_modal_input"; + props.header = headMessage; + props.template_properties.question = exports.ModalFunctions.jqueriefy(question); + const modal = createModal(props); + const input = modal.htmlTag.find(".container-value input"); + const button_cancel = modal.htmlTag.find(".button-cancel"); + const button_submit = modal.htmlTag.find(".button-submit"); + let submited = false; + input.on('keyup change', event => { + const str = input.val(); + const valid = str !== undefined && validator(str); + input.attr("pattern", valid ? null : "^[a]{1000}$").toggleClass("is-invalid", !valid); + button_submit.prop("disabled", !valid); + }); + input.on('keydown', event => { + if (event.keyCode !== PPTListener_1.KeyCode.KEY_RETURN || event.shiftKey) + return; + if (button_submit.prop("disabled")) + return; + button_submit.trigger('click'); + }); + button_submit.on('click', event => { + if (!submited) { + submited = true; + const str = input.val(); + if (str !== undefined && validator(str)) + callback(str); + else + callback(false); + } + modal.close(); + }).prop("disabled", !validator("")); + button_cancel.on('click', event => { + if (!submited) { + submited = true; + callback(false); + } + modal.close(); + }); + modal.open_listener.push(() => input.focus()); + modal.close_listener.push(() => button_cancel.trigger('click')); + return modal; +} +exports.createInputModal = createInputModal; +function createErrorModal(header, message, props = { footer: undefined }) { + props = exports.ModalFunctions.warpProperties(props); + (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; + props.header = header; + props.body = message; + const modal = createModal(props); + modal.htmlTag.find(".modal-body").addClass("modal-error"); + return modal; +} +exports.createErrorModal = createErrorModal; +function createInfoModal(header, message, props = { footer: undefined }) { + props = exports.ModalFunctions.warpProperties(props); + (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; + props.header = header; + props.body = message; + const modal = createModal(props); + modal.htmlTag.find(".modal-body").addClass("modal-info"); + return modal; +} +exports.createInfoModal = createInfoModal; + + +/***/ }), + +/***/ "./shared/js/ui/frames/chat.ts": +/*!*************************************!*\ + !*** ./shared/js/ui/frames/chat.ts ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); +var ChatType; +(function (ChatType) { + ChatType[ChatType["GENERAL"] = 0] = "GENERAL"; + ChatType[ChatType["SERVER"] = 1] = "SERVER"; + ChatType[ChatType["CHANNEL"] = 2] = "CHANNEL"; + ChatType[ChatType["CLIENT"] = 3] = "CLIENT"; +})(ChatType = exports.ChatType || (exports.ChatType = {})); +var MessageHelper; +(function (MessageHelper) { + function htmlEscape(message) { + const div = document.createElement('div'); + div.innerText = message; + message = div.innerHTML; + return message.replace(/ /g, ' ').split(/
/); + } + MessageHelper.htmlEscape = htmlEscape; + function formatElement(object, escape_html = true) { + if ($.isArray(object)) { + let result = []; + for (let element of object) + result.push(...formatElement(element, escape_html)); + return result; + } + else if (typeof (object) == "string") { + if (object.length == 0) + return []; + return escape_html ? + htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)) : + [$.spawn("div").css("display", "inline-block").html(object)]; + } + else if (typeof (object) === "object") { + if (object instanceof $) + return [object]; + return formatElement(""); + } + else if (typeof (object) === "function") + return formatElement(object(), escape_html); + else if (typeof (object) === "undefined") + return formatElement(""); + else if (typeof (object) === "number") + return [$.spawn("a").text(object)]; + return formatElement(""); + } + MessageHelper.formatElement = formatElement; + function formatMessage(pattern, ...objects) { + let begin = 0, found = 0; + let result = []; + do { + found = pattern.indexOf('{', found); + if (found == -1 || pattern.length <= found + 1) { + result.push(...formatElement(pattern.substr(begin))); + break; + } + if (found > 0 && pattern[found - 1] == '\\') { + found++; + continue; + } + result.push(...formatElement(pattern.substr(begin, found - begin))); + let offset = 0; + if (pattern[found + 1] == ':') { + offset++; + while (pattern[found + 1 + offset] != ':' && found + 1 + offset < pattern.length) + offset++; + const tag = pattern.substr(found + 2, offset - 1); + offset++; + if (pattern[found + offset + 1] != '}' && found + 1 + offset < pattern.length) { + found++; + continue; + } + result.push($.spawn(tag)); + } + else { + let number; + while ("0123456789".includes(pattern[found + 1 + offset])) + offset++; + number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0"); + if (pattern[found + offset + 1] != '}') { + found++; + continue; + } + if (objects.length < number) + log_1.log.warn(log_1.LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number); + result.push(...formatElement(objects[number])); + } + found = found + 1 + offset; + begin = found + 1; + } while (found++); + return result; + } + MessageHelper.formatMessage = formatMessage; + function bbcode_chat(message) { + return messages.formatter.bbcode.format(message, { + is_chat_message: true + }); + } + MessageHelper.bbcode_chat = bbcode_chat; + let network; + (function (network) { + network.KB = 1024; + network.MB = 1024 * network.KB; + network.GB = 1024 * network.MB; + network.TB = 1024 * network.GB; + function format_bytes(value, options) { + options = options || {}; + if (typeof options.exact !== "boolean") + options.exact = true; + if (typeof options.unit !== "string") + options.unit = "Bytes"; + let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + let v, unit; + if (value > 2 * network.TB) { + unit = "TB"; + v = value / network.TB; + } + else if (value > network.GB) { + unit = "GB"; + v = value / network.GB; + } + else if (value > network.MB) { + unit = "MB"; + v = value / network.MB; + } + else if (value > network.KB) { + unit = "KB"; + v = value / network.KB; + } + else { + unit = ""; + v = value; + } + let result = ""; + if (options.exact || !unit) { + result += points; + if (options.unit) { + result += " " + options.unit; + if (options.time) + result += "/" + options.time; + } + } + if (unit) { + result += (result ? " / " : "") + v.toFixed(2) + " " + unit; + if (options.time) + result += "/" + options.time; + } + return result; + } + network.format_bytes = format_bytes; + })(network = MessageHelper.network || (MessageHelper.network = {})); + MessageHelper.K = 1000; + MessageHelper.M = 1000 * MessageHelper.K; + MessageHelper.G = 1000 * MessageHelper.M; + MessageHelper.T = 1000 * MessageHelper.G; + function format_number(value, options) { + options = Object.assign(options || {}, {}); + let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + let v, unit; + if (value > 2 * MessageHelper.T) { + unit = "T"; + v = value / MessageHelper.T; + } + else if (value > MessageHelper.G) { + unit = "G"; + v = value / MessageHelper.G; + } + else if (value > MessageHelper.M) { + unit = "M"; + v = value / MessageHelper.M; + } + else if (value > MessageHelper.K) { + unit = "K"; + v = value / MessageHelper.K; + } + else { + unit = ""; + v = value; + } + if (unit && options.time) + unit = unit + "/" + options.time; + return points + " " + (options.unit || "") + (unit ? (" / " + v.toFixed(2) + " " + unit) : ""); + } + MessageHelper.format_number = format_number; + MessageHelper.TIME_SECOND = 1000; + MessageHelper.TIME_MINUTE = 60 * MessageHelper.TIME_SECOND; + MessageHelper.TIME_HOUR = 60 * MessageHelper.TIME_MINUTE; + MessageHelper.TIME_DAY = 24 * MessageHelper.TIME_HOUR; + MessageHelper.TIME_WEEK = 7 * MessageHelper.TIME_DAY; + function format_time(time, default_value) { + let result = ""; + if (time > MessageHelper.TIME_WEEK) { + const amount = Math.floor(time / MessageHelper.TIME_WEEK); + result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); + time -= amount * MessageHelper.TIME_WEEK; + } + if (time > MessageHelper.TIME_DAY) { + const amount = Math.floor(time / MessageHelper.TIME_DAY); + result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); + time -= amount * MessageHelper.TIME_DAY; + } + if (time > MessageHelper.TIME_HOUR) { + const amount = Math.floor(time / MessageHelper.TIME_HOUR); + result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); + time -= amount * MessageHelper.TIME_HOUR; + } + if (time > MessageHelper.TIME_MINUTE) { + const amount = Math.floor(time / MessageHelper.TIME_MINUTE); + result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); + time -= amount * MessageHelper.TIME_MINUTE; + } + if (time > MessageHelper.TIME_SECOND) { + const amount = Math.floor(time / MessageHelper.TIME_SECOND); + result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); + time -= amount * MessageHelper.TIME_SECOND; + } + return result.length > 0 ? result.substring(1) : default_value; + } + MessageHelper.format_time = format_time; + let _icon_size_style; + function set_icon_size(size) { + if (!_icon_size_style) + _icon_size_style = $.spawn("style").appendTo($("#style")); + _icon_size_style.text("\n" + + ".message > .emoji {\n" + + " height: " + size + "!important;\n" + + " width: " + size + "!important;\n" + + "}\n"); + } + MessageHelper.set_icon_size = set_icon_size; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "icon size init", + function: () => __awaiter(this, void 0, void 0, function* () { + MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em"); + }), + priority: 10 + }); +})(MessageHelper = exports.MessageHelper || (exports.MessageHelper = {})); + + +/***/ }), + +/***/ "./shared/js/ui/modal/ModalConnect.ts": +/*!********************************************!*\ + !*** ./shared/js/ui/modal/ModalConnect.ts ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var connection_log; +(function (connection_log) { + let _history = []; + function log_connect(address) { + let entry = _history.find(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port); + if (!entry) { + _history.push(entry = { + last_timestamp: Date.now(), + first_timestamp: Date.now(), + address: address, + clients_online: 0, + clients_total: 0, + country: 'unknown', + name: 'Unknown', + icon_id: 0, + total_connection: 0, + flag_password: false, + password_hash: undefined + }); + } + entry.last_timestamp = Date.now(); + entry.total_connection++; + _save(); + } + connection_log.log_connect = log_connect; + function update_address_info(address, data) { + _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { + for (const key of Object.keys(data)) { + if (typeof (data[key]) !== "undefined") { + e[key] = data[key]; + } + } + }); + _save(); + } + connection_log.update_address_info = update_address_info; + function update_address_password(address, password_hash) { + _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { + e.password_hash = password_hash; + }); + _save(); + } + connection_log.update_address_password = update_address_password; + function _save() { + settings.changeGlobal(Settings.KEY_CONNECT_HISTORY, JSON.stringify(_history)); + } + function history() { + return _history.sort((a, b) => b.last_timestamp - a.last_timestamp); + } + connection_log.history = history; + function delete_entry(address) { + _history = _history.filter(e => !(e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port)); + _save(); + } + connection_log.delete_entry = delete_entry; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: 'connection history load', + priority: 1, + function: () => __awaiter(this, void 0, void 0, function* () { + _history = []; + try { + _history = JSON.parse(settings.global(Settings.KEY_CONNECT_HISTORY, "[]")); + } + catch (error) { + log.warn(LogCategory.CLIENT, tr("Failed to load connection history: {}"), error); + } + }) + }); +})(connection_log = exports.connection_log || (exports.connection_log = {})); +var Modals; +(function (Modals) { + function spawnConnectModal(options, defaultHost = { url: "ts.TeaSpeak.de", enforce: false }, connect_profile) { + let selected_profile; + const random_id = (() => { + const array = new Uint32Array(10); + window.crypto.getRandomValues(array); + return array.join(""); + })(); + const modal = createModal({ + header: tr("Connect to a server"), + body: $("#tmpl_connect").renderTag({ + client: native_client, + forum_path: settings.static("forum_path"), + password_id: random_id, + multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), + default_connect_new_tab: typeof (options.default_connect_new_tab) === "boolean" && options.default_connect_new_tab + }), + footer: () => undefined, + min_width: "28em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-connect"); + { + const container_last_servers = modal.htmlTag.find(".container-last-servers"); + const button = modal.htmlTag.find(".button-toggle-last-servers"); + const set_show = shown => { + container_last_servers.toggleClass('shown', shown); + button.find(".arrow").toggleClass('down', shown).toggleClass('up', !shown); + settings.changeGlobal("connect_show_last_servers", shown); + }; + button.on('click', event => { + set_show(!container_last_servers.hasClass("shown")); + }); + set_show(settings.static_global("connect_show_last_servers", false)); + } + const apply = (header, body, footer) => { + const container_last_server_body = modal.htmlTag.find(".container-last-servers .table .body"); + const container_empty = container_last_server_body.find(".body-empty"); + let current_connect_data; + const button_connect = footer.find(".button-connect"); + const button_connect_tab = footer.find(".button-connect-new-tab"); + const button_manage = body.find(".button-manage-profiles"); + const input_profile = body.find(".container-select-profile select"); + const input_address = body.find(".container-address input"); + const input_nickname = body.find(".container-nickname input"); + const input_password = body.find(".container-password input"); + let updateFields = (reset_current_data) => { + if (reset_current_data) { + current_connect_data = undefined; + container_last_server_body.find(".selected").removeClass("selected"); + } + let address = input_address.val().toString(); + settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address); + let flag_address = !!address.match(Modals.Regex.IP_V4) || !!address.match(Modals.Regex.IP_V6) || !!address.match(Modals.Regex.DOMAIN); + let nickname = input_nickname.val().toString(); + if (nickname) + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname); + else + nickname = input_nickname.attr("placeholder") || ""; + let flag_nickname = nickname.length >= 3 && nickname.length <= 32; + input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address); + input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname); + const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid(); + button_connect.prop("disabled", flag_disabled); + button_connect_tab.prop("disabled", flag_disabled); + }; + input_address.val(defaultHost.enforce ? defaultHost.url : settings.static_global(Settings.KEY_CONNECT_ADDRESS, defaultHost.url)); + input_address + .on("keyup", () => updateFields(true)) + .on('keydown', event => { + if (event.keyCode == KeyCode.KEY_ENTER && !event.shiftKey) + button_connect.trigger('click'); + }); + button_manage.on('click', event => { + const modal = Modals.spawnSettingsModal("identity-profiles"); + modal.close_listener.push(() => { + input_profile.trigger('change'); + }); + return true; + }); + { + for (const profile of profiles.profiles()) { + input_profile.append($.spawn("option").text(profile.profile_name).val(profile.id)); + } + input_profile.on('change', event => { + selected_profile = profiles.find_profile(input_profile.val()) || profiles.default_profile(); + { + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined); + input_nickname + .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user") + .val(""); + } + settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, selected_profile.id); + input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid()); + updateFields(true); + }); + input_profile.val(connect_profile && connect_profile.profile ? + connect_profile.profile.id : + settings.static_global(Settings.KEY_CONNECT_PROFILE, "default")).trigger('change'); + } + const last_nickname = settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined); + if (last_nickname) + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, last_nickname); + input_nickname.val(last_nickname); + input_nickname.on("keyup", () => updateFields(true)); + setTimeout(() => updateFields(false), 100); + const server_address = () => { + let address = input_address.val().toString(); + if (address.match(Modals.Regex.IP_V6) && !address.startsWith("[")) + return "[" + address + "]"; + return address; + }; + button_connect.on('click', event => { + modal.close(); + const connection = server_connections.active_connection_handler(); + if (connection) { + connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { + nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), + password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } + }); + } + else { + button_connect_tab.trigger('click'); + } + }); + button_connect_tab.on('click', event => { + modal.close(); + const connection = server_connections.spawn_server_connection_handler(); + server_connections.set_active_connection_handler(connection); + connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { + nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), + password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } + }); + }); + { + for (const entry of connection_log.history().slice(0, 10)) { + $.spawn("div").addClass("row").append($.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { + event.preventDefault(); + const row = $(event.target).parents('.row'); + row.hide(250, () => { + row.detach(); + }); + connection_log.delete_entry(entry.address); + container_empty.toggle(container_last_server_body.children().length > 1); + })).append($.spawn("div").addClass("column name").append([ + IconManager.generate_tag(IconManager.load_cached_icon(entry.icon_id)), + $.spawn("a").text(entry.name) + ])).append($.spawn("div").addClass("column address").text(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : ""))).append($.spawn("div").addClass("column password").text(entry.flag_password ? tr("Yes") : tr("No"))).append($.spawn("div").addClass("column country-name").append([ + $.spawn("div").addClass("country flag-" + entry.country.toLowerCase()), + $.spawn("a").text(i18n.country_name(entry.country, tr("Global"))) + ])).append($.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total)).append($.spawn("div").addClass("column connections").text(entry.total_connection + "")).on('click', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + current_connect_data = entry; + container_last_server_body.find(".selected").removeClass("selected"); + $(event.target).parent('.row').addClass('selected'); + input_address.val(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")); + input_password.val(entry.flag_password && entry.password_hash ? "WolverinDEV Yeahr!" : "").trigger('change'); + }).on('dblclick', event => { + current_connect_data = entry; + button_connect.trigger('click'); + }).appendTo(container_last_server_body); + container_empty.toggle(false); + } + } + }; + apply(modal.htmlTag, modal.htmlTag, modal.htmlTag); + modal.open(); + return; + } + Modals.spawnConnectModal = spawnConnectModal; + Modals.Regex = { + DOMAIN: /^(localhost|((([a-zA-Z0-9_-]{0,63}\.){0,253})?[a-zA-Z0-9_-]{0,63}\.[a-zA-Z]{2,64}))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,46}))$/, + IP_V4: /(^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4}))$/, + IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, + IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, + }; +})(Modals = exports.Modals || (exports.Modals = {})); + + +/***/ }) + +/******/ }); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3Byb2Nlc3MvYnJvd3Nlci5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vYW1kLW9wdGlvbnMuanMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvQnJvd3NlcklQQy50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvUFBUTGlzdGVuZXIudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL2Nvbm5lY3Rpb24vQ29tbWFuZEhlbHBlci50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZS50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvY29ubmVjdGlvbi9TZXJ2ZXJDb25uZWN0aW9uRGVjbGFyYXRpb24udHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL2NyeXB0by9hc24xLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9jcnlwdG8vc2hhLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9jcnlwdG8vdWlkLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9pMThuL2xvY2FsaXplLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9tYWluLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9wcm9maWxlcy9Db25uZWN0aW9uUHJvZmlsZS50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvcHJvZmlsZXMvSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvTmFtZUlkZW50aXR5LnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9wcm9maWxlcy9pZGVudGl0aWVzL1RlYUZvcnVtSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvVGVhbVNwZWFrSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvdGVhc3BlYWstZm9ydW0udHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy91aS9lbGVtZW50cy9tb2RhbC50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvdWkvZnJhbWVzL2NoYXQudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3VpL21vZGFsL01vZGFsQ29ubmVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO1FBQUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7OztRQUdBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSwwQ0FBMEMsZ0NBQWdDO1FBQzFFO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0Esd0RBQXdELGtCQUFrQjtRQUMxRTtRQUNBLGlEQUFpRCxjQUFjO1FBQy9EOztRQUVBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQSx5Q0FBeUMsaUNBQWlDO1FBQzFFLGdIQUFnSCxtQkFBbUIsRUFBRTtRQUNySTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDJCQUEyQiwwQkFBMEIsRUFBRTtRQUN2RCxpQ0FBaUMsZUFBZTtRQUNoRDtRQUNBO1FBQ0E7O1FBRUE7UUFDQSxzREFBc0QsK0RBQStEOztRQUVySDtRQUNBOzs7UUFHQTtRQUNBOzs7Ozs7Ozs7Ozs7QUNsRkE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FBSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHVCQUF1QixzQkFBc0I7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQkFBcUI7QUFDckI7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLHFDQUFxQzs7QUFFckM7QUFDQTtBQUNBOztBQUVBLDJCQUEyQjtBQUMzQjtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsVUFBVTs7Ozs7Ozs7Ozs7O0FDdkx0QztBQUNBOzs7Ozs7Ozs7Ozs7O0FDREE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFF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LEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUNwRSxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBRWxDLEtBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFO2dCQUNyQixDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztnQkFDcEUsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2FBQ3RDO1lBRUQsS0FBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDdkIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2FBQzdCO1lBRUQsT0FBTSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2xCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsVUFBVSxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNoRCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBRTFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ2hELENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFFMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNkLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsVUFBVSxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNoRCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7YUFDN0I7WUFFRCxPQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDbEIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNoQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2FBQzdCO1lBRUQsT0FBTSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2xCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDM0MsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMvQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBRTFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQy9DLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFFMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNkLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFNBQVMsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDL0MsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMvQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7YUFDN0I7WUFFRCxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMvQixDQUFDLENBQUM7UUFFRixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsR0FBRztZQUNqQixJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFFaEIsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUV6RSxPQUFPLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUM5RCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzFELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQztnQkFDbEQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDMUQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDO2dCQUNsRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMxRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUM7Z0JBQ2xELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzFELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQztnQkFDbEQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDMUQsU0FBUyxDQUFDLENB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LEdBQUcsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzdCLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUMsSUFBTSxFQUFFLENBQUM7UUFDN0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFTLENBQUMsQ0FBQztRQUM3QixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDLElBQVcsQ0FBQyxDQUFDO1FBRzdCLE1BQU0sSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDdkU7SUFHRCxJQUFJLGFBQWEsSUFBSSxDQUFDLEVBQUU7UUFDcEIsS0FBSyxHQUFHLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUUzQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBR3ZCLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsSUFBTSxDQUFDLENBQUM7UUFFdkIsTUFBTSxJQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0tBQ2hEO1NBQU0sSUFBSSxhQUFhLElBQUksQ0FBQyxFQUFFO1FBQzNCLEtBQUssR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRTNELENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDMUIsQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFNLENBQUMsQ0FBQztRQUcxQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDLElBQVEsQ0FBQyxDQUFDO1FBRTFC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QUUsQ0FBQyxDQUFDLENBQUM7d0JBQzNHLGNBQWMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLGFBQWEsSUFBSSxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUNqSCxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLEtBQUssQ0FBQyxFQUFFO3dCQUN0QixvQkFBb0IsR0FBRyxLQUFLLENBQUM7d0JBQzdCLGNBQWMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3BDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO29CQUN4QyxlQUFlLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2lCQUNqQzthQUNKO1FBQ0wsQ0FBQyxDQUFDO1FBQ0YsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFbkQsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2IsT0FBTztJQUNYLENBQUM7SUFsT2Usd0JBQWlCLG9CQWtPaEM7SUFFWSxZQUFLLEdBQUc7UUFFakIsTUFBTSxFQUFFLG9LQUFvSztRQUU1SyxLQUFLLEVBQUUsc0tBQXNLO1FBQzdLLEtBQUssRUFBRSxxcEJBQXFwQjtRQUM1cEIsRUFBRSxFQUFFLG13Q0FBbXdDO0tBQzF3QyxDQUFDO0FBQ04sQ0FBQyxFQTdPZ0IsTUFBTSxHQUFOLGNBQU0sS0FBTixjQUFNLFFBNk90QiIsImZpbGUiOiJidW5kbGUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3NoYXJlZC9qcy9tYWluLnRzXCIpO1xuIiwiLy8gc2hpbSBmb3IgdXNpbmcgcHJvY2VzcyBpbiBicm93c2VyXG52YXIgcHJvY2VzcyA9IG1vZHVsZS5leHBvcnRzID0ge307XG5cbi8vIGNhY2hlZCBmcm9tIHdoYXRldmVyIGdsb2JhbCBpcyBwcmVzZW50IHNvIHRoYXQgdGVzdCBydW5uZXJzIHRoYXQgc3R1YiBpdFxuLy8gZG9uJ3QgYnJlYWsgdGhpbmdzLiAgQnV0IHdlIG5lZWQgdG8gd3JhcCBpdCBpbiBhIHRyeSBjYXRjaCBpbiBjYXNlIGl0IGlzXG4vLyB3cmFwcGVkIGluIHN0cmljdCBtb2RlIGNvZGUgd2hpY2ggZG9lc24ndCBkZWZpbmUgYW55IGdsb2JhbHMuICBJdCdzIGluc2lkZSBhXG4vLyBmdW5jdGlvbiBiZWNhdXNlIHRyeS9jYXRjaGVzIGRlb3B0aW1pemUgaW4gY2VydGFpbiBlbmdpbmVzLlxuXG52YXIgY2FjaGVkU2V0VGltZW91dDtcbnZhciBjYWNoZWRDbGVhclRpbWVvdXQ7XG5cbmZ1bmN0aW9uIGRlZmF1bHRTZXRUaW1vdXQoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdzZXRUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG5mdW5jdGlvbiBkZWZhdWx0Q2xlYXJUaW1lb3V0ICgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NsZWFyVGltZW91dCBoYXMgbm90IGJlZW4gZGVmaW5lZCcpO1xufVxuKGZ1bmN0aW9uICgpIHtcbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIHNldFRpbWVvdXQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBzZXRUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBkZWZhdWx0U2V0VGltb3V0O1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIGNsZWFyVGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gY2xlYXJUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICB9XG59ICgpKVxuZnVuY3Rpb24gcnVuVGltZW91dChmdW4pIHtcbiAgICBpZiAoY2FjaGVkU2V0VGltZW91dCA9PT0gc2V0VGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgLy8gaWYgc2V0VGltZW91dCB3YXNuJ3QgYXZhaWxhYmxlIGJ1dCB3YXMgbGF0dGVyIGRlZmluZWRcbiAgICBpZiAoKGNhY2hlZFNldFRpbWVvdXQgPT09IGRlZmF1bHRTZXRUaW1vdXQgfHwgIWNhY2hlZFNldFRpbWVvdXQpICYmIHNldFRpbWVvdXQpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBzZXRUaW1lb3V0KGZ1biwgMCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIC8vIHdoZW4gd2hlbiBzb21lYm9keSBoYXMgc2NyZXdlZCB3aXRoIHNldFRpbWVvdXQgYnV0IG5vIEkuRS4gbWFkZG5lc3NcbiAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9IGNhdGNoKGUpe1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gV2hlbiB3ZSBhcmUgaW4gSS5FLiBidXQgdGhlIHNjcmlwdCBoYXMgYmVlbiBldmFsZWQgc28gSS5FLiBkb2Vzbid0IHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKG51bGwsIGZ1biwgMCk7XG4gICAgICAgIH0gY2F0Y2goZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvclxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQuY2FsbCh0aGlzLCBmdW4sIDApO1xuICAgICAgICB9XG4gICAgfVxuXG5cbn1cbmZ1bmN0aW9uIHJ1bkNsZWFyVGltZW91dChtYXJrZXIpIHtcbiAgICBpZiAoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBjbGVhclRpbWVvdXQpIHtcbiAgICAgICAgLy9ub3JtYWwgZW52aXJvbWVudHMgaW4gc2FuZSBzaXR1YXRpb25zXG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgLy8gaWYgY2xlYXJUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBkZWZhdWx0Q2xlYXJUaW1lb3V0IHx8ICFjYWNoZWRDbGVhclRpbWVvdXQpICYmIGNsZWFyVGltZW91dCkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0KG1hcmtlcik7XG4gICAgfSBjYXRjaCAoZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgIHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0LmNhbGwobnVsbCwgbWFya2VyKTtcbiAgICAgICAgfSBjYXRjaCAoZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvci5cbiAgICAgICAgICAgIC8vIFNvbWUgdmVyc2lvbnMgb2YgSS5FLiBoYXZlIGRpZmZlcmVudCBydWxlcyBmb3IgY2xlYXJUaW1lb3V0IHZzIHNldFRpbWVvdXRcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbCh0aGlzLCBtYXJrZXIpO1xuICAgICAgICB9XG4gICAgfVxuXG5cblxufVxudmFyIHF1ZXVlID0gW107XG52YXIgZHJhaW5pbmcgPSBmYWxzZTtcbnZhciBjdXJyZW50UXVldWU7XG52YXIgcXVldWVJbmRleCA9IC0xO1xuXG5mdW5jdGlvbiBjbGVhblVwTmV4dFRpY2soKSB7XG4gICAgaWYgKCFkcmFpbmluZyB8fCAhY3VycmVudFF1ZXVlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBpZiAoY3VycmVudFF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBxdWV1ZSA9IGN1cnJlbnRRdWV1ZS5jb25jYXQocXVldWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICB9XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBkcmFpblF1ZXVlKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBkcmFpblF1ZXVlKCkge1xuICAgIGlmIChkcmFpbmluZykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciB0aW1lb3V0ID0gcnVuVGltZW91dChjbGVhblVwTmV4dFRpY2spO1xuICAgIGRyYWluaW5nID0gdHJ1ZTtcblxuICAgIHZhciBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgd2hpbGUobGVuKSB7XG4gICAgICAgIGN1cnJlbnRRdWV1ZSA9IHF1ZXVlO1xuICAgICAgICBxdWV1ZSA9IFtdO1xuICAgICAgICB3aGlsZSAoKytxdWV1ZUluZGV4IDwgbGVuKSB7XG4gICAgICAgICAgICBpZiAoY3VycmVudFF1ZXVlKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudFF1ZXVlW3F1ZXVlSW5kZXhdLnJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICAgICAgbGVuID0gcXVldWUubGVuZ3RoO1xuICAgIH1cbiAgICBjdXJyZW50UXVldWUgPSBudWxsO1xuICAgIGRyYWluaW5nID0gZmFsc2U7XG4gICAgcnVuQ2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xufVxuXG5wcm9jZXNzLm5leHRUaWNrID0gZnVuY3Rpb24gKGZ1bikge1xuICAgIHZhciBhcmdzID0gbmV3IEFycmF5KGFyZ3VtZW50cy5sZW5ndGggLSAxKTtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICAgICAgICB9XG4gICAgfVxuICAgIHF1ZXVlLnB1c2gobmV3IEl0ZW0oZnVuLCBhcmdzKSk7XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCA9PT0gMSAmJiAhZHJhaW5pbmcpIHtcbiAgICAgICAgcnVuVGltZW91dChkcmFpblF1ZXVlKTtcbiAgICB9XG59O1xuXG4vLyB2OCBsaWtlcyBwcmVkaWN0aWJsZSBvYmplY3RzXG5mdW5jdGlvbiBJdGVtKGZ1biwgYXJyYXkpIHtcbiAgICB0aGlzLmZ1biA9IGZ1bjtcbiAgICB0aGlzLmFycmF5ID0gYXJyYXk7XG59XG5JdGVtLnByb3RvdHlwZS5ydW4gPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5mdW4uYXBwbHkobnVsbCwgdGhpcy5hcnJheSk7XG59O1xucHJvY2Vzcy50aXRsZSA9ICdicm93c2VyJztcbnByb2Nlc3MuYnJvd3NlciA9IHRydWU7XG5wcm9jZXNzLmVudiA9IHt9O1xucHJvY2Vzcy5hcmd2ID0gW107XG5wcm9jZXNzLnZlcnNpb24gPSAnJzsgLy8gZW1wdHkgc3RyaW5nIHRvIGF2b2lkIHJlZ2V4cCBpc3N1ZXNcbnByb2Nlc3MudmVyc2lvbnMgPSB7fTtcblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbnByb2Nlc3Mub24gPSBub29wO1xucHJvY2Vzcy5hZGRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLm9uY2UgPSBub29wO1xucHJvY2Vzcy5vZmYgPSBub29wO1xucHJvY2Vzcy5yZW1vdmVMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUFsbExpc3RlbmVycyA9IG5vb3A7XG5wcm9jZXNzLmVtaXQgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kTGlzdGVuZXIgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kT25jZUxpc3RlbmVyID0gbm9vcDtcblxucHJvY2Vzcy5saXN0ZW5lcnMgPSBmdW5jdGlvbiAobmFtZSkgeyByZXR1cm4gW10gfVxuXG5wcm9jZXNzLmJpbmRpbmcgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5iaW5kaW5nIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5cbnByb2Nlc3MuY3dkID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gJy8nIH07XG5wcm9jZXNzLmNoZGlyID0gZnVuY3Rpb24gKGRpcikge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5jaGRpciBpcyBub3Qgc3VwcG9ydGVkJyk7XG59O1xucHJvY2Vzcy51bWFzayA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gMDsgfTtcbiIsIi8qIGdsb2JhbHMgX193ZWJwYWNrX2FtZF9vcHRpb25zX18gKi9cbm1vZHVsZS5leHBvcnRzID0gX193ZWJwYWNrX2FtZF9vcHRpb25zX187XG4iLCJ2YXIgZztcblxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcbmcgPSAoZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0aGlzO1xufSkoKTtcblxudHJ5IHtcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXG5cdGcgPSBnIHx8IG5ldyBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCk7XG59IGNhdGNoIChlKSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xufVxuXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGc7XG4iLCJleHBvcnQgaW50ZXJmYWNlIFdpbmRvdyB7XHJcbiAgICBCcm9hZGNhc3RDaGFubmVsOiBCcm9hZGNhc3RDaGFubmVsO1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIGJpcGMge1xyXG4gICAgZXhwb3J0IGludGVyZmFjZSBCcm9hZGNhc3RNZXNzYWdlIHtcclxuICAgICAgICB0aW1lc3RhbXA6IG51bWJlcjtcclxuICAgICAgICByZWNlaXZlcjogc3RyaW5nO1xyXG4gICAgICAgIHNlbmRlcjogc3RyaW5nO1xyXG5cclxuICAgICAgICB0eXBlOiBzdHJpbmc7XHJcbiAgICAgICAgZGF0YTogYW55O1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIHV1aWR2NCgpIHtcclxuICAgICAgICByZXR1cm4gJ3h4eHh4eHh4LXh4eHgtNHh4eC15eHh4LXh4eHh4eHh4eHh4eCcucmVwbGFjZSgvW3h5XS9nLCBmdW5jdGlvbihjKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHIgPSBNYXRoLnJhbmRvbSgpICogMTYgfCAwLCB2ID0gYyA9PSAneCcgPyByIDogKHIgJiAweDMgfCAweDgpO1xyXG4gICAgICAgICAgICByZXR1cm4gdi50b1N0cmluZygxNik7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgaW50ZXJmYWNlIFByb2Nlc3NRdWVyeSB7XHJcbiAgICAgICAgdGltZXN0YW1wOiBudW1iZXJcclxuICAgICAgICBxdWVyeV9pZDogc3RyaW5nO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ2hhbm5lbE1lc3NhZ2Uge1xyXG4gICAgICAgIGNoYW5uZWxfaWQ6IHN0cmluZztcclxuICAgICAgICB0eXBlOiBzdHJpbmc7XHJcbiAgICAgICAgZGF0YTogYW55O1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgUHJvY2Vzc1F1ZXJ5UmVzcG9uc2Uge1xyXG4gICAgICAgIHJlcXVlc3RfdGltZXN0YW1wOiBudW1iZXJcclxuICAgICAgICByZXF1ZXN0X3F1ZXJ5X2lkOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIGRldmljZV9pZDogc3RyaW5nO1xyXG4gICAgICAgIHByb3RvY29sOiBudW1iZXI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBDZXJ0aWZpY2F0ZUFjY2VwdENhbGxiYWNrIHtcclxuICAgICAgICByZXF1ZXN0X2lkOiBzdHJpbmc7XHJcbiAgICB9XHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIENlcnRpZmljYXRlQWNjZXB0U3VjY2VlZGVkIHsgfVxyXG5cclxuICAgIGV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNpY0lQQ0hhbmRsZXIge1xyXG4gICAgICAgIHByb3RlY3RlZCBzdGF0aWMgcmVhZG9ubHkgQlJPQURDQVNUX1VOSVFVRV9JRCA9IFwiMDAwMDAwMDAtMDAwMC00MDAwLTAwMDAtMDAwMDAwMDAwMDAwXCI7XHJcbiAgICAgICAgcHJvdGVjdGVkIHN0YXRpYyByZWFkb25seSBQUk9UT0NPTF9WRVJTSU9OID0gMTtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIF9jaGFubmVsczogQ2hhbm5lbFtdID0gW107XHJcbiAgICAgICAgcHJvdGVjdGVkIHVuaXF1ZV9pZDtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKCkgeyB9XHJcblxyXG4gICAgICAgIHNldHVwKCkge1xyXG4gICAgICAgICAgICB0aGlzLnVuaXF1ZV9pZCA9IHV1aWR2NCgpOyAvKiBsZXRzIGdldCBhbiB1bmlxdWUgaWRlbnRpZmllciAqL1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZ2V0X2xvY2FsX2FkZHJlc3MoKSB7IHJldHVybiB0aGlzLnVuaXF1ZV9pZDsgfVxyXG5cclxuICAgICAgICBhYnN0cmFjdCBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBkYXRhOiBhbnksIHRhcmdldD86IHN0cmluZyk7XHJcblxyXG4gICAgICAgIHByb3RlY3RlZCBoYW5kbGVfbWVzc2FnZShtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgIC8vbG9nLnRyYWNlKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBtZXNzYWdlICVvXCIpLCBtZXNzYWdlKTtcclxuXHJcbiAgICAgICAgICAgIGlmKG1lc3NhZ2UucmVjZWl2ZXIgPT09IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lEKSB7XHJcbiAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJwcm9jZXNzLXF1ZXJ5XCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGEgZGV2aWNlIHF1ZXJ5IGZyb20gJXMuXCIpLCBtZXNzYWdlLnNlbmRlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZW5kX21lc3NhZ2UoXCJwcm9jZXNzLXF1ZXJ5LXJlc3BvbnNlXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9xdWVyeV9pZDogKDxQcm9jZXNzUXVlcnk+bWVzc2FnZS5kYXRhKS5xdWVyeV9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF90aW1lc3RhbXA6ICg8UHJvY2Vzc1F1ZXJ5Pm1lc3NhZ2UuZGF0YSkudGltZXN0YW1wLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgZGV2aWNlX2lkOiB0aGlzLnVuaXF1ZV9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2w6IEJhc2ljSVBDSGFuZGxlci5QUk9UT0NPTF9WRVJTSU9OXHJcbiAgICAgICAgICAgICAgICAgICAgfSBhcyBQcm9jZXNzUXVlcnlSZXNwb25zZSwgbWVzc2FnZS5zZW5kZXIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSBlbHNlIGlmKG1lc3NhZ2UucmVjZWl2ZXIgPT09IHRoaXMudW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJwcm9jZXNzLXF1ZXJ5LXJlc3BvbnNlXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZTogUHJvY2Vzc1F1ZXJ5UmVzcG9uc2UgPSBtZXNzYWdlLmRhdGE7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5fcXVlcnlfcmVzdWx0c1tyZXNwb25zZS5yZXF1ZXN0X3F1ZXJ5X2lkXSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcXVlcnlfcmVzdWx0c1tyZXNwb25zZS5yZXF1ZXN0X3F1ZXJ5X2lkXS5wdXNoKHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGEgcXVlcnkgcmVzcG9uc2UgZm9yIGFuIHVua25vd24gcmVxdWVzdC5cIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImNlcnRpZmljYXRlLWFjY2VwdC1jYWxsYmFja1wiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YTogQ2VydGlmaWNhdGVBY2NlcHRDYWxsYmFjayA9IG1lc3NhZ2UuZGF0YTtcclxuICAgICAgICAgICAgICAgICAgICBpZighdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF0pIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGNlcnRpZmljYXRlIGFjY2VwdCBjYWxsYmFjayBmb3IgYW4gdW5rbm93biByZXF1ZXN0IElELlwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF0oKTtcclxuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwiY2VydGlmaWNhdGUtYWNjZXB0LXN1Y2NlZWRlZFwiLCB7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIH0gYXMgQ2VydGlmaWNhdGVBY2NlcHRTdWNjZWVkZWQsIG1lc3NhZ2Uuc2VuZGVyKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImNlcnRpZmljYXRlLWFjY2VwdC1zdWNjZWVkZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9jZXJ0X2FjY2VwdF9zdWNjZWVkZWRbbWVzc2FnZS5zZW5kZXJdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjZXJ0aWZpY2F0ZSBhY2NlcHQgc3VjY2VlZGVkLCBidXQgaGF2ZW4ndCBhIGNhbGxiYWNrLlwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2VydF9hY2NlcHRfc3VjY2VlZGVkW21lc3NhZ2Uuc2VuZGVyXSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT09IFwiY2hhbm5lbFwiKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhOiBDaGFubmVsTWVzc2FnZSA9IG1lc3NhZ2UuZGF0YTtcclxuXHJcbiAgICAgICAgICAgICAgICBsZXQgY2hhbm5lbF9pbnZva2VkID0gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICBmb3IoY29uc3QgY2hhbm5lbCBvZiB0aGlzLl9jaGFubmVscylcclxuICAgICAgICAgICAgICAgICAgICBpZihjaGFubmVsLmNoYW5uZWxfaWQgPT09IGRhdGEuY2hhbm5lbF9pZCAmJiAodHlwZW9mKGNoYW5uZWwudGFyZ2V0X2lkKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCBjaGFubmVsLnRhcmdldF9pZCA9PT0gbWVzc2FnZS5zZW5kZXIpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGNoYW5uZWwubWVzc2FnZV9oYW5kbGVyKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIobWVzc2FnZS5zZW5kZXIsIG1lc3NhZ2UucmVjZWl2ZXIgPT09IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lELCBkYXRhKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY2hhbm5lbF9pbnZva2VkID0gdHJ1ZTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZighY2hhbm5lbF9pbnZva2VkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiUmVjZWl2ZWQgY2hhbm5lbCBtZXNzYWdlIGZvciB1bmtub3duIGNoYW5uZWwgKCVzKVwiKSwgZGF0YS5jaGFubmVsX2lkKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY3JlYXRlX2NoYW5uZWwodGFyZ2V0X2lkPzogc3RyaW5nLCBjaGFubmVsX2lkPzogc3RyaW5nKSB7XHJcbiAgICAgICAgICAgIGxldCBjaGFubmVsOiBDaGFubmVsID0ge1xyXG4gICAgICAgICAgICAgICAgdGFyZ2V0X2lkOiB0YXJnZXRfaWQsXHJcbiAgICAgICAgICAgICAgICBjaGFubmVsX2lkOiBjaGFubmVsX2lkIHx8IHV1aWR2NCgpLFxyXG4gICAgICAgICAgICAgICAgbWVzc2FnZV9oYW5kbGVyOiB1bmRlZmluZWQsXHJcbiAgICAgICAgICAgICAgICBzZW5kX21lc3NhZ2U6ICh0eXBlOiBzdHJpbmcsIGRhdGE6IGFueSwgdGFyZ2V0Pzogc3RyaW5nKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodHlwZW9mIHRhcmdldCAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZih0eXBlb2YgY2hhbm5lbC50YXJnZXRfaWQgPT09IFwic3RyaW5nXCIgJiYgdGFyZ2V0ICE9IGNoYW5uZWwudGFyZ2V0X2lkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJ0YXJnZXQgaWQgZG9lcyBub3QgbWF0Y2ggY2hhbm5lbCB0YXJnZXRcIjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwiY2hhbm5lbFwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IHR5cGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IGRhdGEsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoYW5uZWxfaWQ6IGNoYW5uZWwuY2hhbm5lbF9pZFxyXG4gICAgICAgICAgICAgICAgICAgIH0gYXMgQ2hhbm5lbE1lc3NhZ2UsIHRhcmdldCB8fCBjaGFubmVsLnRhcmdldF9pZCB8fCBCYXNpY0lQQ0hhbmRsZXIuQlJPQURDQVNUX1VOSVFVRV9JRCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICB0aGlzLl9jaGFubmVscy5wdXNoKGNoYW5uZWwpO1xyXG4gICAgICAgICAgICByZXR1cm4gY2hhbm5lbDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNoYW5uZWxzKCkgOiBDaGFubmVsW10geyByZXR1cm4gdGhpcy5fY2hhbm5lbHM7IH1cclxuXHJcbiAgICAgICAgZGVsZXRlX2NoYW5uZWwoY2hhbm5lbDogQ2hhbm5lbCkge1xyXG4gICAgICAgICAgICB0aGlzLl9jaGFubmVscyA9IHRoaXMuX2NoYW5uZWxzLmZpbHRlcihlID0+IGUgIT09IGNoYW5uZWwpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcHJpdmF0ZSBfcXVlcnlfcmVzdWx0czoge1trZXk6IHN0cmluZ106UHJvY2Vzc1F1ZXJ5UmVzcG9uc2VbXX0gPSB7fTtcclxuICAgICAgICBhc3luYyBxdWVyeV9wcm9jZXNzZXModGltZW91dD86IG51bWJlcikgOiBQcm9taXNlPFByb2Nlc3NRdWVyeVJlc3BvbnNlW10+IHtcclxuICAgICAgICAgICAgY29uc3QgcXVlcnlfaWQgPSB1dWlkdjQoKTtcclxuICAgICAgICAgICAgdGhpcy5fcXVlcnlfcmVzdWx0c1txdWVyeV9pZF0gPSBbXTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwicHJvY2Vzcy1xdWVyeVwiLCB7XHJcbiAgICAgICAgICAgICAgICBxdWVyeV9pZDogcXVlcnlfaWQsXHJcbiAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KClcclxuICAgICAgICAgICAgfSBhcyBQcm9jZXNzUXVlcnkpO1xyXG5cclxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIHRpbWVvdXQgfHwgMjUwKSk7XHJcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuX3F1ZXJ5X3Jlc3VsdHNbcXVlcnlfaWRdO1xyXG4gICAgICAgICAgICBkZWxldGUgdGhpcy5fcXVlcnlfcmVzdWx0c1txdWVyeV9pZF07XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBwcml2YXRlIF9jZXJ0X2FjY2VwdF9jYWxsYmFja3M6IHtba2V5OiBzdHJpbmddOigoKSA9PiBhbnkpfSA9IHt9O1xyXG4gICAgICAgIHJlZ2lzdGVyX2NlcnRpZmljYXRlX2FjY2VwdF9jYWxsYmFjayhjYWxsYmFjazogKCkgPT4gYW55KSA6IHN0cmluZyB7XHJcbiAgICAgICAgICAgIGNvbnN0IGlkID0gdXVpZHY0KCk7XHJcbiAgICAgICAgICAgIHRoaXMuX2NlcnRfYWNjZXB0X2NhbGxiYWNrc1tpZF0gPSBjYWxsYmFjaztcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudW5pcXVlX2lkICsgXCI6XCIgKyBpZDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZDoge1tzZW5kZXI6IHN0cmluZ106KCgpID0+IGFueSl9ID0ge307XHJcbiAgICAgICAgcG9zdF9jZXJ0aWZpY2F0ZV9hY2NwZWN0ZWQoaWQ6IHN0cmluZywgdGltZW91dD86IG51bWJlcikgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBpZC5zcGxpdChcIjpcIik7XHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZFtkYXRhWzBdXTtcclxuICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwidGltZW91dFwiKTtcclxuICAgICAgICAgICAgICAgIH0sIHRpbWVvdXQgfHwgMjUwKTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZFtkYXRhWzBdXSA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fY2VydF9hY2NlcHRfc3VjY2VlZGVkW2RhdGFbMF1dO1xyXG4gICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgdGhpcy5zZW5kX21lc3NhZ2UoXCJjZXJ0aWZpY2F0ZS1hY2NlcHQtY2FsbGJhY2tcIiwge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlcXVlc3RfaWQ6IGRhdGFbMV1cclxuICAgICAgICAgICAgICAgIH0gYXMgQ2VydGlmaWNhdGVBY2NlcHRDYWxsYmFjaywgZGF0YVswXSk7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ2hhbm5lbCB7XHJcbiAgICAgICAgcmVhZG9ubHkgY2hhbm5lbF9pZDogc3RyaW5nO1xyXG4gICAgICAgIHRhcmdldF9pZD86IHN0cmluZztcclxuXHJcbiAgICAgICAgbWVzc2FnZV9oYW5kbGVyOiAocmVtb3RlX2lkOiBzdHJpbmcsIGJyb2FkY2FzdDogYm9vbGVhbiwgbWVzc2FnZTogQ2hhbm5lbE1lc3NhZ2UpID0+IGFueTtcclxuICAgICAgICBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBtZXNzYWdlOiBhbnksIHRhcmdldD86IHN0cmluZyk7XHJcbiAgICB9XHJcblxyXG4gICAgY2xhc3MgQnJvYWRjYXN0Q2hhbm5lbElQQyBleHRlbmRzIEJhc2ljSVBDSGFuZGxlciB7XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ0hBTk5FTF9OQU1FID0gXCJUZWFTcGVhay1XZWJcIjtcclxuXHJcbiAgICAgICAgcHJpdmF0ZSBjaGFubmVsOiBCcm9hZGNhc3RDaGFubmVsO1xyXG5cclxuICAgICAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHNldHVwKCkge1xyXG4gICAgICAgICAgICBzdXBlci5zZXR1cCgpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jaGFubmVsID0gbmV3IEJyb2FkY2FzdENoYW5uZWwoQnJvYWRjYXN0Q2hhbm5lbElQQy5DSEFOTkVMX05BTUUpO1xyXG4gICAgICAgICAgICB0aGlzLmNoYW5uZWwub25tZXNzYWdlID0gdGhpcy5vbl9tZXNzYWdlLmJpbmQodGhpcyk7XHJcbiAgICAgICAgICAgIHRoaXMuY2hhbm5lbC5vbm1lc3NhZ2VlcnJvciA9IHRoaXMub25fZXJyb3IuYmluZCh0aGlzKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgb25fbWVzc2FnZShldmVudDogTWVzc2FnZUV2ZW50KSB7XHJcbiAgICAgICAgICAgIGlmKHR5cGVvZihldmVudC5kYXRhKSAhPT0gXCJzdHJpbmdcIikge1xyXG4gICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIG1lc3NhZ2Ugd2l0aCBhbiBpbnZhbGlkIHR5cGUgKCVzKTogJW9cIiksIHR5cGVvZihldmVudC5kYXRhKSwgZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGxldCBtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlO1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgbWVzc2FnZSA9IEpTT04ucGFyc2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgYW4gaW52YWxpZCBlbmNvZGVkIG1lc3NhZ2U6ICVvXCIpLCBldmVudC5kYXRhKTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzdXBlci5oYW5kbGVfbWVzc2FnZShtZXNzYWdlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgb25fZXJyb3IoZXZlbnQ6IE1lc3NhZ2VFdmVudCkge1xyXG4gICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgZXJyb3I6ICVvXCIpLCBldmVudCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBkYXRhOiBhbnksIHRhcmdldD86IHN0cmluZykge1xyXG4gICAgICAgICAgICBjb25zdCBtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlID0ge30gYXMgYW55O1xyXG5cclxuICAgICAgICAgICAgbWVzc2FnZS5zZW5kZXIgPSB0aGlzLnVuaXF1ZV9pZDtcclxuICAgICAgICAgICAgbWVzc2FnZS5yZWNlaXZlciA9IHRhcmdldCA/IHRhcmdldCA6IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lEO1xyXG4gICAgICAgICAgICBtZXNzYWdlLnRpbWVzdGFtcCA9IERhdGUubm93KCk7XHJcbiAgICAgICAgICAgIG1lc3NhZ2UudHlwZSA9IHR5cGU7XHJcbiAgICAgICAgICAgIG1lc3NhZ2UuZGF0YSA9IGRhdGE7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNoYW5uZWwucG9zdE1lc3NhZ2UoSlNPTi5zdHJpbmdpZnkobWVzc2FnZSkpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgbmFtZXNwYWNlIGNvbm5lY3Qge1xyXG4gICAgICAgIGV4cG9ydCB0eXBlIENvbm5lY3RSZXF1ZXN0RGF0YSA9IHtcclxuICAgICAgICAgICAgYWRkcmVzczogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgcHJvZmlsZT86IHN0cmluZztcclxuICAgICAgICAgICAgdXNlcm5hbWU/OiBzdHJpbmc7XHJcbiAgICAgICAgICAgIHBhc3N3b3JkPzoge1xyXG4gICAgICAgICAgICAgICAgdmFsdWU6IHN0cmluZztcclxuICAgICAgICAgICAgICAgIGhhc2hlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgQ29ubmVjdE9mZmVyIHtcclxuICAgICAgICAgICAgcmVxdWVzdF9pZDogc3RyaW5nO1xyXG4gICAgICAgICAgICBkYXRhOiBDb25uZWN0UmVxdWVzdERhdGE7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgaW50ZXJmYWNlIENvbm5lY3RPZmZlckFuc3dlciB7XHJcbiAgICAgICAgICAgIHJlcXVlc3RfaWQ6IHN0cmluZztcclxuICAgICAgICAgICAgYWNjZXB0ZWQ6IGJvb2xlYW47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgaW50ZXJmYWNlIENvbm5lY3RFeGVjdXRlIHtcclxuICAgICAgICAgICAgcmVxdWVzdF9pZDogc3RyaW5nO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBDb25uZWN0RXhlY3V0ZWQge1xyXG4gICAgICAgICAgICByZXF1ZXN0X2lkOiBzdHJpbmc7XHJcbiAgICAgICAgICAgIHN1Y2NlZWRlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgbWVzc2FnZT86IHN0cmluZztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8qIFRoZSBjb25uZWN0IHByb2Nlc3M6XHJcbiAgICAgICAgICogIDEuIEJyb2FkY2FzdCBhbiBvZmZlclxyXG4gICAgICAgICAqICAyLiBXYWl0IDUwbXMgZm9yIGFsbCBvZmZlciByZXNwb25zZXMgb3IgdW50aWwgdGhlIGZpcnN0IG9uZSByZXNwb25kIHdpdGggXCJva1wiXHJcbiAgICAgICAgICogIDMuIFNlbGVjdCAoaWYgcG9zc2libGUpIG9uIGFjY2VwdGVkIG9mZmVyIGFuZCBleGVjdXRlIHRoZSBjb25uZWN0XHJcbiAgICAgICAgICovXHJcbiAgICAgICAgZXhwb3J0IGNsYXNzIENvbm5lY3RIYW5kbGVyIHtcclxuICAgICAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ0hBTk5FTF9OQU1FID0gXCJjb25uZWN0XCI7XHJcblxyXG4gICAgICAgICAgICByZWFkb25seSBpcGNfaGFuZGxlcjogQmFzaWNJUENIYW5kbGVyO1xyXG4gICAgICAgICAgICBwcml2YXRlIGlwY19jaGFubmVsOiBDaGFubmVsO1xyXG5cclxuICAgICAgICAgICAgcHVibGljIGNhbGxiYWNrX2F2YWlsYWJsZTogKGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSkgPT4gYm9vbGVhbiA9ICgpID0+IGZhbHNlO1xyXG4gICAgICAgICAgICBwdWJsaWMgY2FsbGJhY2tfZXhlY3V0ZTogKGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSkgPT4gYm9vbGVhbiB8IHN0cmluZyA9ICgpID0+IGZhbHNlO1xyXG5cclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX3BlbmRpbmdfY29ubmVjdF9vZmZlcnM6IHtcclxuICAgICAgICAgICAgICAgIGlkOiBzdHJpbmc7XHJcbiAgICAgICAgICAgICAgICBkYXRhOiBDb25uZWN0UmVxdWVzdERhdGE7XHJcbiAgICAgICAgICAgICAgICB0aW1lb3V0OiBudW1iZXI7XHJcblxyXG4gICAgICAgICAgICAgICAgcmVtb3RlX2hhbmRsZXI6IHN0cmluZztcclxuICAgICAgICAgICAgfVtdID0gW107XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzOiB7XHJcbiAgICAgICAgICAgICAgICBpZDogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgICAgIGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YTtcclxuICAgICAgICAgICAgICAgIHRpbWVvdXQ6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19zdWNjZXNzOiAoKSA9PiBhbnk7XHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19mYWlsZWQ6IChtZXNzYWdlOiBzdHJpbmcpID0+IGFueTtcclxuICAgICAgICAgICAgICAgIGNhbGxiYWNrX2F2YWlsOiAoKSA9PiBQcm9taXNlPGJvb2xlYW4+O1xyXG5cclxuICAgICAgICAgICAgICAgIHJlbW90ZV9oYW5kbGVyPzogc3RyaW5nO1xyXG4gICAgICAgICAgICB9W10gPSBbXTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yKGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXIpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaXBjX2hhbmRsZXIgPSBpcGNfaGFuZGxlcjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHVibGljIHNldHVwKCkge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfY2hhbm5lbCA9IHRoaXMuaXBjX2hhbmRsZXIuY3JlYXRlX2NoYW5uZWwodW5kZWZpbmVkLCBDb25uZWN0SGFuZGxlci5DSEFOTkVMX05BTUUpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIgPSB0aGlzLm9uX21lc3NhZ2UuYmluZCh0aGlzKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBvbl9tZXNzYWdlKHNlbmRlcjogc3RyaW5nLCBicm9hZGNhc3Q6IGJvb2xlYW4sIG1lc3NhZ2U6IENoYW5uZWxNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgICAgICBpZihicm9hZGNhc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJvZmZlclwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEgYXMgQ29ubmVjdE9mZmVyO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2NlcHRlZDogdGhpcy5jYWxsYmFja19hdmFpbGFibGUoZGF0YS5kYXRhKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3RfaWQ6IGRhdGEucmVxdWVzdF9pZFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGFzIENvbm5lY3RPZmZlckFuc3dlcjtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlc3BvbnNlLmFjY2VwdGVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIG5ldyBjb25uZWN0IG9mZmVyIGZyb20gJXM6ICVzXCIpLCBzZW5kZXIsIGRhdGEucmVxdWVzdF9pZCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbGQgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3RlX2hhbmRsZXI6IHNlbmRlcixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLmRhdGEsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IGRhdGEucmVxdWVzdF9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lb3V0OiAwXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5wdXNoKGxkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxkLnRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkRyb3BwaW5nIGNvbm5lY3QgcmVxdWVzdCAlcywgYmVjYXVzZSB3ZSBuZXZlciByZWNlaXZlZCBhbiBleGVjdXRlLlwiKSwgbGQuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdfY29ubmVjdF9vZmZlcnMucmVtb3ZlKGxkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIDEyMCAqIDEwMDApIGFzIGFueTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcIm9mZmVyLWFuc3dlclwiLCByZXNwb25zZSwgc2VuZGVyKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKG1lc3NhZ2UudHlwZSA9PSBcIm9mZmVyLWFuc3dlclwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEgYXMgQ29ubmVjdE9mZmVyQW5zd2VyO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gdGhpcy5fcGVuZGluZ19jb25uZWN0c19yZXF1ZXN0cy5maW5kKGUgPT4gZS5pZCA9PT0gZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIXJlcXVlc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjb25uZWN0IG9mZmVyIGFuc3dlciB3aXRoIHVua25vd24gcmVxdWVzdCBpZCAoJXMpLlwiKSwgZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighZGF0YS5hY2NlcHRlZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmRlYnVnKExvZ0NhdGVnb3J5LklQQywgdHIoXCJDbGllbnQgJXMgcmVqZWN0ZWQgdGhlIGNvbm5lY3Qgb2ZmZXIgKCVzKS5cIiksIHNlbmRlciwgcmVxdWVzdC5pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYocmVxdWVzdC5yZW1vdGVfaGFuZGxlcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmRlYnVnKExvZ0NhdGVnb3J5LklQQywgdHIoXCJDbGllbnQgJXMgYWNjZXB0ZWQgdGhlIGNvbm5lY3Qgb2ZmZXIgKCVzKSwgYnV0IG9mZmVyIGhhcyBhbHJlYWR5IGJlZW4gYWNjZXB0ZWQuXCIpLCBzZW5kZXIsIHJlcXVlc3QuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkNsaWVudCAlcyBhY2NlcHRlZCB0aGUgY29ubmVjdCBvZmZlciAoJXMpLiBSZXF1ZXN0IGxvY2FsIGFjY2VwdGFuY2UuXCIpLCBzZW5kZXIsIHJlcXVlc3QuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyID0gc2VuZGVyO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocmVxdWVzdC50aW1lb3V0KTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfYXZhaWwoKS50aGVuKGZsYWcgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoIWZsYWcpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChcImxvY2FsIGF2YWlsIHJlamVjdGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkV4ZWN1dGluZyBjb25uZWN0IHdpdGggY2xpZW50ICVzXCIpLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXBjX2NoYW5uZWwuc2VuZF9tZXNzYWdlKFwiZXhlY3V0ZVwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogcmVxdWVzdC5pZFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBhcyBDb25uZWN0RXhlY3V0ZSwgcmVxdWVzdC5yZW1vdGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChcImNvbm5lY3QgZXhlY3V0ZSB0aW1lb3V0XCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgMTAwMCkgYXMgYW55O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkxvY2FsIGF2YWlsIGNhbGxiYWNrIGNhdXNlZCBhbiBlcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfZmFpbGVkKHRyKFwibG9jYWwgYXZhaWwgY2FsbGJhY2sgY2F1c2VkIGFuIGVycm9yXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImV4ZWN1dGVkXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IG1lc3NhZ2UuZGF0YSBhcyBDb25uZWN0RXhlY3V0ZWQ7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLmZpbmQoZSA9PiBlLmlkID09PSBkYXRhLnJlcXVlc3RfaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighcmVxdWVzdCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGNvbm5lY3QgZXhlY3V0ZWQgd2l0aCB1bmtub3duIHJlcXVlc3QgaWQgKCVzKS5cIiksIGRhdGEucmVxdWVzdF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlcXVlc3QucmVtb3RlX2hhbmRsZXIgIT0gc2VuZGVyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlZCBmb3IgcmVxdWVzdCAlcywgYnV0IGZyb20gd3JvbmcgY2xpZW50OiAlcyAoZXhwZWN0ZWQgJXMpXCIpLCBkYXRhLnJlcXVlc3RfaWQsIHNlbmRlciwgcmVxdWVzdC5yZW1vdGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5kZWJ1ZyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlZCByZXNwb25zZSBmcm9tIGNsaWVudCAlcyBmb3IgcmVxdWVzdCAlcy4gU3VjY2VlZGVkOiAlbyAoJXMpXCIpLCBzZW5kZXIsIGRhdGEucmVxdWVzdF9pZCwgZGF0YS5zdWNjZWVkZWQsIGRhdGEubWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dChyZXF1ZXN0LnRpbWVvdXQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZihkYXRhLnN1Y2NlZWRlZClcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfc3VjY2VzcygpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChkYXRhLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImV4ZWN1dGVcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gbWVzc2FnZS5kYXRhIGFzIENvbm5lY3RFeGVjdXRlO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5maW5kKGUgPT4gZS5pZCA9PT0gZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIXJlcXVlc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjb25uZWN0IGV4ZWN1dGUgd2l0aCB1bmtub3duIHJlcXVlc3QgaWQgKCVzKS5cIiksIGRhdGEucmVxdWVzdF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlcXVlc3QucmVtb3RlX2hhbmRsZXIgIT0gc2VuZGVyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlIGZvciByZXF1ZXN0ICVzLCBidXQgZnJvbSB3cm9uZyBjbGllbnQ6ICVzIChleHBlY3RlZCAlcylcIiksIGRhdGEucmVxdWVzdF9pZCwgc2VuZGVyLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocmVxdWVzdC50aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5yZW1vdmUocmVxdWVzdCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkV4ZWN1dGluZyBjb25uZWN0IGZvciAlc1wiKSwgZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgY3IgPSB0aGlzLmNhbGxiYWNrX2V4ZWN1dGUocmVxdWVzdC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogZGF0YS5yZXF1ZXN0X2lkLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2NlZWRlZDogdHlwZW9mKGNyKSAhPT0gXCJzdHJpbmdcIiAmJiBjcixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IHR5cGVvZihjcikgPT09IFwic3RyaW5nXCIgPyBjciA6IFwiXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gYXMgQ29ubmVjdEV4ZWN1dGVkO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcImV4ZWN1dGVkXCIsIHJlc3BvbnNlLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHBvc3RfY29ubmVjdF9yZXF1ZXN0KGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSwgY2FsbGJhY2tfYXZhaWw6ICgpID0+IFByb21pc2U8Ym9vbGVhbj4pIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBkID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZDogdXVpZHY0KCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVvdXQ6IDAsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19zdWNjZXNzOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLnJlbW92ZShwZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocGQudGltZW91dCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19mYWlsZWQ6IGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdfY29ubmVjdHNfcmVxdWVzdHMucmVtb3ZlKHBkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dChwZC50aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19hdmFpbDogY2FsbGJhY2tfYXZhaWwsXHJcbiAgICAgICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLnB1c2gocGQpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcIm9mZmVyXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogcGQuaWQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHBkLmRhdGFcclxuICAgICAgICAgICAgICAgICAgICB9IGFzIENvbm5lY3RPZmZlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgcGQudGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBwZC5jYWxsYmFja19mYWlsZWQoXCJyZWNlaXZlZCBubyByZXNwb25zZSB0byBvZmZlclwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9LCA1MCkgYXMgYW55O1xyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgbmFtZXNwYWNlIG1wcm94eSB7XHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBNZXRob2RQcm94eUludm9rZURhdGEge1xyXG4gICAgICAgICAgICBtZXRob2RfbmFtZTogc3RyaW5nO1xyXG4gICAgICAgICAgICBhcmd1bWVudHM6IGFueVtdO1xyXG4gICAgICAgICAgICBwcm9taXNlX2lkOiBzdHJpbmc7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgTWV0aG9kUHJveHlSZXN1bHREYXRhIHtcclxuICAgICAgICAgICAgcHJvbWlzZV9pZDogc3RyaW5nO1xyXG4gICAgICAgICAgICByZXN1bHQ6IGFueTtcclxuICAgICAgICAgICAgc3VjY2VzczogYm9vbGVhbjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBNZXRob2RQcm94eUNhbGxiYWNrIHtcclxuICAgICAgICAgICAgcHJvbWlzZTogUHJvbWlzZTxhbnk+O1xyXG4gICAgICAgICAgICBwcm9taXNlX2lkOiBzdHJpbmc7XHJcblxyXG4gICAgICAgICAgICByZXNvbHZlOiAob2JqZWN0OiBhbnkpID0+IGFueTtcclxuICAgICAgICAgICAgcmVqZWN0OiAob2JqZWN0OiBhbnkpID0+IGFueTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCB0eXBlIE1ldGhvZFByb3h5Q29ubmVjdFBhcmFtZXRlcnMgPSB7XHJcbiAgICAgICAgICAgIGNoYW5uZWxfaWQ6IHN0cmluZztcclxuICAgICAgICAgICAgY2xpZW50X2lkOiBzdHJpbmc7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGV4cG9ydCBhYnN0cmFjdCBjbGFzcyBNZXRob2RQcm94eSB7XHJcbiAgICAgICAgICAgIHJlYWRvbmx5IGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXI7XHJcbiAgICAgICAgICAgIHByaXZhdGUgX2lwY19jaGFubmVsOiBDaGFubmVsO1xyXG4gICAgICAgICAgICBwcml2YXRlIF9pcGNfcGFyYW1ldGVyczogTWV0aG9kUHJveHlDb25uZWN0UGFyYW1ldGVycztcclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgcmVhZG9ubHkgX2xvY2FsOiBib29sZWFuO1xyXG4gICAgICAgICAgICBwcml2YXRlIHJlYWRvbmx5IF9zbGF2ZTogYm9vbGVhbjtcclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX2Nvbm5lY3RlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgcHJpdmF0ZSBfcHJveGllZF9tZXRob2RzOiB7W2tleTogc3RyaW5nXTooKSA9PiBQcm9taXNlPGFueT59ID0ge307XHJcbiAgICAgICAgICAgIHByaXZhdGUgX3Byb3hpZWRfY2FsbGJhY2tzOiB7W2tleTogc3RyaW5nXTpNZXRob2RQcm94eUNhbGxiYWNrfSA9IHt9O1xyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXIsIGNvbm5lY3RfcGFyYW1zPzogTWV0aG9kUHJveHlDb25uZWN0UGFyYW1ldGVycykge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfaGFuZGxlciA9IGlwY19oYW5kbGVyO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5faXBjX3BhcmFtZXRlcnMgPSBjb25uZWN0X3BhcmFtcztcclxuICAgICAgICAgICAgICAgIHRoaXMuX2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fc2xhdmUgPSB0eXBlb2YoY29ubmVjdF9wYXJhbXMpICE9PSBcInVuZGVmaW5lZFwiO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fbG9jYWwgPSB0eXBlb2YoY29ubmVjdF9wYXJhbXMpICE9PSBcInVuZGVmaW5lZFwiICYmIGNvbm5lY3RfcGFyYW1zLmNoYW5uZWxfaWQgPT09IFwibG9jYWxcIiAmJiBjb25uZWN0X3BhcmFtcy5jbGllbnRfaWQgPT09IFwibG9jYWxcIjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIHNldHVwKCkge1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy5fbG9jYWwpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jb25uZWN0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25fY29ubmVjdGVkKCk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbCA9IHRoaXMuaXBjX2hhbmRsZXIuY3JlYXRlX2NoYW5uZWwodGhpcy5faXBjX3BhcmFtZXRlcnMuY2xpZW50X2lkLCB0aGlzLl9pcGNfcGFyYW1ldGVycy5jaGFubmVsX2lkKTtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsID0gdGhpcy5pcGNfaGFuZGxlci5jcmVhdGVfY2hhbm5lbCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIgPSB0aGlzLl9oYW5kbGVfbWVzc2FnZS5iaW5kKHRoaXMpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5zZW5kX21lc3NhZ2UoXCJpbml0aWFsaXplXCIsIHt9KTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGZpbmFsaXplKCkge1xyXG4gICAgICAgICAgICAgICAgaWYoIXRoaXMuX2xvY2FsKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5fY29ubmVjdGVkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5zZW5kX21lc3NhZ2UoXCJmaW5hbGl6ZVwiLCB7fSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaXBjX2hhbmRsZXIuZGVsZXRlX2NoYW5uZWwodGhpcy5faXBjX2NoYW5uZWwpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHByb21pc2Ugb2YgT2JqZWN0LnZhbHVlcyh0aGlzLl9wcm94aWVkX2NhbGxiYWNrcykpXHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZS5yZWplY3QoXCJkaXNjb25uZWN0ZWRcIik7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX2NhbGxiYWNrcyA9IHt9O1xyXG5cclxuICAgICAgICAgICAgICAgIHRoaXMuX2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5vbl9kaXNjb25uZWN0ZWQoKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIHJlZ2lzdGVyX21ldGhvZDxSPihtZXRob2Q6ICguLi5hcmdzOiBhbnlbXSkgPT4gUHJvbWlzZTxSPiB8IHN0cmluZykge1xyXG4gICAgICAgICAgICAgICAgbGV0IG1ldGhvZF9uYW1lOiBzdHJpbmc7XHJcbiAgICAgICAgICAgICAgICBpZih0eXBlb2YgbWV0aG9kID09PSBcImZ1bmN0aW9uXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlZ2lzdGVyaW5nIG1ldGhvZCBwcm94eSBmb3IgJXNcIiksIG1ldGhvZC5uYW1lKTtcclxuICAgICAgICAgICAgICAgICAgICBtZXRob2RfbmFtZSA9IG1ldGhvZC5uYW1lO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlZ2lzdGVyaW5nIG1ldGhvZCBwcm94eSBmb3IgJXNcIiksIG1ldGhvZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kX25hbWUgPSBtZXRob2Q7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIXRoaXNbbWV0aG9kX25hbWVdKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwibWV0aG9kIGlzIG1pc3NpbmcgaW4gY3VycmVudCBvYmplY3RcIjtcclxuXHJcbiAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX21ldGhvZHNbbWV0aG9kX25hbWVdID0gdGhpc1ttZXRob2RfbmFtZV07XHJcbiAgICAgICAgICAgICAgICBpZighdGhpcy5fbG9jYWwpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzW21ldGhvZF9uYW1lXSA9ICguLi5hcmdzOiBhbnlbXSkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighdGhpcy5fY29ubmVjdGVkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KFwibm90IGNvbm5lY3RlZFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHByb3h5X2NhbGxiYWNrID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvbWlzZV9pZDogdXVpZHY0KClcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBhcyBNZXRob2RQcm94eUNhbGxiYWNrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX2NhbGxiYWNrc1twcm94eV9jYWxsYmFjay5wcm9taXNlX2lkXSA9IHByb3h5X2NhbGxiYWNrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBwcm94eV9jYWxsYmFjay5wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJveHlfY2FsbGJhY2sucmVzb2x2ZSA9IHJlc29sdmU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm94eV9jYWxsYmFjay5yZWplY3QgPSByZWplY3Q7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5faXBjX2NoYW5uZWwuc2VuZF9tZXNzYWdlKFwiaW52b2tlXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VfaWQ6IHByb3h5X2NhbGxiYWNrLnByb21pc2VfaWQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmd1bWVudHM6IFsuLi5hcmdzXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZF9uYW1lOiBtZXRob2RfbmFtZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGFzIE1ldGhvZFByb3h5SW52b2tlRGF0YSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBwcm94eV9jYWxsYmFjay5wcm9taXNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfaGFuZGxlX21lc3NhZ2UocmVtb3RlX2lkOiBzdHJpbmcsIGJvcmFkY2FzdDogYm9vbGVhbiwgbWVzc2FnZTogQ2hhbm5lbE1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIGlmKG1lc3NhZ2UudHlwZSA9PT0gXCJmaW5hbGl6ZVwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlX2ZpbmFsaXplKCk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYobWVzc2FnZS50eXBlID09PSBcImluaXRpYWxpemVcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2hhbmRsZV9yZW1vdGVfY2FsbGJhY2socmVtb3RlX2lkKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZihtZXNzYWdlLnR5cGUgPT09IFwiaW52b2tlXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9oYW5kbGVfaW52b2tlKG1lc3NhZ2UuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYobWVzc2FnZS50eXBlID09PSBcInJlc3VsdFwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlX3Jlc3VsdChtZXNzYWdlLmRhdGEpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9oYW5kbGVfZmluYWxpemUoKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLm9uX2Rpc2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fY29ubmVjdGVkID0gZmFsc2U7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX2hhbmRsZV9yZW1vdGVfY2FsbGJhY2socmVtb3RlX2lkOiBzdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9pcGNfY2hhbm5lbC50YXJnZXRfaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZih0aGlzLl9zbGF2ZSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJpbml0aWFsaXplIHdyb25nIHN0YXRlIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC50YXJnZXRfaWQgPSByZW1vdGVfaWQ7IC8qIG5vdyB3ZSdyZSBhYmxlIHRvIHNlbmQgbWVzc2FnZXMgKi9cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcImluaXRpYWxpemVcIiwgdHJ1ZSk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9zbGF2ZSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJpbml0aWFsaXplIHdyb25nIHN0YXRlIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5fY29ubmVjdGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfc2VuZF9yZXN1bHQocHJvbWlzZV9pZDogc3RyaW5nLCBzdWNjZXNzOiBib29sZWFuLCBtZXNzYWdlOiBhbnkpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcInJlc3VsdFwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZV9pZDogcHJvbWlzZV9pZCxcclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQ6IG1lc3NhZ2UsXHJcbiAgICAgICAgICAgICAgICAgICAgc3VjY2Vzczogc3VjY2Vzc1xyXG4gICAgICAgICAgICAgICAgfSBhcyBNZXRob2RQcm94eVJlc3VsdERhdGEpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9oYW5kbGVfaW52b2tlKGRhdGE6IE1ldGhvZFByb3h5SW52b2tlRGF0YSkge1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy5fcHJveGllZF9tZXRob2RzW2RhdGEubWV0aG9kX25hbWVdKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwid2UgY291bGQgbm90IGludm9rZSBhIGxvY2FsIHByb3hpZWQgbWV0aG9kIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzW2RhdGEubWV0aG9kX25hbWVdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2VuZF9yZXN1bHQoZGF0YS5wcm9taXNlX2lkLCBmYWxzZSwgXCJtaXNzaW5nIG1ldGhvZFwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiSW52b2tpbmcgbWV0aG9kICVzIHdpdGggYXJndW1lbnRzOiAlb1wiKSwgZGF0YS5tZXRob2RfbmFtZSwgZGF0YS5hcmd1bWVudHMpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBwcm9taXNlID0gdGhpc1tkYXRhLm1ldGhvZF9uYW1lXSguLi5kYXRhLmFyZ3VtZW50cyk7XHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZS50aGVuKHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZXN1bHQ6ICVvXCIpLCByZXN1bHQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9zZW5kX3Jlc3VsdChkYXRhLnByb21pc2VfaWQsIHRydWUsIHJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9zZW5kX3Jlc3VsdChkYXRhLnByb21pc2VfaWQsIGZhbHNlLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2VuZF9yZXN1bHQoZGF0YS5wcm9taXNlX2lkLCBmYWxzZSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfaGFuZGxlX3Jlc3VsdChkYXRhOiBNZXRob2RQcm94eVJlc3VsdERhdGEpIHtcclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9wcm94aWVkX2NhbGxiYWNrc1tkYXRhLnByb21pc2VfaWRdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiUmVjZWl2ZWQgcHJveHkgbWV0aG9kIHJlc3VsdCBmb3IgdW5rbm93biBwcm9taXNlXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBjb25zdCBjYWxsYmFjayA9IHRoaXMuX3Byb3hpZWRfY2FsbGJhY2tzW2RhdGEucHJvbWlzZV9pZF07XHJcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fcHJveGllZF9jYWxsYmFja3NbZGF0YS5wcm9taXNlX2lkXTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZihkYXRhLnN1Y2Nlc3MpXHJcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sucmVzb2x2ZShkYXRhLnJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sucmVqZWN0KGRhdGEucmVzdWx0KTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZ2VuZXJhdGVfY29ubmVjdF9wYXJhbWV0ZXJzKCkgOiBNZXRob2RQcm94eUNvbm5lY3RQYXJhbWV0ZXJzIHtcclxuICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwib25seSBtYXN0ZXJzIGNhbiBnZW5lcmF0ZSBjb25uZWN0IHBhcmFtZXRlcnMhXCI7XHJcbiAgICAgICAgICAgICAgICBpZighdGhpcy5faXBjX2NoYW5uZWwpXHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJwbGVhc2UgY2FsbCBzZXR1cCgpIGJlZm9yZVwiO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY2hhbm5lbF9pZDogdGhpcy5faXBjX2NoYW5uZWwuY2hhbm5lbF9pZCxcclxuICAgICAgICAgICAgICAgICAgICBjbGllbnRfaWQ6IHRoaXMuaXBjX2hhbmRsZXIuZ2V0X2xvY2FsX2FkZHJlc3MoKVxyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaXNfc2xhdmUoKSB7IHJldHVybiB0aGlzLl9sb2NhbCB8fCB0aGlzLl9zbGF2ZTsgfSAvKiB0aGUgcG9wb3V0IG1vZGFsICovXHJcbiAgICAgICAgICAgIGlzX21hc3RlcigpIHsgcmV0dXJuIHRoaXMuX2xvY2FsIHx8ICF0aGlzLl9zbGF2ZTsgfSAvKiB0aGUgaG9zdCAodGVhd2ViIGFwcGxpY2F0aW9uKSAqL1xyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGFic3RyYWN0IG9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICBwcm90ZWN0ZWQgYWJzdHJhY3Qgb25fZGlzY29ubmVjdGVkKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGxldCBoYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXI7XHJcbiAgICBsZXQgY29ubmVjdF9oYW5kbGVyOiBjb25uZWN0LkNvbm5lY3RIYW5kbGVyO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBzZXR1cCgpIHtcclxuICAgICAgICBpZighc3VwcG9ydGVkKCkpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaGFuZGxlciA9IG5ldyBCcm9hZGNhc3RDaGFubmVsSVBDKCk7XHJcbiAgICAgICAgaGFuZGxlci5zZXR1cCgpO1xyXG5cclxuICAgICAgICBjb25uZWN0X2hhbmRsZXIgPSBuZXcgY29ubmVjdC5Db25uZWN0SGFuZGxlcihoYW5kbGVyKTtcclxuICAgICAgICBjb25uZWN0X2hhbmRsZXIuc2V0dXAoKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gZ2V0X2hhbmRsZXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIGhhbmRsZXI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGdldF9jb25uZWN0X2hhbmRsZXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIGNvbm5lY3RfaGFuZGxlcjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc3VwcG9ydGVkKCkge1xyXG4gICAgICAgIC8qIGlvcyBkb2VzIG5vdCBzdXBwb3J0IHRoaXMgKi9cclxuICAgICAgICByZXR1cm4gdHlwZW9mKHdpbmRvdy5Ccm9hZGNhc3RDaGFubmVsKSAhPT0gXCJ1bmRlZmluZWRcIjtcclxuICAgIH1cclxufSIsImV4cG9ydCBlbnVtIEtleUNvZGUge1xyXG4gICAgS0VZX0NBTkNFTCA9IDMsXHJcbiAgICBLRVlfSEVMUCA9IDYsXHJcbiAgICBLRVlfQkFDS19TUEFDRSA9IDgsXHJcbiAgICBLRVlfVEFCID0gOSxcclxuICAgIEtFWV9DTEVBUiA9IDEyLFxyXG4gICAgS0VZX1JFVFVSTiA9IDEzLFxyXG4gICAgS0VZX0VOVEVSID0gMTQsXHJcbiAgICBLRVlfU0hJRlQgPSAxNixcclxuICAgIEtFWV9DT05UUk9MID0gMTcsXHJcbiAgICBLRVlfQUxUID0gMTgsXHJcbiAgICBLRVlfUEFVU0UgPSAxOSxcclxuICAgIEtFWV9DQVBTX0xPQ0sgPSAyMCxcclxuICAgIEtFWV9FU0NBUEUgPSAyNyxcclxuICAgIEtFWV9TUEFDRSA9IDMyLFxyXG4gICAgS0VZX1BBR0VfVVAgPSAzMyxcclxuICAgIEtFWV9QQUdFX0RPV04gPSAzNCxcclxuICAgIEtFWV9FTkQgPSAzNSxcclxuICAgIEtFWV9IT01FID0gMzYsXHJcbiAgICBLRVlfTEVGVCA9IDM3LFxyXG4gICAgS0VZX1VQID0gMzgsXHJcbiAgICBLRVlfUklHSFQgPSAzOSxcclxuICAgIEtFWV9ET1dOID0gNDAsXHJcbiAgICBLRVlfUFJJTlRTQ1JFRU4gPSA0NCxcclxuICAgIEtFWV9JTlNFUlQgPSA0NSxcclxuICAgIEtFWV9ERUxFVEUgPSA0NixcclxuICAgIEtFWV8wID0gNDgsXHJcbiAgICBLRVlfMSA9IDQ5LFxyXG4gICAgS0VZXzIgPSA1MCxcclxuICAgIEtFWV8zID0gNTEsXHJcbiAgICBLRVlfNCA9IDUyLFxyXG4gICAgS0VZXzUgPSA1MyxcclxuICAgIEtFWV82ID0gNTQsXHJcbiAgICBLRVlfNyA9IDU1LFxyXG4gICAgS0VZXzggPSA1NixcclxuICAgIEtFWV85ID0gNTcsXHJcbiAgICBLRVlfU0VNSUNPTE9OID0gNTksXHJcbiAgICBLRVlfRVFVQUxTID0gNjEsXHJcbiAgICBLRVlfQSA9IDY1LFxyXG4gICAgS0VZX0IgPSA2NixcclxuICAgIEtFWV9DID0gNjcsXHJcbiAgICBLRVlfRCA9IDY4LFxyXG4gICAgS0VZX0UgPSA2OSxcclxuICAgIEtFWV9GID0gNzAsXHJcbiAgICBLRVlfRyA9IDcxLFxyXG4gICAgS0VZX0ggPSA3MixcclxuICAgIEtFWV9JID0gNzMsXHJcbiAgICBLRVlfSiA9IDc0LFxyXG4gICAgS0VZX0sgPSA3NSxcclxuICAgIEtFWV9MID0gNzYsXHJcbiAgICBLRVlfTSA9IDc3LFxyXG4gICAgS0VZX04gPSA3OCxcclxuICAgIEtFWV9PID0gNzksXHJcbiAgICBLRVlfUCA9IDgwLFxyXG4gICAgS0VZX1EgPSA4MSxcclxuICAgIEtFWV9SID0gODIsXHJcbiAgICBLRVlfUyA9IDgzLFxyXG4gICAgS0VZX1QgPSA4NCxcclxuICAgIEtFWV9VID0gODUsXHJcbiAgICBLRVlfViA9IDg2LFxyXG4gICAgS0VZX1cgPSA4NyxcclxuICAgIEtFWV9YID0gODgsXHJcbiAgICBLRVlfWSA9IDg5LFxyXG4gICAgS0VZX1ogPSA5MCxcclxuICAgIEtFWV9MRUZUX0NNRCA9IDkxLFxyXG4gICAgS0VZX1JJR0hUX0NNRCA9IDkzLFxyXG4gICAgS0VZX0NPTlRFWFRfTUVOVSA9IDkzLFxyXG4gICAgS0VZX05VTVBBRDAgPSA5NixcclxuICAgIEtFWV9OVU1QQUQxID0gOTcsXHJcbiAgICBLRVlfTlVNUEFEMiA9IDk4LFxyXG4gICAgS0VZX05VTVBBRDMgPSA5OSxcclxuICAgIEtFWV9OVU1QQUQ0ID0gMTAwLFxyXG4gICAgS0VZX05VTVBBRDUgPSAxMDEsXHJcbiAgICBLRVlfTlVNUEFENiA9IDEwMixcclxuICAgIEtFWV9OVU1QQUQ3ID0gMTAzLFxyXG4gICAgS0VZX05VTVBBRDggPSAxMDQsXHJcbiAgICBLRVlfTlVNUEFEOSA9IDEwNSxcclxuICAgIEtFWV9NVUxUSVBMWSA9IDEwNixcclxuICAgIEtFWV9BREQgPSAxMDcsXHJcbiAgICBLRVlfU0VQQVJBVE9SID0gMTA4LFxyXG4gICAgS0VZX1NVQlRSQUNUID0gMTA5LFxyXG4gICAgS0VZX0RFQ0lNQUwgPSAxMTAsXHJcbiAgICBLRVlfRElWSURFID0gMTExLFxyXG4gICAgS0VZX0YxID0gMTEyLFxyXG4gICAgS0VZX0YyID0gMTEzLFxyXG4gICAgS0VZX0YzID0gMTE0LFxyXG4gICAgS0VZX0Y0ID0gMTE1LFxyXG4gICAgS0VZX0Y1ID0gMTE2LFxyXG4gICAgS0VZX0Y2ID0gMTE3LFxyXG4gICAgS0VZX0Y3ID0gMTE4LFxyXG4gICAgS0VZX0Y4ID0gMTE5LFxyXG4gICAgS0VZX0Y5ID0gMTIwLFxyXG4gICAgS0VZX0YxMCA9IDEyMSxcclxuICAgIEtFWV9GMTEgPSAxMjIsXHJcbiAgICBLRVlfRjEyID0gMTIzLFxyXG4gICAgS0VZX0YxMyA9IDEyNCxcclxuICAgIEtFWV9GMTQgPSAxMjUsXHJcbiAgICBLRVlfRjE1ID0gMTI2LFxyXG4gICAgS0VZX0YxNiA9IDEyNyxcclxuICAgIEtFWV9GMTcgPSAxMjgsXHJcbiAgICBLRVlfRjE4ID0gMTI5LFxyXG4gICAgS0VZX0YxOSA9IDEzMCxcclxuICAgIEtFWV9GMjAgPSAxMzEsXHJcbiAgICBLRVlfRjIxID0gMTMyLFxyXG4gICAgS0VZX0YyMiA9IDEzMyxcclxuICAgIEtFWV9GMjMgPSAxMzQsXHJcbiAgICBLRVlfRjI0ID0gMTM1LFxyXG4gICAgS0VZX05VTV9MT0NLID0gMTQ0LFxyXG4gICAgS0VZX1NDUk9MTF9MT0NLID0gMTQ1LFxyXG4gICAgS0VZX0NPTU1BID0gMTg4LFxyXG4gICAgS0VZX1BFUklPRCA9IDE5MCxcclxuICAgIEtFWV9TTEFTSCA9IDE5MSxcclxuICAgIEtFWV9CQUNLX1FVT1RFID0gMTkyLFxyXG4gICAgS0VZX09QRU5fQlJBQ0tFVCA9IDIxOSxcclxuICAgIEtFWV9CQUNLX1NMQVNIID0gMjIwLFxyXG4gICAgS0VZX0NMT1NFX0JSQUNLRVQgPSAyMjEsXHJcbiAgICBLRVlfUVVPVEUgPSAyMjIsXHJcbiAgICBLRVlfTUVUQSA9IDIyNFxyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHBwdCB7XHJcbiAgICBleHBvcnQgZW51bSBFdmVudFR5cGUge1xyXG4gICAgICAgIEtFWV9QUkVTUyxcclxuICAgICAgICBLRVlfUkVMRUFTRSxcclxuICAgICAgICBLRVlfVFlQRURcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZW51bSBTcGVjaWFsS2V5IHtcclxuICAgICAgICBDVFJMLFxyXG4gICAgICAgIFdJTkRPV1MsXHJcbiAgICAgICAgU0hJRlQsXHJcbiAgICAgICAgQUxUXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBLZXlEZXNjcmlwdG9yIHtcclxuICAgICAgICBrZXlfY29kZTogc3RyaW5nO1xyXG5cclxuICAgICAgICBrZXlfY3RybDogYm9vbGVhbjtcclxuICAgICAgICBrZXlfd2luZG93czogYm9vbGVhbjtcclxuICAgICAgICBrZXlfc2hpZnQ6IGJvb2xlYW47XHJcbiAgICAgICAga2V5X2FsdDogYm9vbGVhbjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIEtleUV2ZW50IGV4dGVuZHMgS2V5RGVzY3JpcHRvciB7XHJcbiAgICAgICAgcmVhZG9ubHkgdHlwZTogRXZlbnRUeXBlO1xyXG5cclxuICAgICAgICByZWFkb25seSBrZXk6IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIEtleUhvb2sgZXh0ZW5kcyBLZXlEZXNjcmlwdG9yIHtcclxuICAgICAgICBjYW5jZWw6IGJvb2xlYW47XHJcblxyXG5cclxuICAgICAgICBjYWxsYmFja19wcmVzczogKCkgPT4gYW55O1xyXG4gICAgICAgIGNhbGxiYWNrX3JlbGVhc2U6ICgpID0+IGFueTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24ga2V5X2Rlc2NyaXB0aW9uKGtleTogS2V5RGVzY3JpcHRvcikge1xyXG4gICAgICAgIGxldCByZXN1bHQgPSBcIlwiO1xyXG4gICAgICAgIGlmKGtleS5rZXlfc2hpZnQpXHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiArIFwiICsgdHIoXCJTaGlmdFwiKTtcclxuICAgICAgICBpZihrZXkua2V5X2FsdClcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiICsgXCIgKyB0cihcIkFsdFwiKTtcclxuICAgICAgICBpZihrZXkua2V5X2N0cmwpXHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiArIFwiICsgdHIoXCJDVFJMXCIpO1xyXG4gICAgICAgIGlmKGtleS5rZXlfd2luZG93cylcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiICsgXCIgKyB0cihcIldpblwiKTtcclxuXHJcbiAgICAgICAgaWYoIXJlc3VsdCAmJiAha2V5LmtleV9jb2RlKVxyXG4gICAgICAgICAgICByZXR1cm4gdHIoXCJ1bnNldFwiKTtcclxuXHJcbiAgICAgICAgaWYoa2V5LmtleV9jb2RlKVxyXG4gICAgICAgICAgICByZXN1bHQgKz0gXCIgKyBcIiArIGtleS5rZXlfY29kZTtcclxuICAgICAgICByZXR1cm4gcmVzdWx0LnN1YnN0cigzKTtcclxuICAgIH1cclxufSIsImltcG9ydCB7XHJcbiAgICBDbGllbnROYW1lSW5mbyxcclxuICAgIENvbW1hbmRSZXN1bHQsXHJcbiAgICBFcnJvcklELFxyXG4gICAgUGxheWxpc3QsIFBsYXlsaXN0SW5mbywgUGxheWxpc3RTb25nLFxyXG4gICAgUXVlcnlMaXN0LFxyXG4gICAgUXVlcnlMaXN0RW50cnksIFNlcnZlckdyb3VwQ2xpZW50XHJcbn0gZnJvbSBcIi4vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7Q2hhbm5lbEVudHJ5fSBmcm9tIFwiLi4vY2hhbm5lbC10cmVlL2NoYW5uZWxcIjtcclxuaW1wb3J0IHtDaGF0VHlwZX0gZnJvbSBcIi4uL3VpL2ZyYW1lcy9jaGF0XCI7XHJcbmltcG9ydCB7Q2xpZW50RW50cnl9IGZyb20gXCIuLi9jaGFubmVsLXRyZWUvY2xpZW50XCI7XHJcbmltcG9ydCB7QWJzdHJhY3RDb21tYW5kSGFuZGxlciwgU2VydmVyQ29tbWFuZCwgU2luZ2xlQ29tbWFuZEhhbmRsZXJ9IGZyb20gXCIuL0Nvbm5lY3Rpb25CYXNlXCI7XHJcbmltcG9ydCB7bG9nLCBMb2dDYXRlZ29yeX0gZnJvbSBcIi4uL2xvZ1wiO1xyXG5cclxuZXhwb3J0IGNsYXNzIENvbW1hbmRIZWxwZXIgZXh0ZW5kcyBBYnN0cmFjdENvbW1hbmRIYW5kbGVyIHtcclxuICAgIHByaXZhdGUgX3dob19hbV9pOiBhbnk7XHJcbiAgICBwcml2YXRlIF9hd2FpdGVyc191bmlxdWVfaWRzOiB7W3VuaXF1ZV9pZDogc3RyaW5nXTooKHJlc29sdmVkOiBDbGllbnROYW1lSW5mbykgPT4gYW55KVtdfSA9IHt9O1xyXG4gICAgcHJpdmF0ZSBfYXdhaXRlcnNfdW5pcXVlX2RiaWQ6IHtbZGF0YWJhc2VfaWQ6IG51bWJlcl06KChyZXNvbHZlZDogQ2xpZW50TmFtZUluZm8pID0+IGFueSlbXX0gPSB7fTtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uKSB7XHJcbiAgICAgICAgc3VwZXIoY29ubmVjdGlvbik7XHJcblxyXG4gICAgICAgIHRoaXMudm9sYXRpbGVfaGFuZGxlcl9ib3NzID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5pZ25vcmVfY29uc3VtZWQgPSB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIGluaXRpYWxpemUoKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLmNvbW1hbmRfaGFuZGxlcl9ib3NzKCkucmVnaXN0ZXJfaGFuZGxlcih0aGlzKTtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIGlmKHRoaXMuY29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICBjb25zdCBoYm9zcyA9IHRoaXMuY29ubmVjdGlvbi5jb21tYW5kX2hhbmRsZXJfYm9zcygpO1xyXG4gICAgICAgICAgICBoYm9zcyAmJiBoYm9zcy51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9pZHMgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgaGFuZGxlX2NvbW1hbmQoY29tbWFuZDogU2VydmVyQ29tbWFuZCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIGlmKGNvbW1hbmQuY29tbWFuZCA9PSBcIm5vdGlmeWNsaWVudG5hbWVmcm9tdWlkXCIpXHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlX25vdGlmeWNsaWVudG5hbWVmcm9tdWlkKGNvbW1hbmQuYXJndW1lbnRzKTtcclxuICAgICAgICBpZihjb21tYW5kLmNvbW1hbmQgPT0gXCJub3RpZnljbGllbnRnZXRuYW1lZnJvbWRiaWRcIilcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVfbm90aWZ5Y2xpZW50Z2V0bmFtZWZyb21kYmlkKGNvbW1hbmQuYXJndW1lbnRzKTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICBqb2luQ2hhbm5lbChjaGFubmVsOiBDaGFubmVsRW50cnksIHBhc3N3b3JkPzogc3RyaW5nKSA6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwiY2xpZW50bW92ZVwiLCB7XHJcbiAgICAgICAgICAgIFwiY2xpZFwiOiB0aGlzLmNvbm5lY3Rpb24uY2xpZW50LmdldENsaWVudElkKCksXHJcbiAgICAgICAgICAgIFwiY2lkXCI6IGNoYW5uZWwuZ2V0Q2hhbm5lbElkKCksXHJcbiAgICAgICAgICAgIFwiY3B3XCI6IHBhc3N3b3JkIHx8IFwiXCJcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzZW5kTWVzc2FnZShtZXNzYWdlOiBzdHJpbmcsIHR5cGU6IENoYXRUeXBlLCB0YXJnZXQ/OiBDaGFubmVsRW50cnkgfCBDbGllbnRFbnRyeSkgOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ+IHtcclxuICAgICAgICBpZih0eXBlID09IENoYXRUeXBlLlNFUlZFUilcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJzZW5kdGV4dG1lc3NhZ2VcIiwge1widGFyZ2V0bW9kZVwiOiAzLCBcInRhcmdldFwiOiAwLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlID09IENoYXRUeXBlLkNIQU5ORUwpXHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwic2VuZHRleHRtZXNzYWdlXCIsIHtcInRhcmdldG1vZGVcIjogMiwgXCJ0YXJnZXRcIjogKHRhcmdldCBhcyBDaGFubmVsRW50cnkpLmdldENoYW5uZWxJZCgpLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlID09IENoYXRUeXBlLkNMSUVOVClcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJzZW5kdGV4dG1lc3NhZ2VcIiwge1widGFyZ2V0bW9kZVwiOiAxLCBcInRhcmdldFwiOiAodGFyZ2V0IGFzIENsaWVudEVudHJ5KS5jbGllbnRJZCgpLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICB9XHJcblxyXG4gICAgdXBkYXRlQ2xpZW50KGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKSA6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xyXG4gICAgICAgIGxldCBkYXRhID0ge307XHJcbiAgICAgICAgZGF0YVtrZXldID0gdmFsdWU7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJjbGllbnR1cGRhdGVcIiwgZGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgaW5mb19mcm9tX3VpZCguLi5fdW5pcXVlX2lkczogc3RyaW5nW10pIDogUHJvbWlzZTxDbGllbnROYW1lSW5mb1tdPiB7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2U6IENsaWVudE5hbWVJbmZvW10gPSBbXTtcclxuICAgICAgICBjb25zdCByZXF1ZXN0ID0gW107XHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2lkcyA9IG5ldyBTZXQoX3VuaXF1ZV9pZHMpO1xyXG4gICAgICAgIGlmKCF1bmlxdWVfaWRzLnNpemUpIHJldHVybiBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2lkX3Jlc29sdmVyczoge1t1bmlxdWVfaWQ6IHN0cmluZ106IChyZXNvbHZlZDogQ2xpZW50TmFtZUluZm8pID0+IGFueX0gPSB7fTtcclxuXHJcblxyXG4gICAgICAgIGZvcihjb25zdCB1bmlxdWVfaWQgb2YgdW5pcXVlX2lkcykge1xyXG4gICAgICAgICAgICByZXF1ZXN0LnB1c2goeydjbHVpZCc6IHVuaXF1ZV9pZH0pO1xyXG4gICAgICAgICAgICAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2lkc1t1bmlxdWVfaWRdIHx8ICh0aGlzLl9hd2FpdGVyc191bmlxdWVfaWRzW3VuaXF1ZV9pZF0gPSBbXSkpXHJcbiAgICAgICAgICAgICAgICAucHVzaCh1bmlxdWVfaWRfcmVzb2x2ZXJzW3VuaXF1ZV9pZF0gPSBpbmZvID0+IHJlc3BvbnNlLnB1c2goaW5mbykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImNsaWVudGdldG5hbWVmcm9tdWlkXCIsIHJlcXVlc3QpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBDb21tYW5kUmVzdWx0ICYmIGVycm9yLmlkID09IEVycm9ySUQuRU1QVFlfUkVTVUxUKSB7XHJcbiAgICAgICAgICAgICAgICAvKiBub3RoaW5nICovXHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0gZmluYWxseSB7XHJcbiAgICAgICAgICAgIC8qIGNsZWFudXAgKi9cclxuICAgICAgICAgICAgZm9yKGNvbnN0IHVuaXF1ZV9pZCBvZiBPYmplY3Qua2V5cyh1bmlxdWVfaWRfcmVzb2x2ZXJzKSlcclxuICAgICAgICAgICAgICAgICh0aGlzLl9hd2FpdGVyc191bmlxdWVfaWRzW3VuaXF1ZV9pZF0gfHwgW10pLnJlbW92ZSh1bmlxdWVfaWRfcmVzb2x2ZXJzW3VuaXF1ZV9pZF0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgaGFuZGxlX25vdGlmeWNsaWVudGdldG5hbWVmcm9tZGJpZChqc29uOiBhbnlbXSkge1xyXG4gICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm86IENsaWVudE5hbWVJbmZvID0ge1xyXG4gICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZDogZW50cnlbXCJjbHVpZFwiXSxcclxuICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbG5hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IHBhcnNlSW50KGVudHJ5W1wiY2xkYmlkXCJdKVxyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgZnVuY3Rpb25zID0gdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbaW5mby5jbGllbnRfZGF0YWJhc2VfaWRdIHx8IFtdO1xyXG4gICAgICAgICAgICBkZWxldGUgdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbaW5mby5jbGllbnRfZGF0YWJhc2VfaWRdO1xyXG5cclxuICAgICAgICAgICAgZm9yKGNvbnN0IGZuIG9mIGZ1bmN0aW9ucylcclxuICAgICAgICAgICAgICAgIGZuKGluZm8pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBpbmZvX2Zyb21fY2xkYmlkKC4uLl9jbGRiaWQ6IG51bWJlcltdKSA6IFByb21pc2U8Q2xpZW50TmFtZUluZm9bXT4ge1xyXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlOiBDbGllbnROYW1lSW5mb1tdID0gW107XHJcbiAgICAgICAgY29uc3QgcmVxdWVzdCA9IFtdO1xyXG4gICAgICAgIGNvbnN0IHVuaXF1ZV9jbGRiaWQgPSBuZXcgU2V0KF9jbGRiaWQpO1xyXG4gICAgICAgIGlmKCF1bmlxdWVfY2xkYmlkLnNpemUpIHJldHVybiBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2NsZGJpZF9yZXNvbHZlcnM6IHtbZGJpZDogbnVtYmVyXTogKHJlc29sdmVkOiBDbGllbnROYW1lSW5mbykgPT4gYW55fSA9IHt9O1xyXG5cclxuXHJcbiAgICAgICAgZm9yKGNvbnN0IGNsZGJpZCBvZiB1bmlxdWVfY2xkYmlkKSB7XHJcbiAgICAgICAgICAgIHJlcXVlc3QucHVzaCh7J2NsZGJpZCc6IGNsZGJpZH0pO1xyXG4gICAgICAgICAgICAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbY2xkYmlkXSB8fCAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbY2xkYmlkXSA9IFtdKSlcclxuICAgICAgICAgICAgICAgIC5wdXNoKHVuaXF1ZV9jbGRiaWRfcmVzb2x2ZXJzW2NsZGJpZF0gPSBpbmZvID0+IHJlc3BvbnNlLnB1c2goaW5mbykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImNsaWVudGdldG5hbWVmcm9tZGJpZFwiLCByZXF1ZXN0KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdCAmJiBlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgLyogbm90aGluZyAqL1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGZpbmFsbHkge1xyXG4gICAgICAgICAgICAvKiBjbGVhbnVwICovXHJcbiAgICAgICAgICAgIGZvcihjb25zdCBjbGRiaWQgb2YgT2JqZWN0LmtleXModW5pcXVlX2NsZGJpZF9yZXNvbHZlcnMpKVxyXG4gICAgICAgICAgICAgICAgKHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9kYmlkW2NsZGJpZF0gfHwgW10pLnJlbW92ZSh1bmlxdWVfY2xkYmlkX3Jlc29sdmVyc1tjbGRiaWRdKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9ub3RpZnljbGllbnRuYW1lZnJvbXVpZChqc29uOiBhbnlbXSkge1xyXG4gICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm86IENsaWVudE5hbWVJbmZvID0ge1xyXG4gICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZDogZW50cnlbXCJjbHVpZFwiXSxcclxuICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbG5hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IHBhcnNlSW50KGVudHJ5W1wiY2xkYmlkXCJdKVxyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgZnVuY3Rpb25zID0gdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2lkc1tlbnRyeVtcImNsdWlkXCJdXSB8fCBbXTtcclxuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9pZHNbZW50cnlbXCJjbHVpZFwiXV07XHJcblxyXG4gICAgICAgICAgICBmb3IoY29uc3QgZm4gb2YgZnVuY3Rpb25zKVxyXG4gICAgICAgICAgICAgICAgZm4oaW5mbyk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcXVlcnlfbGlzdChzZXJ2ZXJfaWQ6IG51bWJlciA9IHVuZGVmaW5lZCkgOiBQcm9taXNlPFF1ZXJ5TGlzdD4ge1xyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxRdWVyeUxpc3Q+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBjb21tYW5kOiBcIm5vdGlmeXF1ZXJ5bGlzdFwiLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGNvbW1hbmQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGpzb24gPSBjb21tYW5kLmFyZ3VtZW50cztcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0ge30gYXMgUXVlcnlMaXN0O1xyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQuZmxhZ19hbGwgPSBqc29uWzBdW1wiZmxhZ19hbGxcIl07XHJcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LmZsYWdfb3duID0ganNvblswXVtcImZsYWdfb3duXCJdO1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdC5xdWVyaWVzID0gW107XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlbnRyeSA9IHt9IGFzIFF1ZXJ5TGlzdEVudHJ5O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkuYm91bmRlZF9zZXJ2ZXIgPSBwYXJzZUludChlbnRyeVtcImNsaWVudF9ib3VuZF9zZXJ2ZXJcIl0pO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkudXNlcm5hbWUgPSBlbnRyeVtcImNsaWVudF9sb2dpbl9uYW1lXCJdO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkudW5pcXVlX2lkID0gZW50cnlbXCJjbGllbnRfdW5pcXVlX2lkZW50aWZpZXJcIl07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQucXVlcmllcy5wdXNoKHJlbnRyeSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgIGxldCBkYXRhID0ge307XHJcbiAgICAgICAgICAgIGlmKHNlcnZlcl9pZCAhPT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICAgICAgZGF0YVtcInNlcnZlcl9pZFwiXSA9IHNlcnZlcl9pZDtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJxdWVyeWxpc3RcIiwgZGF0YSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVtb3ZlX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICByZXF1ZXN0X3BsYXlsaXN0X2xpc3QoKSA6IFByb21pc2U8UGxheWxpc3RbXT4ge1xyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IHNpbmdsZV9oYW5kbGVyOiBTaW5nbGVDb21tYW5kSGFuZGxlciA9IHtcclxuICAgICAgICAgICAgICAgIGNvbW1hbmQ6IFwibm90aWZ5cGxheWxpc3RsaXN0XCIsXHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QganNvbiA9IGNvbW1hbmQuYXJndW1lbnRzO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdDogUGxheWxpc3RbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2lkOiBwYXJzZUludChlbnRyeVtcInBsYXlsaXN0X2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF9ib3RfaWQ6IHBhcnNlSW50KGVudHJ5W1wicGxheWxpc3RfYm90X2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90aXRsZTogZW50cnlbXCJwbGF5bGlzdF90aXRsZVwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90eXBlOiBwYXJzZUludChlbnRyeVtcInBsYXlsaXN0X3R5cGVcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X293bmVyX2RiaWQ6IHBhcnNlSW50KGVudHJ5W1wicGxheWxpc3Rfb3duZXJfZGJpZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfbmFtZTogZW50cnlbXCJwbGF5bGlzdF9vd25lcl9uYW1lXCJdLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWVkZWRfcG93ZXJfbW9kaWZ5OiBwYXJzZUludChlbnRyeVtcIm5lZWRlZF9wb3dlcl9tb2RpZnlcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lZWRlZF9wb3dlcl9wZXJtaXNzaW9uX21vZGlmeTogcGFyc2VJbnQoZW50cnlbXCJuZWVkZWRfcG93ZXJfcGVybWlzc2lvbl9tb2RpZnlcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lZWRlZF9wb3dlcl9kZWxldGU6IHBhcnNlSW50KGVudHJ5W1wibmVlZGVkX3Bvd2VyX2RlbGV0ZVwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVlZGVkX3Bvd2VyX3NvbmdfYWRkOiBwYXJzZUludChlbnRyeVtcIm5lZWRlZF9wb3dlcl9zb25nX2FkZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVlZGVkX3Bvd2VyX3NvbmdfbW92ZTogcGFyc2VJbnQoZW50cnlbXCJuZWVkZWRfcG93ZXJfc29uZ19tb3ZlXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWVkZWRfcG93ZXJfc29uZ19yZW1vdmU6IHBhcnNlSW50KGVudHJ5W1wibmVlZGVkX3Bvd2VyX3NvbmdfcmVtb3ZlXCJdKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIkZhaWxlZCB0byBwYXJzZSBwbGF5bGlzdCBlbnRyeTogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZWdpc3Rlcl9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwicGxheWxpc3RsaXN0XCIpLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBDb21tYW5kUmVzdWx0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoZXJyb3IuaWQgPT0gRXJyb3JJRC5FTVBUWV9SRVNVTFQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShbXSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcGxheWxpc3Rfc29uZ3MocGxheWxpc3RfaWQ6IG51bWJlcikgOiBQcm9taXNlPFBsYXlsaXN0U29uZ1tdPiB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlwbGF5bGlzdHNvbmdsaXN0XCIsXHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QganNvbiA9IGNvbW1hbmQuYXJndW1lbnRzO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZihqc29uWzBdW1wicGxheWxpc3RfaWRcIl0gIT0gcGxheWxpc3RfaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5Lk5FVFdPUktJTkcsIHRyKFwiUmVjZWl2ZWQgaW52YWxpZCBub3RpZmljYXRpb24gZm9yIHBsYXlsaXN0IHNvbmdzXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBQbGF5bGlzdFNvbmdbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvbmdfaWQ6IHBhcnNlSW50KGVudHJ5W1wic29uZ19pZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29uZ19pbnZva2VyOiBlbnRyeVtcInNvbmdfaW52b2tlclwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3ByZXZpb3VzX3NvbmdfaWQ6IHBhcnNlSW50KGVudHJ5W1wic29uZ19wcmV2aW91c19zb25nX2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3VybDogZW50cnlbXCJzb25nX3VybFwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3VybF9sb2FkZXI6IGVudHJ5W1wic29uZ191cmxfbG9hZGVyXCJdLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX2xvYWRlZDogZW50cnlbXCJzb25nX2xvYWRlZFwiXSA9PSB0cnVlIHx8IGVudHJ5W1wic29uZ19sb2FkZWRcIl0gPT0gXCIxXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29uZ19tZXRhZGF0YTogZW50cnlbXCJzb25nX21ldGFkYXRhXCJdXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5Lk5FVFdPUktJTkcsIHRyKFwiRmFpbGVkIHRvIHBhcnNlIHBsYXlsaXN0IHNvbmcgZW50cnk6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcInBsYXlsaXN0c29uZ2xpc3RcIiwge3BsYXlsaXN0X2lkOiBwbGF5bGlzdF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKFtdKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmVxdWVzdF9wbGF5bGlzdF9jbGllbnRfbGlzdChwbGF5bGlzdF9pZDogbnVtYmVyKSA6IFByb21pc2U8bnVtYmVyW10+IHtcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBzaW5nbGVfaGFuZGxlcjogU2luZ2xlQ29tbWFuZEhhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBjb21tYW5kOiBcIm5vdGlmeXBsYXlsaXN0Y2xpZW50bGlzdFwiLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGNvbW1hbmQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGpzb24gPSBjb21tYW5kLmFyZ3VtZW50cztcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYoanNvblswXVtcInBsYXlsaXN0X2lkXCJdICE9IHBsYXlsaXN0X2lkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIlJlY2VpdmVkIGludmFsaWQgbm90aWZpY2F0aW9uIGZvciBwbGF5bGlzdCBjbGllbnRzXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBudW1iZXJbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbilcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2gocGFyc2VJbnQoZW50cnlbXCJjbGRiaWRcIl0pKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQuZmlsdGVyKGUgPT4gIWlzTmFOKGUpKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJwbGF5bGlzdGNsaWVudGxpc3RcIiwge3BsYXlsaXN0X2lkOiBwbGF5bGlzdF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQgJiYgZXJyb3IuaWQgPT0gRXJyb3JJRC5FTVBUWV9SRVNVTFQpIHtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKFtdKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIHJlcXVlc3RfY2xpZW50c19ieV9zZXJ2ZXJfZ3JvdXAoZ3JvdXBfaWQ6IG51bWJlcikgOiBQcm9taXNlPFNlcnZlckdyb3VwQ2xpZW50W10+IHtcclxuICAgICAgICAvL3NlcnZlcmdyb3VwY2xpZW50bGlzdCBzZ2lkPTJcclxuICAgICAgICAvL25vdGlmeXNlcnZlcmdyb3VwY2xpZW50bGlzdCBzZ2lkPTYgY2xkYmlkPTIgY2xpZW50X25pY2tuYW1lPVdvbHZlcmluREVWIGNsaWVudF91bmlxdWVfaWRlbnRpZmllcj14eGpuYzE0TG12VGsrTHlybThPT2VvNHRPcXc9XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPFNlcnZlckdyb3VwQ2xpZW50W10+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlzZXJ2ZXJncm91cGNsaWVudGxpc3RcIixcclxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uOiBjb21tYW5kID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoY29tbWFuZC5hcmd1bWVudHNbMF1bXCJzZ2lkXCJdICE9IGdyb3VwX2lkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIlJlY2VpdmVkIGludmFsaWQgbm90aWZpY2F0aW9uIGZvciBzZXJ2ZXIgZ3JvdXAgY2xpZW50IGxpc3RcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQ6IFNlcnZlckdyb3VwQ2xpZW50W10gPSBbXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yKGNvbnN0IGVudHJ5IG9mIGNvbW1hbmQuYXJndW1lbnRzKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudF9kYXRhYmFzZV9pZDogcGFyc2VJbnQoZW50cnlbXCJjbGRiaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbGllbnRfbmlja25hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZGVudGlmaWVyOiBlbnRyeVtcImNsaWVudF91bmlxdWVfaWRlbnRpZmllclwiXVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJGYWlsZWQgdG8gcGFyc2Ugc2VydmVyIGdyb3VwIGNsaWVudCBsaXN0OiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJmYWlsZWQgdG8gcGFyc2UgaW5mb1wiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZWdpc3Rlcl9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwic2VydmVyZ3JvdXBjbGllbnRsaXN0XCIsIHtzZ2lkOiBncm91cF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcGxheWxpc3RfaW5mbyhwbGF5bGlzdF9pZDogbnVtYmVyKSA6IFByb21pc2U8UGxheWxpc3RJbmZvPiB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlwbGF5bGlzdGluZm9cIixcclxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uOiBjb21tYW5kID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gY29tbWFuZC5hcmd1bWVudHNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb25bXCJwbGF5bGlzdF9pZFwiXSAhPSBwbGF5bGlzdF9pZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJSZWNlaXZlZCBpbnZhbGlkIG5vdGlmaWNhdGlvbiBmb3IgcGxheWxpc3QgaW5mb1wiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2lkOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfdGl0bGU6IGpzb25bXCJwbGF5bGlzdF90aXRsZVwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2Rlc2NyaXB0aW9uOiBqc29uW1wicGxheWxpc3RfZGVzY3JpcHRpb25cIl0sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90eXBlOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfdHlwZVwiXSksXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfZGJpZDogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X293bmVyX2RiaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfbmFtZToganNvbltcInBsYXlsaXN0X293bmVyX25hbWVcIl0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfZmxhZ19kZWxldGVfcGxheWVkOiBqc29uW1wicGxheWxpc3RfZmxhZ19kZWxldGVfcGxheWVkXCJdID09IHRydWUgfHwganNvbltcInBsYXlsaXN0X2ZsYWdfZGVsZXRlX3BsYXllZFwiXSA9PSBcIjFcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2ZsYWdfZmluaXNoZWQ6IGpzb25bXCJwbGF5bGlzdF9mbGFnX2ZpbmlzaGVkXCJdID09IHRydWUgfHwganNvbltcInBsYXlsaXN0X2ZsYWdfZmluaXNoZWRcIl0gPT0gXCIxXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF9yZXBsYXlfbW9kZTogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X3JlcGxheV9tb2RlXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZDogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZFwiXSksXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfbWF4X3NvbmdzOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfbWF4X3NvbmdzXCJdKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJGYWlsZWQgdG8gcGFyc2UgcGxheWxpc3QgaW5mbzogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiZmFpbGVkIHRvIHBhcnNlIGluZm9cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcInBsYXlsaXN0aW5mb1wiLCB7cGxheWxpc3RfaWQ6IHBsYXlsaXN0X2lkfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVtb3ZlX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBAZGVwcmVjYXRlZFxyXG4gICAgICogIEl0cyBqdXN0IGEgd29ya2Fyb3VuZCBmb3IgdGhlIHF1ZXJ5IG1hbmFnZW1lbnQuXHJcbiAgICAgKiAgVGhlcmUgaXMgbm8gZ2FyYW50ZSB0aGF0IHRoZSB3aG9hbWkgdHJpY2sgd2lsbCB3b3JrIGZvcmV2ZXJcclxuICAgICAqL1xyXG4gICAgY3VycmVudF92aXJ0dWFsX3NlcnZlcl9pZCgpIDogUHJvbWlzZTxudW1iZXI+IHtcclxuICAgICAgICBpZih0aGlzLl93aG9fYW1faSlcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShwYXJzZUludCh0aGlzLl93aG9fYW1faVtcInZpcnR1YWxzZXJ2ZXJfaWRcIl0pKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPG51bWJlcj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBzaW5nbGVfaGFuZGxlcjogU2luZ2xlQ29tbWFuZEhhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoY29tbWFuZC5jb21tYW5kICE9IFwiXCIgJiYgY29tbWFuZC5jb21tYW5kLmluZGV4T2YoXCI9XCIpID09IC0xKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3dob19hbV9pID0gY29tbWFuZC5hcmd1bWVudHNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShwYXJzZUludCh0aGlzLl93aG9fYW1faVtcInZpcnR1YWxzZXJ2ZXJfaWRcIl0pKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcIndob2FtaVwiKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZW1vdmVfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG4gICAgICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbn0iLCJpbXBvcnQge0Nvbm5lY3Rpb25IYW5kbGVyLCBDb25uZWN0aW9uU3RhdGV9IGZyb20gXCIuLi9Db25uZWN0aW9uSGFuZGxlclwiO1xyXG5pbXBvcnQge1NlcnZlckFkZHJlc3N9IGZyb20gXCIuLi9jaGFubmVsLXRyZWUvc2VydmVyXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7UmVjb3JkZXJQcm9maWxlfSBmcm9tIFwiLi4vdm9pY2UvUmVjb3JkZXJQcm9maWxlXCI7XHJcbmltcG9ydCB7Q29tbWFuZEhlbHBlcn0gZnJvbSBcIi4vQ29tbWFuZEhlbHBlclwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuaW1wb3J0IEhhbmRzaGFrZUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUhhbmRsZXI7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIENvbW1hbmRPcHRpb25zIHtcclxuICAgIGZsYWdzZXQ/OiBzdHJpbmdbXTsgLyogZGVmYXVsdDogW10gKi9cclxuICAgIHByb2Nlc3NfcmVzdWx0PzogYm9vbGVhbjsgLyogZGVmYXVsdDogdHJ1ZSAqL1xyXG5cclxuICAgIHRpbWVvdXQ/OiBudW1iZXIgLyogZGVmYXVsdDogMTAwMCAqLztcclxufVxyXG5leHBvcnQgY29uc3QgQ29tbWFuZE9wdGlvbkRlZmF1bHRzOiBDb21tYW5kT3B0aW9ucyA9IHtcclxuICAgIGZsYWdzZXQ6IFtdLFxyXG4gICAgcHJvY2Vzc19yZXN1bHQ6IHRydWUsXHJcbiAgICB0aW1lb3V0OiAxMDAwXHJcbn07XHJcblxyXG5leHBvcnQgdHlwZSBDb25uZWN0aW9uU3RhdGVMaXN0ZW5lciA9IChvbGRfc3RhdGU6IENvbm5lY3Rpb25TdGF0ZSwgbmV3X3N0YXRlOiBDb25uZWN0aW9uU3RhdGUpID0+IGFueTtcclxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiB7XHJcbiAgICByZWFkb25seSBjbGllbnQ6IENvbm5lY3Rpb25IYW5kbGVyO1xyXG4gICAgcmVhZG9ubHkgY29tbWFuZF9oZWxwZXI6IENvbW1hbmRIZWxwZXI7XHJcblxyXG4gICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGNsaWVudDogQ29ubmVjdGlvbkhhbmRsZXIpIHtcclxuICAgICAgICB0aGlzLmNsaWVudCA9IGNsaWVudDtcclxuXHJcbiAgICAgICAgdGhpcy5jb21tYW5kX2hlbHBlciA9IG5ldyBDb21tYW5kSGVscGVyKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qIHJlc29sdmVkIGFzIHNvb24gYSBjb25uZWN0aW9uIGhhcyBiZWVuIGVzdGFibGlzaGVkLiBUaGlzIGRvZXMgbm90IG1lYW5zIHRoYXQgdGhlIGF1dGhlbnRpY2F0aW9uIGhhZCB5ZXQgYmVlbiBkb25lISAqL1xyXG4gICAgYWJzdHJhY3QgY29ubmVjdChhZGRyZXNzOiBTZXJ2ZXJBZGRyZXNzLCBoYW5kc2hha2U6IEhhbmRzaGFrZUhhbmRsZXIsIHRpbWVvdXQ/OiBudW1iZXIpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICBhYnN0cmFjdCBjb25uZWN0ZWQoKSA6IGJvb2xlYW47XHJcbiAgICBhYnN0cmFjdCBkaXNjb25uZWN0KHJlYXNvbj86IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+O1xyXG5cclxuICAgIGFic3RyYWN0IHN1cHBvcnRfdm9pY2UoKSA6IGJvb2xlYW47XHJcbiAgICBhYnN0cmFjdCB2b2ljZV9jb25uZWN0aW9uKCkgOiB2b2ljZS5BYnN0cmFjdFZvaWNlQ29ubmVjdGlvbiB8IHVuZGVmaW5lZDtcclxuXHJcbiAgICBhYnN0cmFjdCBjb21tYW5kX2hhbmRsZXJfYm9zcygpIDogQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3M7XHJcbiAgICBhYnN0cmFjdCBzZW5kX2NvbW1hbmQoY29tbWFuZDogc3RyaW5nLCBkYXRhPzogYW55IHwgYW55W10sIG9wdGlvbnM/OiBDb21tYW5kT3B0aW9ucykgOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ+O1xyXG5cclxuICAgIGFic3RyYWN0IGdldCBvbmNvbm5lY3Rpb25zdGF0ZWNoYW5nZWQoKSA6IENvbm5lY3Rpb25TdGF0ZUxpc3RlbmVyO1xyXG4gICAgYWJzdHJhY3Qgc2V0IG9uY29ubmVjdGlvbnN0YXRlY2hhbmdlZChsaXN0ZW5lcjogQ29ubmVjdGlvblN0YXRlTGlzdGVuZXIpO1xyXG5cclxuICAgIGFic3RyYWN0IHJlbW90ZV9hZGRyZXNzKCkgOiBTZXJ2ZXJBZGRyZXNzOyAvKiBvbmx5IHZhbGlkIHdoZW4gY29ubmVjdGVkICovXHJcbiAgICBhYnN0cmFjdCBoYW5kc2hha2VfaGFuZGxlcigpIDogSGFuZHNoYWtlSGFuZGxlcjsgLyogb25seSB2YWxpZCB3aGVuIGNvbm5lY3RlZCAqL1xyXG5cclxuICAgIGFic3RyYWN0IHBpbmcoKSA6IHtcclxuICAgICAgICBuYXRpdmU6IG51bWJlcixcclxuICAgICAgICBqYXZhc2NyaXB0PzogbnVtYmVyXHJcbiAgICB9O1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHZvaWNlIHtcclxuICAgIGV4cG9ydCBlbnVtIFBsYXllclN0YXRlIHtcclxuICAgICAgICBQUkVCVUZGRVJJTkcsXHJcbiAgICAgICAgUExBWUlORyxcclxuICAgICAgICBCVUZGRVJJTkcsXHJcbiAgICAgICAgU1RPUFBJTkcsXHJcbiAgICAgICAgU1RPUFBFRFxyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCB0eXBlIExhdGVuY3lTZXR0aW5ncyA9IHtcclxuICAgICAgICBtaW5fYnVmZmVyOiBudW1iZXI7IC8qIG1pbGxpc2Vjb25kcyAqL1xyXG4gICAgICAgIG1heF9idWZmZXI6IG51bWJlcjsgLyogbWlsbGlzZWNvbmRzICovXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBWb2ljZUNsaWVudCB7XHJcbiAgICAgICAgY2xpZW50X2lkOiBudW1iZXI7XHJcblxyXG4gICAgICAgIGNhbGxiYWNrX3BsYXliYWNrOiAoKSA9PiBhbnk7XHJcbiAgICAgICAgY2FsbGJhY2tfc3RvcHBlZDogKCkgPT4gYW55O1xyXG5cclxuICAgICAgICBjYWxsYmFja19zdGF0ZV9jaGFuZ2VkOiAobmV3X3N0YXRlOiBQbGF5ZXJTdGF0ZSkgPT4gYW55O1xyXG5cclxuICAgICAgICBnZXRfc3RhdGUoKSA6IFBsYXllclN0YXRlO1xyXG5cclxuICAgICAgICBnZXRfdm9sdW1lKCkgOiBudW1iZXI7XHJcbiAgICAgICAgc2V0X3ZvbHVtZSh2b2x1bWU6IG51bWJlcikgOiB2b2lkO1xyXG5cclxuICAgICAgICBhYm9ydF9yZXBsYXkoKTtcclxuXHJcbiAgICAgICAgc3VwcG9ydF9sYXRlbmN5X3NldHRpbmdzKCkgOiBib29sZWFuO1xyXG5cclxuICAgICAgICByZXNldF9sYXRlbmN5X3NldHRpbmdzKCk7XHJcbiAgICAgICAgbGF0ZW5jeV9zZXR0aW5ncyhzZXR0aW5ncz86IExhdGVuY3lTZXR0aW5ncykgOiBMYXRlbmN5U2V0dGluZ3M7XHJcblxyXG4gICAgICAgIHN1cHBvcnRfZmx1c2goKSA6IGJvb2xlYW47XHJcbiAgICAgICAgZmx1c2goKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RWb2ljZUNvbm5lY3Rpb24ge1xyXG4gICAgICAgIHJlYWRvbmx5IGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbjtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgYWJzdHJhY3QgY29ubmVjdGVkKCkgOiBib29sZWFuO1xyXG4gICAgICAgIGFic3RyYWN0IGVuY29kaW5nX3N1cHBvcnRlZChjb2RlYzogbnVtYmVyKSA6IGJvb2xlYW47XHJcbiAgICAgICAgYWJzdHJhY3QgZGVjb2Rpbmdfc3VwcG9ydGVkKGNvZGVjOiBudW1iZXIpIDogYm9vbGVhbjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3QgcmVnaXN0ZXJfY2xpZW50KGNsaWVudF9pZDogbnVtYmVyKSA6IFZvaWNlQ2xpZW50O1xyXG4gICAgICAgIGFic3RyYWN0IGF2YWlsYWJsZV9jbGllbnRzKCkgOiBWb2ljZUNsaWVudFtdO1xyXG4gICAgICAgIGFic3RyYWN0IHVucmVnaXN0ZXJfY2xpZW50KGNsaWVudDogVm9pY2VDbGllbnQpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3Qgdm9pY2VfcmVjb3JkZXIoKSA6IFJlY29yZGVyUHJvZmlsZTtcclxuICAgICAgICBhYnN0cmFjdCBhY3F1aXJlX3ZvaWNlX3JlY29yZGVyKHJlY29yZGVyOiBSZWNvcmRlclByb2ZpbGUgfCB1bmRlZmluZWQpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3QgZ2V0X2VuY29kZXJfY29kZWMoKSA6IG51bWJlcjtcclxuICAgICAgICBhYnN0cmFjdCBzZXRfZW5jb2Rlcl9jb2RlYyhjb2RlYzogbnVtYmVyKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFNlcnZlckNvbW1hbmQge1xyXG4gICAgY29tbWFuZDogc3RyaW5nO1xyXG4gICAgYXJndW1lbnRzOiBhbnlbXTtcclxufVxyXG5cclxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0Q29tbWFuZEhhbmRsZXIge1xyXG4gICAgcmVhZG9ubHkgY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uO1xyXG5cclxuICAgIGhhbmRsZXJfYm9zczogQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3MgfCB1bmRlZmluZWQ7XHJcbiAgICB2b2xhdGlsZV9oYW5kbGVyX2Jvc3M6IGJvb2xlYW4gPSBmYWxzZTsgLyogaWYgdHJ1ZSB0aGFuIHRoZSBjb21tYW5kIGhhbmRsZXIgY291bGQgYmUgcmVnaXN0ZXJlZCB0d2ljZSB0byB0d28gb3IgbW9yZSBoYW5kbGVycyAqL1xyXG5cclxuICAgIGlnbm9yZV9jb25zdW1lZDogYm9vbGVhbiA9IGZhbHNlO1xyXG5cclxuICAgIHByb3RlY3RlZCBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQHJldHVybiBJZiB0aGUgY29tbWFuZCBzaG91bGQgYmUgY29uc3VtZWRcclxuICAgICAqL1xyXG4gICAgYWJzdHJhY3QgaGFuZGxlX2NvbW1hbmQoY29tbWFuZDogU2VydmVyQ29tbWFuZCkgOiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNpbmdsZUNvbW1hbmRIYW5kbGVyIHtcclxuICAgIG5hbWU/OiBzdHJpbmc7XHJcbiAgICBjb21tYW5kPzogc3RyaW5nO1xyXG4gICAgdGltZW91dD86IG51bWJlcjtcclxuXHJcbiAgICAvKiBpZiB0aGUgcmV0dXJuIGlzIHRydWUgdGhlbiB0aGUgY29tbWFuZCBoYW5kbGVyIHdpbGwgYmUgcmVtb3ZlZCAqL1xyXG4gICAgZnVuY3Rpb246IChjb21tYW5kOiBTZXJ2ZXJDb21tYW5kKSA9PiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3Mge1xyXG4gICAgcmVhZG9ubHkgY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uO1xyXG4gICAgcHJvdGVjdGVkIGNvbW1hbmRfaGFuZGxlcnM6IEFic3RyYWN0Q29tbWFuZEhhbmRsZXJbXSA9IFtdO1xyXG4gICAgLyogVE9ETzogVGltZW91dCAqL1xyXG4gICAgcHJvdGVjdGVkIHNpbmdsZV9jb21tYW5kX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyW10gPSBbXTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycyA9IHVuZGVmaW5lZDtcclxuICAgICAgICB0aGlzLnNpbmdsZV9jb21tYW5kX2hhbmRsZXIgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgcmVnaXN0ZXJfaGFuZGxlcihoYW5kbGVyOiBBYnN0cmFjdENvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgaWYoIWhhbmRsZXIudm9sYXRpbGVfaGFuZGxlcl9ib3NzICYmIGhhbmRsZXIuaGFuZGxlcl9ib3NzKVxyXG4gICAgICAgICAgICB0aHJvdyBcImhhbmRsZXIgYWxyZWFkeSByZWdpc3RlcmVkXCI7XHJcblxyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5yZW1vdmUoaGFuZGxlcik7IC8qIGp1c3QgdG8gYmUgc3VyZSAqL1xyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5wdXNoKGhhbmRsZXIpO1xyXG4gICAgICAgIGhhbmRsZXIuaGFuZGxlcl9ib3NzID0gdGhpcztcclxuICAgIH1cclxuXHJcbiAgICB1bnJlZ2lzdGVyX2hhbmRsZXIoaGFuZGxlcjogQWJzdHJhY3RDb21tYW5kSGFuZGxlcikge1xyXG4gICAgICAgIGlmKCFoYW5kbGVyLnZvbGF0aWxlX2hhbmRsZXJfYm9zcyAmJiBoYW5kbGVyLmhhbmRsZXJfYm9zcyAhPT0gdGhpcykge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJUcmllZCB0byB1bnJlZ2lzdGVyIGNvbW1hbmQgaGFuZGxlciB3aGljaCBkb2VzIG5vdCBiZWxvbmcgdG8gdGhlIGhhbmRsZXIgYm9zc1wiKSk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5yZW1vdmUoaGFuZGxlcik7XHJcbiAgICAgICAgaGFuZGxlci5oYW5kbGVyX2Jvc3MgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG5cclxuICAgIHJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKGhhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgdGhpcy5zaW5nbGVfY29tbWFuZF9oYW5kbGVyLnB1c2goaGFuZGxlcik7XHJcbiAgICB9XHJcblxyXG4gICAgcmVtb3ZlX3NpbmdsZV9oYW5kbGVyKGhhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgdGhpcy5zaW5nbGVfY29tbWFuZF9oYW5kbGVyLnJlbW92ZShoYW5kbGVyKTtcclxuICAgIH1cclxuXHJcbiAgICBoYW5kbGVycygpIDogQWJzdHJhY3RDb21tYW5kSGFuZGxlcltdIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5jb21tYW5kX2hhbmRsZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIGludm9rZV9oYW5kbGUoY29tbWFuZDogU2VydmVyQ29tbWFuZCkgOiBib29sZWFuIHtcclxuICAgICAgICBsZXQgZmxhZ19jb25zdW1lZCA9IGZhbHNlO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgaGFuZGxlciBvZiB0aGlzLmNvbW1hbmRfaGFuZGxlcnMpIHtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmKCFmbGFnX2NvbnN1bWVkIHx8IGhhbmRsZXIuaWdub3JlX2NvbnN1bWVkKVxyXG4gICAgICAgICAgICAgICAgICAgIGZsYWdfY29uc3VtZWQgPSBmbGFnX2NvbnN1bWVkIHx8IGhhbmRsZXIuaGFuZGxlX2NvbW1hbmQoY29tbWFuZCk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW52b2tlIGNvbW1hbmQgaGFuZGxlci4gSW52b2NhdGlvbiByZXN1bHRzIGluIGFuIGV4Y2VwdGlvbjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yKGNvbnN0IGhhbmRsZXIgb2YgWy4uLnRoaXMuc2luZ2xlX2NvbW1hbmRfaGFuZGxlcl0pIHtcclxuICAgICAgICAgICAgaWYoaGFuZGxlci5jb21tYW5kICYmIGhhbmRsZXIuY29tbWFuZCAhPSBjb21tYW5kLmNvbW1hbmQpXHJcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcclxuXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBpZihoYW5kbGVyLmZ1bmN0aW9uKGNvbW1hbmQpKVxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2luZ2xlX2NvbW1hbmRfaGFuZGxlci5yZW1vdmUoaGFuZGxlcik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW52b2tlIHNpbmdsZSBjb21tYW5kIGhhbmRsZXIuIEludm9jYXRpb24gcmVzdWx0cyBpbiBhbiBleGNlcHRpb246ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmbGFnX2NvbnN1bWVkO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtMYXRlclByb21pc2V9IGZyb20gXCIuLi91dGlscy9oZWxwZXJzXCI7XHJcblxyXG5leHBvcnQgZW51bSBFcnJvcklEIHtcclxuICAgIE5PVF9JTVBMRU1FTlRFRCA9IDB4MixcclxuICAgIENPTU1BTkRfTk9UX0ZPVU5EID0gMHgxMDAsXHJcblxyXG4gICAgUEVSTUlTU0lPTl9FUlJPUiA9IDI1NjgsXHJcbiAgICBFTVBUWV9SRVNVTFQgPSAweDA1MDEsXHJcbiAgICBQTEFZTElTVF9JU19JTl9VU0UgPSAweDIxMDMsXHJcblxyXG4gICAgRklMRV9BTFJFQURZX0VYSVNUUyA9IDIwNTAsXHJcblxyXG4gICAgQ0xJRU5UX0lOVkFMSURfSUQgPSAweDAyMDAsXHJcblxyXG4gICAgQ09OVkVSU0FUSU9OX0lOVkFMSURfSUQgPSAweDIyMDAsXHJcbiAgICBDT05WRVJTQVRJT05fTU9SRV9EQVRBID0gMHgyMjAxLFxyXG4gICAgQ09OVkVSU0FUSU9OX0lTX1BSSVZBVEUgPSAweDIyMDJcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIENvbW1hbmRSZXN1bHQge1xyXG4gICAgc3VjY2VzczogYm9vbGVhbjtcclxuICAgIGlkOiBudW1iZXI7XHJcbiAgICBtZXNzYWdlOiBzdHJpbmc7XHJcbiAgICBleHRyYV9tZXNzYWdlOiBzdHJpbmc7XHJcblxyXG4gICAganNvbjogYW55O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGpzb24pIHtcclxuICAgICAgICB0aGlzLmpzb24gPSBqc29uO1xyXG4gICAgICAgIHRoaXMuaWQgPSBwYXJzZUludChqc29uW1wiaWRcIl0pO1xyXG4gICAgICAgIHRoaXMubWVzc2FnZSA9IGpzb25bXCJtc2dcIl07XHJcblxyXG4gICAgICAgIHRoaXMuZXh0cmFfbWVzc2FnZSA9IFwiXCI7XHJcbiAgICAgICAgaWYoanNvbltcImV4dHJhX21zZ1wiXSkgdGhpcy5leHRyYV9tZXNzYWdlID0ganNvbltcImV4dHJhX21zZ1wiXTtcclxuXHJcbiAgICAgICAgdGhpcy5zdWNjZXNzID0gdGhpcy5pZCA9PSAwO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIENsaWVudE5hbWVJbmZvIHtcclxuICAgIC8vY2x1aWQ9dFl6S1VyeW5cXC9cXC9ZOFZCTWY4UEhVVDZCMWVpRT0gbmFtZT1FeHAgY2xuYW1lPUV4cCBjbGRiaWQ9OVxyXG4gICAgY2xpZW50X3VuaXF1ZV9pZDogc3RyaW5nO1xyXG4gICAgY2xpZW50X25pY2tuYW1lOiBzdHJpbmc7XHJcbiAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDbGllbnROYW1lRnJvbVVpZCB7XHJcbiAgICBwcm9taXNlOiBMYXRlclByb21pc2U8Q2xpZW50TmFtZUluZm9bXT4sXHJcbiAgICBrZXlzOiBzdHJpbmdbXSxcclxuICAgIHJlc3BvbnNlOiBDbGllbnROYW1lSW5mb1tdXHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVyR3JvdXBDbGllbnQge1xyXG4gICAgY2xpZW50X25pY2tuYW1lOiBzdHJpbmc7XHJcbiAgICBjbGllbnRfdW5pcXVlX2lkZW50aWZpZXI6IHN0cmluZztcclxuICAgIGNsaWVudF9kYXRhYmFzZV9pZDogbnVtYmVyO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFF1ZXJ5TGlzdEVudHJ5IHtcclxuICAgIHVzZXJuYW1lOiBzdHJpbmc7XHJcbiAgICB1bmlxdWVfaWQ6IHN0cmluZztcclxuICAgIGJvdW5kZWRfc2VydmVyOiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUXVlcnlMaXN0IHtcclxuICAgIGZsYWdfb3duOiBib29sZWFuO1xyXG4gICAgZmxhZ19hbGw6IGJvb2xlYW47XHJcblxyXG4gICAgcXVlcmllczogUXVlcnlMaXN0RW50cnlbXTtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQbGF5bGlzdCB7XHJcbiAgICBwbGF5bGlzdF9pZDogbnVtYmVyO1xyXG4gICAgcGxheWxpc3RfYm90X2lkOiBudW1iZXI7XHJcbiAgICBwbGF5bGlzdF90aXRsZTogc3RyaW5nO1xyXG4gICAgcGxheWxpc3RfdHlwZTogbnVtYmVyO1xyXG4gICAgcGxheWxpc3Rfb3duZXJfZGJpZDogbnVtYmVyO1xyXG4gICAgcGxheWxpc3Rfb3duZXJfbmFtZTogc3RyaW5nO1xyXG5cclxuICAgIG5lZWRlZF9wb3dlcl9tb2RpZnk6IG51bWJlcjtcclxuICAgIG5lZWRlZF9wb3dlcl9wZXJtaXNzaW9uX21vZGlmeTogbnVtYmVyO1xyXG4gICAgbmVlZGVkX3Bvd2VyX2RlbGV0ZTogbnVtYmVyO1xyXG4gICAgbmVlZGVkX3Bvd2VyX3NvbmdfYWRkOiBudW1iZXI7XHJcbiAgICBuZWVkZWRfcG93ZXJfc29uZ19tb3ZlOiBudW1iZXI7XHJcbiAgICBuZWVkZWRfcG93ZXJfc29uZ19yZW1vdmU6IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQbGF5bGlzdEluZm8ge1xyXG4gICAgcGxheWxpc3RfaWQ6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X3RpdGxlOiBzdHJpbmcsXHJcbiAgICBwbGF5bGlzdF9kZXNjcmlwdGlvbjogc3RyaW5nLFxyXG4gICAgcGxheWxpc3RfdHlwZTogbnVtYmVyLFxyXG5cclxuICAgIHBsYXlsaXN0X293bmVyX2RiaWQ6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X293bmVyX25hbWU6IHN0cmluZyxcclxuXHJcbiAgICBwbGF5bGlzdF9mbGFnX2RlbGV0ZV9wbGF5ZWQ6IGJvb2xlYW4sXHJcbiAgICBwbGF5bGlzdF9mbGFnX2ZpbmlzaGVkOiBib29sZWFuLFxyXG4gICAgcGxheWxpc3RfcmVwbGF5X21vZGU6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZDogbnVtYmVyLFxyXG5cclxuICAgIHBsYXlsaXN0X21heF9zb25nczogbnVtYmVyXHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUGxheWxpc3RTb25nIHtcclxuICAgIHNvbmdfaWQ6IG51bWJlcjtcclxuICAgIHNvbmdfcHJldmlvdXNfc29uZ19pZDogbnVtYmVyO1xyXG4gICAgc29uZ19pbnZva2VyOiBzdHJpbmc7XHJcbiAgICBzb25nX3VybDogc3RyaW5nO1xyXG4gICAgc29uZ191cmxfbG9hZGVyOiBzdHJpbmc7XHJcbiAgICBzb25nX2xvYWRlZDogYm9vbGVhbjtcclxuICAgIHNvbmdfbWV0YWRhdGE6IHN0cmluZztcclxufSIsIi8vIEFTTi4xIEphdmFTY3JpcHQgZGVjb2RlclxyXG4vLyBDb3B5cmlnaHQgKGMpIDIwMDgtMjAxOCBMYXBvIEx1Y2hpbmkgPGxhcG9AbGFwby5pdD5cclxuLy8gQ29weXJpZ2h0IChjKSAyMDE5LTIwMTkgTWFya3VzIEhhZGVuZmVsZHQgPGdpdEB0ZWFzcGVhay5kZT5cclxuXHJcbi8vIFBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG4vLyBwdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQsIHByb3ZpZGVkIHRoYXQgdGhlIGFib3ZlXHJcbi8vIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2UgYXBwZWFyIGluIGFsbCBjb3BpZXMuXHJcbi8vXHJcbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIgQU5EIFRIRSBBVVRIT1IgRElTQ0xBSU1TIEFMTCBXQVJSQU5USUVTXHJcbi8vIFdJVEggUkVHQVJEIFRPIFRISVMgU09GVFdBUkUgSU5DTFVESU5HIEFMTCBJTVBMSUVEIFdBUlJBTlRJRVMgT0ZcclxuLy8gTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTLiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SIEJFIExJQUJMRSBGT1JcclxuLy8gQU5ZIFNQRUNJQUwsIERJUkVDVCwgSU5ESVJFQ1QsIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyBPUiBBTlkgREFNQUdFU1xyXG4vLyBXSEFUU09FVkVSIFJFU1VMVElORyBGUk9NIExPU1MgT0YgVVNFLCBEQVRBIE9SIFBST0ZJVFMsIFdIRVRIRVIgSU4gQU5cclxuLy8gQUNUSU9OIE9GIENPTlRSQUNULCBORUdMSUdFTkNFIE9SIE9USEVSIFRPUlRJT1VTIEFDVElPTiwgQVJJU0lORyBPVVQgT0ZcclxuLy8gT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBVU0UgT1IgUEVSRk9STUFOQ0UgT0YgVEhJUyBTT0ZUV0FSRS5cclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgYXNuMSB7XHJcbiAgICBkZWNsYXJlIGNsYXNzIEludDEwIHtcclxuICAgICAgICBjb25zdHJ1Y3Rvcih2YWx1ZT86IGFueSk7XHJcblxyXG4gICAgICAgIHN1YihzdWI6IG51bWJlcik7XHJcbiAgICAgICAgbXVsQWRkKG11bDogbnVtYmVyLCBhZGQ6IG51bWJlcik7XHJcbiAgICAgICAgc2ltcGxpZnkoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBlbGxpcHNpcyA9IFwiXFx1MjAyNlwiO1xyXG5cclxuICAgIGZ1bmN0aW9uIHN0cmluZ19jdXQoc3RyLCBsZW4pIHtcclxuICAgICAgICBpZiAoc3RyLmxlbmd0aCA+IGxlbilcclxuICAgICAgICAgICAgc3RyID0gc3RyLnN1YnN0cmluZygwLCBsZW4pICsgZWxsaXBzaXM7XHJcbiAgICAgICAgcmV0dXJuIHN0cjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY2xhc3MgU3RyZWFtIHtcclxuICAgICAgICBwcml2YXRlIHN0YXRpYyBIRVhfRElHSVRTID0gXCIwMTIzNDU2Nzg5QUJDREVGXCI7XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVUaW1lUyA9IC9eKFxcZFxcZCkoMFsxLTldfDFbMC0yXSkoMFsxLTldfFsxMl1cXGR8M1swMV0pKFswMV1cXGR8MlswLTNdKSg/OihbMC01XVxcZCkoPzooWzAtNV1cXGQpKD86Wy4sXShcXGR7MSwzfSkpPyk/KT8oWnxbLStdKD86WzBdXFxkfDFbMC0yXSkoWzAtNV1cXGQpPyk/JC87XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVUaW1lTCA9IC9eKFxcZFxcZFxcZFxcZCkoMFsxLTldfDFbMC0yXSkoMFsxLTldfFsxMl1cXGR8M1swMV0pKFswMV1cXGR8MlswLTNdKSg/OihbMC01XVxcZCkoPzooWzAtNV1cXGQpKD86Wy4sXShcXGR7MSwzfSkpPyk/KT8oWnxbLStdKD86WzBdXFxkfDFbMC0yXSkoWzAtNV1cXGQpPyk/JC87XHJcblxyXG4gICAgICAgIHBvc2l0aW9uOiBudW1iZXI7XHJcbiAgICAgICAgZGF0YTogc3RyaW5nIHwgQXJyYXlCdWZmZXI7XHJcblxyXG4gICAgICAgIGNvbnN0cnVjdG9yKGRhdGE6IHN0cmluZyB8IFN0cmVhbSB8IEFycmF5QnVmZmVyLCBwb3NpdGlvbjogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmIChkYXRhIGluc3RhbmNlb2YgU3RyZWFtKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhID0gZGF0YS5kYXRhO1xyXG4gICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICB0aGlzLmRhdGEgPSBkYXRhO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5wb3NpdGlvbiA9IHBvc2l0aW9uO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGVuZ3RoKCkgOiBudW1iZXIge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5kYXRhIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5kYXRhLmJ5dGVMZW5ndGg7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGEubGVuZ3RoO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZ2V0KHBvc2l0aW9uPzogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmIChwb3NpdGlvbiA9PT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICAgICAgcG9zaXRpb24gPSB0aGlzLnBvc2l0aW9uKys7XHJcblxyXG4gICAgICAgICAgICBpZiAocG9zaXRpb24gPj0gdGhpcy5sZW5ndGgoKSlcclxuICAgICAgICAgICAgICAgIHRocm93ICdSZXF1ZXN0aW5nIGJ5dGUgb2Zmc2V0ICcgKyB0aGlzLnBvc2l0aW9uICsgJyBvbiBhIHN0cmVhbSBvZiBsZW5ndGggJyArIHRoaXMubGVuZ3RoKCk7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gKHR5cGVvZih0aGlzLmRhdGEpID09PSBcInN0cmluZ1wiKSA/IHRoaXMuZGF0YS5jaGFyQ29kZUF0KHBvc2l0aW9uKSA6IHRoaXMuZGF0YVtwb3NpdGlvbl07XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBoZXhCeXRlKGJ5dGU6IG51bWJlcikge1xyXG4gICAgICAgICAgICByZXR1cm4gU3RyZWFtLkhFWF9ESUdJVFMuY2hhckF0KChieXRlID4+IDQpICYgMHhGKSArIFN0cmVhbS5IRVhfRElHSVRTLmNoYXJBdChieXRlICYgMHhGKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHBhcnNlU3RyaW5nSVNPKHN0YXJ0LCBlbmQpIHtcclxuICAgICAgICAgICAgbGV0IHMgPSBcIlwiO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSlcclxuICAgICAgICAgICAgICAgIHMgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSh0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcGFyc2VTdHJpbmdVVEYoc3RhcnQsIGVuZCkge1xyXG4gICAgICAgICAgICBsZXQgcyA9IFwiXCI7XHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSBzdGFydDsgaSA8IGVuZDspIHtcclxuICAgICAgICAgICAgICAgIGxldCBjID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIGlmIChjIDwgMTI4KVxyXG4gICAgICAgICAgICAgICAgICAgIHMgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjKTtcclxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKChjID4gMTkxKSAmJiAoYyA8IDIyNCkpXHJcbiAgICAgICAgICAgICAgICAgICAgcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCgoYyAmIDB4MUYpIDw8IDYpIHwgKHRoaXMuZ2V0KGkrKykgJiAweDNGKSk7XHJcbiAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCgoYyAmIDB4MEYpIDw8IDEyKSB8ICgodGhpcy5nZXQoaSsrKSAmIDB4M0YpIDw8IDYpIHwgKHRoaXMuZ2V0KGkrKykgJiAweDNGKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHM7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBwYXJzZVN0cmluZ0JNUChzdGFydCwgZW5kKSB7XHJcbiAgICAgICAgICAgIGxldCBzdHIgPSBcIlwiLCBoaSwgbG87XHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSBzdGFydDsgaSA8IGVuZDspIHtcclxuICAgICAgICAgICAgICAgIGhpID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIGxvID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIHN0ciArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChoaSA8PCA4KSB8IGxvKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gc3RyO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcGFyc2VUaW1lKHN0YXJ0LCBlbmQsIHNob3J0WWVhcikge1xyXG4gICAgICAgICAgICBsZXQgcyA9IHRoaXMucGFyc2VTdHJpbmdJU08oc3RhcnQsIGVuZCksXHJcbiAgICAgICAgICAgICAgICBtID0gKHNob3J0WWVhciA/IFN0cmVhbS5yZVRpbWVTIDogU3RyZWFtLnJlVGltZUwpLmV4ZWMocyk7XHJcbiAgICAgICAgICAgIGlmICghbSlcclxuICAgICAgICAgICAgICAgIHJldHVybiBcIlVucmVjb2duaXplZCB0aW1lOiBcIiArIHM7XHJcbiAgICAgICAgICAgIGlmIChzaG9ydFllYXIpIHtcclxuICAgICAgICAgICAgICAgIC8vIHRvIGF2b2lkIHF1ZXJ5aW5nIHRoZSB0aW1lciwgdXNlIHRoZSBmaXhlZCByYW5nZSBbMTk3MCwgMjA2OV1cclxuICAgICAgICAgICAgICAgIC8vIGl0IHdpbGwgY29uZm9ybSB3aXRoIElUVSBYLjQwMCBbLTEwLCArNDBdIHNsaWRpbmcgd2luZG93IHVudGlsIDIwMzBcclxuICAgICAgICAgICAgICAgIC8vbVsxXSA9ICttWzFdO1xyXG4gICAgICAgICAgICAgICAgLy9tWzFdICs9IChwYXJzZUludChtWzFdKSA8IDcwKSA/IDIwMDAgOiAxOTAwO1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmaXhtZSFcIjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzID0gbVsxXSArIFwiLVwiICsgbVsyXSArIFwiLVwiICsgbVszXSArIFwiIFwiICsgbVs0XTtcclxuICAgICAgICAgICAgaWYgKG1bNV0pIHtcclxuICAgICAgICAgICAgICAgIHMgKz0gXCI6XCIgKyBtWzVdO1xyXG4gICAgICAgICAgICAgICAgaWYgKG1bNl0pIHtcclxuICAgICAgICAgICAgICAgICAgICBzICs9IFwiOlwiICsgbVs2XTtcclxuICAgICAgICAgICAgICAgICAgICBpZiAobVs3XSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgcyArPSBcIi5cIiArIG1bN107XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgaWYgKG1bOF0pIHtcclxuICAgICAgICAgICAgICAgIHMgKz0gXCIgVVRDXCI7XHJcbiAgICAgICAgICAgICAgICBpZiAobVs4XSAhPSAnWicpIHtcclxuICAgICAgICAgICAgICAgICAgICBzICs9IG1bOF07XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1bOV0pXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHMgKz0gXCI6XCIgKyBtWzldO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHBhcnNlSW50ZWdlcihzdGFydCwgZW5kKSB7XHJcbiAgICAgICAgICAgIGxldCBjdXJyZW50OiBudW1iZXIgPSB0aGlzLmdldChzdGFydCk7XHJcblxyXG4gICAgICAgICAgICBsZXQgbmVnYXRpdmUgPSAoY3VycmVudCA+IDEyNyk7XHJcbiAgICAgICAgICAgIGxldCBwYWRkaW5nID0gbmVnYXRpdmUgPyAyNTUgOiAwO1xyXG4gICAgICAgICAgICBsZXQgbGVuZ3RoO1xyXG4gICAgICAgICAgICBsZXQgZGVzY3JpcHRvcjogbnVtYmVyIHwgc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgLy8gc2tpcCB1bnVzZWZ1bCBiaXRzIChub3QgYWxsb3dlZCBpbiBERVIpXHJcbiAgICAgICAgICAgIHdoaWxlIChjdXJyZW50ID09IHBhZGRpbmcgJiYgKytzdGFydCA8IGVuZClcclxuICAgICAgICAgICAgICAgIGN1cnJlbnQgPSB0aGlzLmdldChzdGFydCk7XHJcblxyXG4gICAgICAgICAgICBsZW5ndGggPSBlbmQgLSBzdGFydDtcclxuICAgICAgICAgICAgaWYgKGxlbmd0aCA9PT0gMClcclxuICAgICAgICAgICAgICAgIHJldHVybiBuZWdhdGl2ZSA/ICctMScgOiAnMCc7XHJcblxyXG4gICAgICAgICAgICAvLyBzaG93IGJpdCBsZW5ndGggb2YgaHVnZSBpbnRlZ2Vyc1xyXG4gICAgICAgICAgICBpZiAobGVuZ3RoID4gNCkge1xyXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRvciA9IGN1cnJlbnQ7XHJcbiAgICAgICAgICAgICAgICBsZW5ndGggPDw9IDM7IC8qIGNhbGN1bGF0ZSBiaXQgbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICAgICAgd2hpbGUgKCgoZGVzY3JpcHRvciBeIHBhZGRpbmcpICYgMHg4MCkgPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0b3IgPDw9IDE7XHJcbiAgICAgICAgICAgICAgICAgICAgLS1sZW5ndGg7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBkZXNjcmlwdG9yID0gXCIoXCIgKyBsZW5ndGggKyBcIiBiaXQpXFxuXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgLy8gZGVjb2RlIHRoZSBpbnRlZ2VyXHJcbiAgICAgICAgICAgIGlmIChuZWdhdGl2ZSkgY3VycmVudCA9IGN1cnJlbnQgLSAyNTY7XHJcblxyXG4gICAgICAgICAgICBsZXQgbnVtYmVyID0gXCJcIjtcclxuICAgICAgICAgICAgaWYodHlwZW9mKEludDEwKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgbGV0IG4gPSBuZXcgSW50MTAoY3VycmVudCk7XHJcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpXHJcbiAgICAgICAgICAgICAgICAgICAgbi5tdWxBZGQoMjU2LCB0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgICAgICBudW1iZXIgPSBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgbiA9IDA7XHJcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpIHtcclxuICAgICAgICAgICAgICAgICAgICBuIDw8PSA4O1xyXG4gICAgICAgICAgICAgICAgICAgIG4gKz0gdGhpcy5nZXQoaSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBudW1iZXIgPSBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIGRlc2NyaXB0b3IgKyBudW1iZXI7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaXNBU0NJSShzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlcikge1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgYyA9IHRoaXMuZ2V0KGkpO1xyXG4gICAgICAgICAgICAgICAgaWYgKGMgPCAzMiB8fCBjID4gMTc2KVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9O1xyXG4gICAgICAgIFxyXG4gICAgICAgIHBhcnNlQml0U3RyaW5nKHN0YXJ0LCBlbmQsIG1heExlbmd0aCkge1xyXG4gICAgICAgICAgICBsZXQgdW51c2VkQml0ID0gdGhpcy5nZXQoc3RhcnQpLFxyXG4gICAgICAgICAgICAgICAgbGVuQml0ID0gKChlbmQgLSBzdGFydCAtIDEpIDw8IDMpIC0gdW51c2VkQml0LFxyXG4gICAgICAgICAgICAgICAgaW50cm8gPSBcIihcIiArIGxlbkJpdCArIFwiIGJpdClcXG5cIixcclxuICAgICAgICAgICAgICAgIHMgPSBcIlwiO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpIHtcclxuICAgICAgICAgICAgICAgIGxldCBiID0gdGhpcy5nZXQoaSksXHJcbiAgICAgICAgICAgICAgICAgICAgc2tpcCA9IChpID09IGVuZCAtIDEpID8gdW51c2VkQml0IDogMDtcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSA3OyBqID49IHNraXA7IC0tailcclxuICAgICAgICAgICAgICAgICAgICBzICs9IChiID4+IGopICYgMSA/IFwiMVwiIDogXCIwXCI7XHJcbiAgICAgICAgICAgICAgICBpZiAocy5sZW5ndGggPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGludHJvICsgc3RyaW5nX2N1dChzLCBtYXhMZW5ndGgpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBpbnRybyArIHM7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcGFyc2VPY3RldFN0cmluZyhzdGFydCwgZW5kLCBtYXhMZW5ndGgpIHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuaXNBU0NJSShzdGFydCwgZW5kKSlcclxuICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHRoaXMucGFyc2VTdHJpbmdJU08oc3RhcnQsIGVuZCksIG1heExlbmd0aCk7XHJcbiAgICAgICAgICAgIGxldCBsZW4gPSBlbmQgLSBzdGFydCxcclxuICAgICAgICAgICAgICAgIHMgPSBcIihcIiArIGxlbiArIFwiIGJ5dGUpXFxuXCI7XHJcbiAgICAgICAgICAgIG1heExlbmd0aCAvPSAyOyAvLyB3ZSB3b3JrIGluIGJ5dGVzXHJcbiAgICAgICAgICAgIGlmIChsZW4gPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICBlbmQgPSBzdGFydCArIG1heExlbmd0aDtcclxuICAgICAgICAgICAgZm9yIChsZXQgaSA9IHN0YXJ0OyBpIDwgZW5kOyArK2kpXHJcbiAgICAgICAgICAgICAgICBzICs9IHRoaXMuaGV4Qnl0ZSh0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICBzICs9IGVsbGlwc2lzO1xyXG4gICAgICAgICAgICByZXR1cm4gcztcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBwYXJzZU9JRChzdGFydCwgZW5kLCBtYXhMZW5ndGgpIHtcclxuICAgICAgICAgICAgbGV0IHMgPSAnJyxcclxuICAgICAgICAgICAgICAgIG4gPSBuZXcgSW50MTAoKSxcclxuICAgICAgICAgICAgICAgIGJpdHMgPSAwO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgbGV0IHYgPSB0aGlzLmdldChpKTtcclxuICAgICAgICAgICAgICAgIG4ubXVsQWRkKDEyOCwgdiAmIDB4N0YpO1xyXG4gICAgICAgICAgICAgICAgYml0cyArPSA3O1xyXG4gICAgICAgICAgICAgICAgaWYgKCEodiAmIDB4ODApKSB7IC8vIGZpbmlzaGVkXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHMgPT09ICcnKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuLnNpbXBsaWZ5KCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuIGluc3RhbmNlb2YgSW50MTApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uc3ViKDgwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBcIjIuXCIgKyBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgbSA9IG4gPCA4MCA/IG4gPCA0MCA/IDAgOiAxIDogMjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBtICsgXCIuXCIgKyAobiAtIG0gKiA0MCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2VcclxuICAgICAgICAgICAgICAgICAgICAgICAgcyArPSBcIi5cIiArIG4udG9TdHJpbmcoKTtcclxuICAgICAgICAgICAgICAgICAgICBpZiAocy5sZW5ndGggPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHMsIG1heExlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICAgICAgbiA9IG5ldyBJbnQxMCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJpdHMgPSAwO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmIChiaXRzID4gMClcclxuICAgICAgICAgICAgICAgIHMgKz0gXCIuaW5jb21wbGV0ZVwiO1xyXG4gICAgICAgICAgICAvKiBGSVhNRVxyXG4gICAgICAgICAgICBpZiAodHlwZW9mIG9pZHMgPT09ICdvYmplY3QnKSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgb2lkID0gb2lkc1tzXTtcclxuICAgICAgICAgICAgICAgIGlmIChvaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAob2lkLmQpIHMgKz0gXCJcXG5cIiArIG9pZC5kO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvaWQuYykgcyArPSBcIlxcblwiICsgb2lkLmM7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG9pZC53KSBzICs9IFwiXFxuKHdhcm5pbmchKVwiO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICovXHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGVudW0gVGFnQ2xhc3Mge1xyXG4gICAgICAgIFVOSVZFUlNBTCA9IDB4MDAsXHJcbiAgICAgICAgQVBQTElDQVRJT04gPSAweDAxLFxyXG4gICAgICAgIENPTlRFWFQgPSAweDAyLFxyXG4gICAgICAgIFBSSVZBVEUgPSAweDAzXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGVudW0gVGFnVHlwZSB7XHJcbiAgICAgICAgRU9DID0gMHgwMCxcclxuICAgICAgICBCT09MRUFOID0gMHgwMSxcclxuICAgICAgICBJTlRFR0VSID0gMHgwMixcclxuICAgICAgICBCSVRfU1RSSU5HID0gMHgwMyxcclxuICAgICAgICBPQ1RFVF9TVFJJTkcgPSAweDA0LFxyXG4gICAgICAgIE5VTEwgPSAweDA1LFxyXG4gICAgICAgIE9CSkVDVF9JREVOVElGSUVSID0gMHgwNixcclxuICAgICAgICBPYmplY3REZXNjcmlwdG9yID0gMHgwNyxcclxuICAgICAgICBFWFRFUk5BTCA9IDB4MDgsXHJcbiAgICAgICAgUkVBTCA9IDB4MDksXHJcbiAgICAgICAgRU5VTUVSQVRFRCA9IDB4MEEsXHJcbiAgICAgICAgRU1CRURERURfUERWID0gMHgwQixcclxuICAgICAgICBVVEY4U3RyaW5nID0gMHgwQyxcclxuICAgICAgICBTRVFVRU5DRSA9IDB4MTAsXHJcbiAgICAgICAgU0VUID0gMHgxMSxcclxuICAgICAgICBOdW1lcmljU3RyaW5nID0gMHgxMixcclxuICAgICAgICBQcmludGFibGVTdHJpbmcgPSAweDEzLCAvLyBBU0NJSSBzdWJzZXRcclxuICAgICAgICBUZWxldGV4dFN0cmluZyA9IDB4MTQsIC8vIGFrYSBUNjFTdHJpbmdcclxuICAgICAgICBWaWRlb3RleFN0cmluZyA9IDB4MTUsXHJcbiAgICAgICAgSUE1U3RyaW5nID0gMHgxNiwgLy8gQVNDSUlcclxuICAgICAgICBVVENUaW1lID0gMHgxNyxcclxuICAgICAgICBHZW5lcmFsaXplZFRpbWUgPSAweDE4LFxyXG4gICAgICAgIEdyYXBoaWNTdHJpbmcgPSAweDE5LFxyXG4gICAgICAgIFZpc2libGVTdHJpbmcgPSAweDFBLCAvLyBBU0NJSSBzdWJzZXRcclxuICAgICAgICBHZW5lcmFsU3RyaW5nID0gMHgxQixcclxuICAgICAgICBVbml2ZXJzYWxTdHJpbmcgPSAweDFDLFxyXG4gICAgICAgIEJNUFN0cmluZyA9IDB4MUVcclxuICAgIH1cclxuXHJcbiAgICBjbGFzcyBBU04xVGFnIHtcclxuICAgICAgICB0YWdDbGFzczogVGFnQ2xhc3M7XHJcbiAgICAgICAgdHlwZTogVGFnVHlwZTtcclxuICAgICAgICB0YWdDb25zdHJ1Y3RlZDogYm9vbGVhbjtcclxuICAgICAgICB0YWdOdW1iZXI6IG51bWJlcjtcclxuXHJcbiAgICAgICAgY29uc3RydWN0b3Ioc3RyZWFtOiBTdHJlYW0pIHtcclxuICAgICAgICAgICAgbGV0IGJ1ZiA9IHN0cmVhbS5nZXQoKTtcclxuICAgICAgICAgICAgdGhpcy50YWdDbGFzcyA9IGJ1ZiA+PiA2O1xyXG4gICAgICAgICAgICB0aGlzLnRhZ0NvbnN0cnVjdGVkID0gKChidWYgJiAweDIwKSAhPT0gMCk7XHJcbiAgICAgICAgICAgIHRoaXMudGFnTnVtYmVyID0gYnVmICYgMHgxRjtcclxuICAgICAgICAgICAgaWYgKHRoaXMudGFnTnVtYmVyID09IDB4MUYpIHsgLy8gbG9uZyB0YWdcclxuICAgICAgICAgICAgICAgIGxldCBuID0gbmV3IEludDEwKCk7XHJcbiAgICAgICAgICAgICAgICBkbyB7XHJcbiAgICAgICAgICAgICAgICAgICAgYnVmID0gc3RyZWFtLmdldCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIG4ubXVsQWRkKDEyOCwgYnVmICYgMHg3Rik7XHJcbiAgICAgICAgICAgICAgICB9IHdoaWxlIChidWYgJiAweDgwKTtcclxuICAgICAgICAgICAgICAgIHRoaXMudGFnTnVtYmVyID0gbi5zaW1wbGlmeSgpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpc1VuaXZlcnNhbCgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudGFnQ2xhc3MgPT09IDB4MDA7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaXNFT0MoKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnRhZ0NsYXNzID09PSAweDAwICYmIHRoaXMudGFnTnVtYmVyID09PSAweDAwO1xyXG4gICAgICAgIH07XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGNsYXNzIEFTTjEge1xyXG4gICAgICAgIHN0cmVhbTogU3RyZWFtO1xyXG4gICAgICAgIGhlYWRlcjogbnVtYmVyO1xyXG4gICAgICAgIGxlbmd0aDogbnVtYmVyO1xyXG4gICAgICAgIHRhZzogQVNOMVRhZztcclxuICAgICAgICBjaGlsZHJlbjogQVNOMVtdO1xyXG5cclxuICAgICAgICBjb25zdHJ1Y3RvcihzdHJlYW06IFN0cmVhbSwgaGVhZGVyOiBudW1iZXIsIGxlbmd0aDogbnVtYmVyLCB0YWc6IEFTTjFUYWcsIGNoaWxkcmVuOiBBU04xW10pIHtcclxuICAgICAgICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07XHJcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyID0gaGVhZGVyO1xyXG4gICAgICAgICAgICB0aGlzLmxlbmd0aCA9IGxlbmd0aDtcclxuICAgICAgICAgICAgdGhpcy50YWcgPSB0YWc7XHJcbiAgICAgICAgICAgIHRoaXMuY2hpbGRyZW4gPSBjaGlsZHJlbjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnRlbnQobWF4X2xlbmd0aD86IG51bWJlciwgdHlwZT86IFRhZ1R5cGUpIHsgLy8gYSBwcmV2aWV3IG9mIHRoZSBjb250ZW50IChpbnRlbmRlZCBmb3IgaHVtYW5zKVxyXG4gICAgICAgICAgICBpZiAodGhpcy50YWcgPT09IHVuZGVmaW5lZCkgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgICAgIGlmIChtYXhfbGVuZ3RoID09PSB1bmRlZmluZWQpXHJcbiAgICAgICAgICAgICAgICBtYXhfbGVuZ3RoID0gSW5maW5pdHk7XHJcblxyXG4gICAgICAgICAgICBsZXQgY29udGVudCA9IHRoaXMucG9zQ29udGVudCgpLFxyXG4gICAgICAgICAgICAgICAgbGVuID0gTWF0aC5hYnModGhpcy5sZW5ndGgpO1xyXG5cclxuICAgICAgICAgICAgaWYgKCF0aGlzLnRhZy5pc1VuaXZlcnNhbCgpKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5jaGlsZHJlbiAhPT0gbnVsbClcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCIoXCIgKyB0aGlzLmNoaWxkcmVuLmxlbmd0aCArIFwiIGVsZW0pXCI7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucGFyc2VPY3RldFN0cmluZyhjb250ZW50LCBjb250ZW50ICsgbGVuLCBtYXhfbGVuZ3RoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzd2l0Y2ggKHR5cGUgfHwgdGhpcy50YWcudGFnTnVtYmVyKSB7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDE6IC8vIEJPT0xFQU5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gKHRoaXMuc3RyZWFtLmdldChjb250ZW50KSA9PT0gMCkgPyBcImZhbHNlXCIgOiBcInRydWVcIjtcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgwMjogLy8gSU5URUdFUlxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5wYXJzZUludGVnZXIoY29udGVudCwgY29udGVudCArIGxlbik7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDM6IC8vIEJJVF9TVFJJTkdcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5jaGlsZHJlbiA/IFwiKFwiICsgdGhpcy5jaGlsZHJlbi5sZW5ndGggKyBcIiBlbGVtKVwiIDpcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zdHJlYW0ucGFyc2VCaXRTdHJpbmcoY29udGVudCwgY29udGVudCArIGxlbiwgbWF4X2xlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDQ6IC8vIE9DVEVUX1NUUklOR1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmNoaWxkcmVuID8gXCIoXCIgKyB0aGlzLmNoaWxkcmVuLmxlbmd0aCArIFwiIGVsZW0pXCIgOlxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnN0cmVhbS5wYXJzZU9jdGV0U3RyaW5nKGNvbnRlbnQsIGNvbnRlbnQgKyBsZW4sIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MDU6IC8vIE5VTExcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgwNjogLy8gT0JKRUNUX0lERU5USUZJRVJcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucGFyc2VPSUQoY29udGVudCwgY29udGVudCArIGxlbiwgbWF4X2xlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICAvL2Nhc2UgMHgwNzogLy8gT2JqZWN0RGVzY3JpcHRvclxyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MDg6IC8vIEVYVEVSTkFMXHJcbiAgICAgICAgICAgICAgICAvL2Nhc2UgMHgwOTogLy8gUkVBTFxyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MEE6IC8vIEVOVU1FUkFURURcclxuICAgICAgICAgICAgICAgIC8vY2FzZSAweDBCOiAvLyBFTUJFRERFRF9QRFZcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxMDogLy8gU0VRVUVOQ0VcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxMTogLy8gU0VUXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuY2hpbGRyZW4gIT09IG51bGwpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBcIihcIiArIHRoaXMuY2hpbGRyZW4ubGVuZ3RoICsgXCIgZWxlbSlcIjtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBcIihubyBlbGVtKVwiO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDBDOiAvLyBVVEY4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHN0cmluZ19jdXQodGhpcy5zdHJlYW0ucGFyc2VTdHJpbmdVVEYoY29udGVudCwgY29udGVudCArIGxlbiksIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDEyOiAvLyBOdW1lcmljU3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTM6IC8vIFByaW50YWJsZVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDE0OiAvLyBUZWxldGV4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTU6IC8vIFZpZGVvdGV4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTY6IC8vIElBNVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MTk6IC8vIEdyYXBoaWNTdHJpbmdcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxQTogLy8gVmlzaWJsZVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgICAgIC8vY2FzZSAweDFCOiAvLyBHZW5lcmFsU3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgLy9jYXNlIDB4MUM6IC8vIFVuaXZlcnNhbFN0cmluZ1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHRoaXMuc3RyZWFtLnBhcnNlU3RyaW5nSVNPKGNvbnRlbnQsIGNvbnRlbnQgKyBsZW4pLCBtYXhfbGVuZ3RoKTtcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxRTogLy8gQk1QU3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHN0cmluZ19jdXQodGhpcy5zdHJlYW0ucGFyc2VTdHJpbmdCTVAoY29udGVudCwgY29udGVudCArIGxlbiksIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDE3OiAvLyBVVENUaW1lXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTg6IC8vIEdlbmVyYWxpemVkVGltZVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5wYXJzZVRpbWUoY29udGVudCwgY29udGVudCArIGxlbiwgKHRoaXMudGFnLnRhZ051bWJlciA9PSAweDE3KSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgdHlwZU5hbWUoKTogc3RyaW5nIHtcclxuICAgICAgICAgICAgc3dpdGNoICh0aGlzLnRhZy50YWdDbGFzcykge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAwOiAvLyB1bml2ZXJzYWxcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gVGFnVHlwZVt0aGlzLnRhZy50YWdOdW1iZXJdIHx8IChcIlVuaXZlcnNhbF9cIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpKTtcclxuICAgICAgICAgICAgICAgIGNhc2UgMTpcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJBcHBsaWNhdGlvbl9cIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAyOlxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIltcIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpICsgXCJdXCI7IC8vIENvbnRleHRcclxuICAgICAgICAgICAgICAgIGNhc2UgMzpcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJQcml2YXRlX1wiICsgdGhpcy50YWcudGFnTnVtYmVyLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB0b1N0cmluZygpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudHlwZU5hbWUoKSArIFwiQFwiICsgdGhpcy5zdHJlYW0ucG9zaXRpb24gKyBcIltoZWFkZXI6XCIgKyB0aGlzLmhlYWRlciArIFwiLGxlbmd0aDpcIiArIHRoaXMubGVuZ3RoICsgXCIsc3ViOlwiICsgKCh0aGlzLmNoaWxkcmVuID09PSBudWxsKSA/ICdudWxsJyA6IHRoaXMuY2hpbGRyZW4ubGVuZ3RoKSArIFwiXVwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdG9QcmV0dHlTdHJpbmcoaW5kZW50KSB7XHJcbiAgICAgICAgICAgIGlmIChpbmRlbnQgPT09IHVuZGVmaW5lZCkgaW5kZW50ID0gJyc7XHJcbiAgICAgICAgICAgIGxldCBzID0gaW5kZW50ICsgdGhpcy50eXBlTmFtZSgpICsgXCIgQFwiICsgdGhpcy5zdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmxlbmd0aCA+PSAwKVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIitcIjtcclxuICAgICAgICAgICAgcyArPSB0aGlzLmxlbmd0aDtcclxuICAgICAgICAgICAgaWYgKHRoaXMudGFnLnRhZ0NvbnN0cnVjdGVkKVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIiAoY29uc3RydWN0ZWQpXCI7XHJcbiAgICAgICAgICAgIGVsc2UgaWYgKCh0aGlzLnRhZy5pc1VuaXZlcnNhbCgpICYmICgodGhpcy50YWcudGFnTnVtYmVyID09IDB4MDMpIHx8ICh0aGlzLnRhZy50YWdOdW1iZXIgPT0gMHgwNCkpKSAmJiAodGhpcy5jaGlsZHJlbiAhPT0gbnVsbCkpXHJcbiAgICAgICAgICAgICAgICBzICs9IFwiIChlbmNhcHN1bGF0ZXMpXCI7XHJcbiAgICAgICAgICAgIGxldCBjb250ZW50ID0gdGhpcy5jb250ZW50KCk7XHJcbiAgICAgICAgICAgIGlmIChjb250ZW50KVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIjogXCIgKyBjb250ZW50LnJlcGxhY2UoL1xcbi9nLCAnfCcpO1xyXG4gICAgICAgICAgICBzICs9IFwiXFxuXCI7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmNoaWxkcmVuICE9PSBudWxsKSB7XHJcbiAgICAgICAgICAgICAgICBpbmRlbnQgKz0gJyAgJztcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwLCBtYXggPSB0aGlzLmNoaWxkcmVuLmxlbmd0aDsgaSA8IG1heDsgKytpKVxyXG4gICAgICAgICAgICAgICAgICAgIHMgKz0gdGhpcy5jaGlsZHJlbltpXS50b1ByZXR0eVN0cmluZyhpbmRlbnQpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHBvc1N0YXJ0KCkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcG9zQ29udGVudCgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtLnBvc2l0aW9uICsgdGhpcy5oZWFkZXI7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcG9zRW5kKCkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucG9zaXRpb24gKyB0aGlzLmhlYWRlciArIE1hdGguYWJzKHRoaXMubGVuZ3RoKTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBzdGF0aWMgZGVjb2RlTGVuZ3RoKHN0cmVhbTogU3RyZWFtKSB7XHJcbiAgICAgICAgICAgIGxldCBidWYgPSBzdHJlYW0uZ2V0KCk7XHJcbiAgICAgICAgICAgIGNvbnN0IGxlbiA9IGJ1ZiAmIDB4N0Y7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPT0gYnVmKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGxlbjtcclxuICAgICAgICAgICAgaWYgKGxlbiA+IDYpIC8vIG5vIHJlYXNvbiB0byB1c2UgSW50MTAsIGFzIGl0IHdvdWxkIGJlIGEgaHVnZSBidWZmZXIgYW55d2F5c1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJMZW5ndGggb3ZlciA0OCBiaXRzIG5vdCBzdXBwb3J0ZWQgYXQgcG9zaXRpb24gXCIgKyAoc3RyZWFtLnBvc2l0aW9uIC0gMSk7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPT09IDApXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gbnVsbDsgLy8gdW5kZWZpbmVkXHJcblxyXG4gICAgICAgICAgICBidWYgPSAwO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlbjsgKytpKVxyXG4gICAgICAgICAgICAgICAgYnVmID0gKGJ1ZiA8PCA4KSArIHN0cmVhbS5nZXQoKTtcclxuICAgICAgICAgICAgcmV0dXJuIGJ1ZjtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBzdGF0aWMgZW5jb2RlTGVuZ3RoKGJ1ZmZlcjogVWludDhBcnJheSwgb2Zmc2V0OiBudW1iZXIsIGxlbmd0aDogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmKGxlbmd0aCA8IDB4N0YpIHtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltvZmZzZXRdID0gbGVuZ3RoO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW29mZnNldF0gPSAweDgwO1xyXG4gICAgICAgICAgICAgICAgbGV0IGluZGV4ID0gMTtcclxuICAgICAgICAgICAgICAgIHdoaWxlKGxlbmd0aCA+IDApIHtcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbb2Zmc2V0ICsgaW5kZXgrK10gPSBsZW5ndGggJiAweEZGO1xyXG4gICAgICAgICAgICAgICAgICAgIGxlbmd0aCA+Pj0gODtcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbb2Zmc2V0XSArPSAxO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGRlY29kZTAoc3RyZWFtOiBTdHJlYW0pIHtcclxuICAgICAgICBjb25zdCBzdHJlYW1TdGFydCA9IG5ldyBTdHJlYW0oc3RyZWFtLCAwKTsgLyogY29weSAqL1xyXG4gICAgICAgIGNvbnN0IHRhZyA9IG5ldyBBU04xVGFnKHN0cmVhbSk7XHJcbiAgICAgICAgbGV0IGxlbiA9IEFTTjEuZGVjb2RlTGVuZ3RoKHN0cmVhbSk7XHJcbiAgICAgICAgY29uc3Qgc3RhcnQgPSBzdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgY29uc3QgbGVuZ3RoX2hlYWRlciA9IHN0YXJ0IC0gc3RyZWFtU3RhcnQucG9zaXRpb247XHJcbiAgICAgICAgbGV0IGNoaWxkcmVuID0gbnVsbDtcclxuICAgICAgICBjb25zdCBxdWVyeV9jaGlsZHJlbiA9ICgpID0+IHtcclxuICAgICAgICAgICAgY2hpbGRyZW4gPSBbXTtcclxuICAgICAgICAgICAgaWYgKGxlbiAhPT0gbnVsbCkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgZW5kID0gc3RhcnQgKyBsZW47XHJcbiAgICAgICAgICAgICAgICBpZiAoZW5kID4gc3RyZWFtLmxlbmd0aCgpKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93ICdDb250YWluZXIgYXQgb2Zmc2V0ICcgKyBzdGFydCArICcgaGFzIGEgbGVuZ3RoIG9mICcgKyBsZW4gKyAnLCB3aGljaCBpcyBwYXN0IHRoZSBlbmQgb2YgdGhlIHN0cmVhbSc7XHJcbiAgICAgICAgICAgICAgICB3aGlsZSAoc3RyZWFtLnBvc2l0aW9uIDwgZW5kKVxyXG4gICAgICAgICAgICAgICAgICAgIGNoaWxkcmVuW2NoaWxkcmVuLmxlbmd0aF0gPSBkZWNvZGUwKHN0cmVhbSk7XHJcbiAgICAgICAgICAgICAgICBpZiAoc3RyZWFtLnBvc2l0aW9uICE9IGVuZClcclxuICAgICAgICAgICAgICAgICAgICB0aHJvdyAnQ29udGVudCBzaXplIGlzIG5vdCBjb3JyZWN0IGZvciBjb250YWluZXIgYXQgb2Zmc2V0ICcgKyBzdGFydDtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIC8vIHVuZGVmaW5lZCBsZW5ndGhcclxuICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgd2hpbGUgKHRydWUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcyA9IGRlY29kZTAoc3RyZWFtKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHMudGFnLmlzRU9DKCkpIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjaGlsZHJlbltjaGlsZHJlbi5sZW5ndGhdID0gcztcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgbGVuID0gc3RhcnQgLSBzdHJlYW0ucG9zaXRpb247IC8vIHVuZGVmaW5lZCBsZW5ndGhzIGFyZSByZXByZXNlbnRlZCBhcyBuZWdhdGl2ZSB2YWx1ZXNcclxuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aHJvdyAnRXhjZXB0aW9uIHdoaWxlIGRlY29kaW5nIHVuZGVmaW5lZCBsZW5ndGggY29udGVudCBhdCBvZmZzZXQgJyArIHN0YXJ0ICsgJzogJyArIGU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG4gICAgICAgIGlmICh0YWcudGFnQ29uc3RydWN0ZWQpIHtcclxuICAgICAgICAgICAgLy8gbXVzdCBoYXZlIHZhbGlkIGNvbnRlbnRcclxuICAgICAgICAgICAgcXVlcnlfY2hpbGRyZW4oKTtcclxuICAgICAgICB9IGVsc2UgaWYgKHRhZy5pc1VuaXZlcnNhbCgpICYmICgodGFnLnRhZ051bWJlciA9PSAweDAzKSB8fCAodGFnLnRhZ051bWJlciA9PSAweDA0KSkpIHtcclxuICAgICAgICAgICAgLy8gc29tZXRpbWVzIEJpdFN0cmluZyBhbmQgT2N0ZXRTdHJpbmcgYXJlIHVzZWQgdG8gZW5jYXBzdWxhdGUgQVNOLjFcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmICh0YWcudGFnTnVtYmVyID09IDB4MDMpXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHN0cmVhbS5nZXQoKSAhPSAwKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBcIkJJVCBTVFJJTkdzIHdpdGggdW51c2VkIGJpdHMgY2Fubm90IGVuY2Fwc3VsYXRlLlwiO1xyXG4gICAgICAgICAgICAgICAgcXVlcnlfY2hpbGRyZW4oKTtcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY2hpbGRyZW4ubGVuZ3RoOyArK2kpXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNoaWxkcmVuW2ldLnRhZy5pc0VPQygpKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyAnRU9DIGlzIG5vdCBzdXBwb3NlZCB0byBiZSBhY3R1YWwgY29udGVudC4nO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICAvLyBidXQgc2lsZW50bHkgaWdub3JlIHdoZW4gdGhleSBkb24ndFxyXG4gICAgICAgICAgICAgICAgY2hpbGRyZW4gPSBudWxsO1xyXG4gICAgICAgICAgICAgICAgLy9ERUJVRyBjb25zb2xlLmxvZygnQ291bGQgbm90IGRlY29kZSBzdHJ1Y3R1cmUgYXQgJyArIHN0YXJ0ICsgJzonLCBlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoY2hpbGRyZW4gPT09IG51bGwpIHtcclxuICAgICAgICAgICAgaWYgKGxlbiA9PT0gbnVsbClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiV2UgY2FuJ3Qgc2tpcCBvdmVyIGFuIGludmFsaWQgdGFnIHdpdGggdW5kZWZpbmVkIGxlbmd0aCBhdCBvZmZzZXQgXCIgKyBzdGFydDtcclxuICAgICAgICAgICAgc3RyZWFtLnBvc2l0aW9uID0gc3RhcnQgKyBNYXRoLmFicyhsZW4pO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbmV3IEFTTjEoc3RyZWFtU3RhcnQsIGxlbmd0aF9oZWFkZXIsIGxlbiwgdGFnLCBjaGlsZHJlbik7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlY29kZShzdHJlYW06IHN0cmluZyB8IEFycmF5QnVmZmVyKSB7XHJcbiAgICAgICAgcmV0dXJuIGRlY29kZTAobmV3IFN0cmVhbShzdHJlYW0sIDApKTtcclxuICAgIH1cclxufSIsImRlY2xhcmUgZnVuY3Rpb24gZGVmaW5lKCQpO1xyXG5kZWNsYXJlIGZ1bmN0aW9uIHVuZXNjYXBlKHN0cmluZzogc3RyaW5nKTogc3RyaW5nO1xyXG5kZWNsYXJlIGNsYXNzIF9zaGExIHtcclxuICAgIHN0YXRpYyBhcnJheUJ1ZmZlcigkOiBBcnJheUJ1ZmZlcikgOiBBcnJheUJ1ZmZlcjtcclxufVxyXG5cclxuLypcclxuaW50ZXJmYWNlIFdpbmRvdyB7XHJcbiAgICBUZXh0RW5jb2RlcjogYW55O1xyXG59XHJcbiovXHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHNoYSB7XHJcbiAgICAvKlxyXG4gICAgICogW2pzLXNoYTFde0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9lbW4xNzgvanMtc2hhMX1cclxuICAgICAqXHJcbiAgICAgKiBAdmVyc2lvbiAwLjYuMFxyXG4gICAgICogQGF1dGhvciBDaGVuLCBZaS1DeXVhbiBbZW1uMTc4QGdtYWlsLmNvbV1cclxuICAgICAqIEBjb3B5cmlnaHQgQ2hlbiwgWWktQ3l1YW4gMjAxNC0yMDE3XHJcbiAgICAgKiBAbGljZW5zZSBNSVRcclxuICAgICAqL1xyXG4gICAgLypqc2xpbnQgYml0d2lzZTogdHJ1ZSAqL1xyXG4gICAgKGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICd1c2Ugc3RyaWN0JztcclxuXHJcbiAgICAgICAgbGV0IHJvb3Q6IGFueSA9IHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnID8gd2luZG93IDoge307XHJcbiAgICAgICAgbGV0IE5PREVfSlMgPSAhcm9vdC5KU19TSEExX05PX05PREVfSlMgJiYgdHlwZW9mIHByb2Nlc3MgPT09ICdvYmplY3QnICYmIHByb2Nlc3MudmVyc2lvbnMgJiYgcHJvY2Vzcy52ZXJzaW9ucy5ub2RlO1xyXG4gICAgICAgIGlmIChOT0RFX0pTKSB7XHJcbiAgICAgICAgICAgIHJvb3QgPSBnbG9iYWw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBDT01NT05fSlMgPSAhcm9vdC5KU19TSEExX05PX0NPTU1PTl9KUyAmJiB0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0JyAmJiBtb2R1bGUuZXhwb3J0cztcclxuICAgICAgICBsZXQgQU1EID0gdHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiAoZGVmaW5lIGFzIGFueSkuYW1kO1xyXG4gICAgICAgIGxldCBIRVhfQ0hBUlMgPSAnMDEyMzQ1Njc4OWFiY2RlZicuc3BsaXQoJycpO1xyXG4gICAgICAgIGxldCBFWFRSQSA9IFstMjE0NzQ4MzY0OCwgODM4ODYwOCwgMzI3NjgsIDEyOF07XHJcbiAgICAgICAgbGV0IFNISUZUID0gWzI0LCAxNiwgOCwgMF07XHJcbiAgICAgICAgbGV0IE9VVFBVVF9UWVBFUyA9IFsnaGV4JywgJ2FycmF5JywgJ2RpZ2VzdCcsICdhcnJheUJ1ZmZlciddO1xyXG5cclxuICAgICAgICBsZXQgYmxvY2tzID0gW107XHJcblxyXG4gICAgICAgIGxldCBjcmVhdGVPdXRwdXRNZXRob2QgPSBmdW5jdGlvbiAob3V0cHV0VHlwZSkge1xyXG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgU2hhMSh0cnVlKS51cGRhdGUobWVzc2FnZSlbb3V0cHV0VHlwZV0oKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBsZXQgY3JlYXRlTWV0aG9kID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICBsZXQgbWV0aG9kOiBhbnkgPSBjcmVhdGVPdXRwdXRNZXRob2QoJ2hleCcpO1xyXG4gICAgICAgICAgICBpZiAoTk9ERV9KUykge1xyXG4gICAgICAgICAgICAgICAgbWV0aG9kID0gbm9kZVdyYXAobWV0aG9kKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBtZXRob2QuY3JlYXRlID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyAoU2hhMSBhcyBhbnkpKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIG1ldGhvZC51cGRhdGUgPSBmdW5jdGlvbiAobWVzc2FnZSkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIG1ldGhvZC5jcmVhdGUoKS51cGRhdGUobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgT1VUUFVUX1RZUEVTLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IE9VVFBVVF9UWVBFU1tpXTtcclxuICAgICAgICAgICAgICAgIG1ldGhvZFt0eXBlXSA9IGNyZWF0ZU91dHB1dE1ldGhvZCh0eXBlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gbWV0aG9kO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHZhciBub2RlV3JhcCA9IGZ1bmN0aW9uIChtZXRob2QpIHtcclxuICAgICAgICAgICAgdmFyIGNyeXB0byA9IGV2YWwoXCJyZXF1aXJlKCdjcnlwdG8nKVwiKTtcclxuICAgICAgICAgICAgdmFyIEJ1ZmZlciA9IGV2YWwoXCJyZXF1aXJlKCdidWZmZXInKS5CdWZmZXJcIik7XHJcbiAgICAgICAgICAgIHZhciBub2RlTWV0aG9kID0gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTEnKS51cGRhdGUobWVzc2FnZSwgJ3V0ZjgnKS5kaWdlc3QoJ2hleCcpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChtZXNzYWdlLmNvbnN0cnVjdG9yID09PSBBcnJheUJ1ZmZlcikge1xyXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBuZXcgVWludDhBcnJheShtZXNzYWdlKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAobWVzc2FnZS5sZW5ndGggPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBtZXRob2QobWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTEnKS51cGRhdGUobmV3IEJ1ZmZlcihtZXNzYWdlKSkuZGlnZXN0KCdoZXgnKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgcmV0dXJuIG5vZGVNZXRob2Q7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgZnVuY3Rpb24gU2hhMShzaGFyZWRNZW1vcnkpIHtcclxuICAgICAgICAgICAgaWYgKHNoYXJlZE1lbW9yeSkge1xyXG4gICAgICAgICAgICAgICAgYmxvY2tzWzBdID0gYmxvY2tzWzE2XSA9IGJsb2Nrc1sxXSA9IGJsb2Nrc1syXSA9IGJsb2Nrc1szXSA9XHJcbiAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzRdID0gYmxvY2tzWzVdID0gYmxvY2tzWzZdID0gYmxvY2tzWzddID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzhdID0gYmxvY2tzWzldID0gYmxvY2tzWzEwXSA9IGJsb2Nrc1sxMV0gPVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzEyXSA9IGJsb2Nrc1sxM10gPSBibG9ja3NbMTRdID0gYmxvY2tzWzE1XSA9IDA7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmJsb2NrcyA9IGJsb2NrcztcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuYmxvY2tzID0gWzAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDBdO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB0aGlzLmgwID0gMHg2NzQ1MjMwMTtcclxuICAgICAgICAgICAgdGhpcy5oMSA9IDB4RUZDREFCODk7XHJcbiAgICAgICAgICAgIHRoaXMuaDIgPSAweDk4QkFEQ0ZFO1xyXG4gICAgICAgICAgICB0aGlzLmgzID0gMHgxMDMyNTQ3NjtcclxuICAgICAgICAgICAgdGhpcy5oNCA9IDB4QzNEMkUxRjA7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmJsb2NrID0gdGhpcy5zdGFydCA9IHRoaXMuYnl0ZXMgPSB0aGlzLmhCeXRlcyA9IDA7XHJcbiAgICAgICAgICAgIHRoaXMuZmluYWxpemVkID0gdGhpcy5oYXNoZWQgPSBmYWxzZTtcclxuICAgICAgICAgICAgdGhpcy5maXJzdCA9IHRydWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbiAobWVzc2FnZSkge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5maW5hbGl6ZWQpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB2YXIgbm90U3RyaW5nID0gdHlwZW9mKG1lc3NhZ2UpICE9PSAnc3RyaW5nJztcclxuICAgICAgICAgICAgaWYgKG5vdFN0cmluZyAmJiBtZXNzYWdlLmNvbnN0cnVjdG9yID09PSByb290LkFycmF5QnVmZmVyKSB7XHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlID0gbmV3IFVpbnQ4QXJyYXkobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgdmFyIGNvZGUsIGluZGV4ID0gMCwgaSwgbGVuZ3RoID0gbWVzc2FnZS5sZW5ndGggfHwgMCwgYmxvY2tzID0gdGhpcy5ibG9ja3M7XHJcblxyXG4gICAgICAgICAgICB3aGlsZSAoaW5kZXggPCBsZW5ndGgpIHtcclxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmhhc2hlZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGFzaGVkID0gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzBdID0gdGhpcy5ibG9jaztcclxuICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTZdID0gYmxvY2tzWzFdID0gYmxvY2tzWzJdID0gYmxvY2tzWzNdID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzRdID0gYmxvY2tzWzVdID0gYmxvY2tzWzZdID0gYmxvY2tzWzddID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1s4XSA9IGJsb2Nrc1s5XSA9IGJsb2Nrc1sxMF0gPSBibG9ja3NbMTFdID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTJdID0gYmxvY2tzWzEzXSA9IGJsb2Nrc1sxNF0gPSBibG9ja3NbMTVdID0gMDtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZihub3RTdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGkgPSB0aGlzLnN0YXJ0OyBpbmRleCA8IGxlbmd0aCAmJiBpIDwgNjQ7ICsraW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gbWVzc2FnZVtpbmRleF0gPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGkgPSB0aGlzLnN0YXJ0OyBpbmRleCA8IGxlbmd0aCAmJiBpIDwgNjQ7ICsraW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29kZSA9IG1lc3NhZ2UuY2hhckNvZGVBdChpbmRleCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb2RlIDwgMHg4MCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gY29kZSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjb2RlIDwgMHg4MDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweGMwIHwgKGNvZGUgPj4gNikpIDw8IFNISUZUW2krKyAmIDNdO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gKDB4ODAgfCAoY29kZSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjb2RlIDwgMHhkODAwIHx8IGNvZGUgPj0gMHhlMDAwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHhlMCB8IChjb2RlID4+IDEyKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiA2KSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweDgwIHwgKGNvZGUgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlID0gMHgxMDAwMCArICgoKGNvZGUgJiAweDNmZikgPDwgMTApIHwgKG1lc3NhZ2UuY2hhckNvZGVBdCgrK2luZGV4KSAmIDB4M2ZmKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHhmMCB8IChjb2RlID4+IDE4KSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiAxMikgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiA2KSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweDgwIHwgKGNvZGUgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5sYXN0Qnl0ZUluZGV4ID0gaTtcclxuICAgICAgICAgICAgICAgIHRoaXMuYnl0ZXMgKz0gaSAtIHRoaXMuc3RhcnQ7XHJcbiAgICAgICAgICAgICAgICBpZiAoaSA+PSA2NCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYmxvY2sgPSBibG9ja3NbMTZdO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhcnQgPSBpIC0gNjQ7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oYXNoKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oYXNoZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLnN0YXJ0ID0gaTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAodGhpcy5ieXRlcyA+IDQyOTQ5NjcyOTUpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaEJ5dGVzICs9IHRoaXMuYnl0ZXMgLyA0Mjk0OTY3Mjk2IDw8IDA7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmJ5dGVzID0gdGhpcy5ieXRlcyAlIDQyOTQ5NjcyOTY7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgU2hhMS5wcm90b3R5cGUuZmluYWxpemUgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmZpbmFsaXplZCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHRoaXMuZmluYWxpemVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgdmFyIGJsb2NrcyA9IHRoaXMuYmxvY2tzLCBpID0gdGhpcy5sYXN0Qnl0ZUluZGV4O1xyXG4gICAgICAgICAgICBibG9ja3NbMTZdID0gdGhpcy5ibG9jaztcclxuICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gRVhUUkFbaSAmIDNdO1xyXG4gICAgICAgICAgICB0aGlzLmJsb2NrID0gYmxvY2tzWzE2XTtcclxuICAgICAgICAgICAgaWYgKGkgPj0gNTYpIHtcclxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5oYXNoZWQpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmhhc2goKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGJsb2Nrc1swXSA9IHRoaXMuYmxvY2s7XHJcbiAgICAgICAgICAgICAgICBibG9ja3NbMTZdID0gYmxvY2tzWzFdID0gYmxvY2tzWzJdID0gYmxvY2tzWzNdID1cclxuICAgICAgICAgICAgICAgICAgICBibG9ja3NbNF0gPSBibG9ja3NbNV0gPSBibG9ja3NbNl0gPSBibG9ja3NbN10gPVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbOF0gPSBibG9ja3NbOV0gPSBibG9ja3NbMTBdID0gYmxvY2tzWzExXSA9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTJdID0gYmxvY2tzWzEzXSA9IGJsb2Nrc1sxNF0gPSBibG9ja3NbMTVdID0gMDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBibG9ja3NbMTRdID0gdGhpcy5oQnl0ZXMgPDwgMyB8IHRoaXMuYnl0ZXMgPj4+IDI5O1xyXG4gICAgICAgICAgICBibG9ja3NbMTVdID0gdGhpcy5ieXRlcyA8PCAzO1xyXG4gICAgICAgICAgICB0aGlzLmhhc2goKTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS5oYXNoID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICB2YXIgYSA9IHRoaXMuaDAsIGIgPSB0aGlzLmgxLCBjID0gdGhpcy5oMiwgZCA9IHRoaXMuaDMsIGUgPSB0aGlzLmg0O1xyXG4gICAgICAgICAgICB2YXIgZiwgaiwgdCwgYmxvY2tzID0gdGhpcy5ibG9ja3M7XHJcblxyXG4gICAgICAgICAgICBmb3IoaiA9IDE2OyBqIDwgODA7ICsraikge1xyXG4gICAgICAgICAgICAgICAgdCA9IGJsb2Nrc1tqIC0gM10gXiBibG9ja3NbaiAtIDhdIF4gYmxvY2tzW2ogLSAxNF0gXiBibG9ja3NbaiAtIDE2XTtcclxuICAgICAgICAgICAgICAgIGJsb2Nrc1tqXSA9ICAodCA8PCAxKSB8ICh0ID4+PiAzMSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcihqID0gMDsgaiA8IDIwOyBqICs9IDUpIHtcclxuICAgICAgICAgICAgICAgIGYgPSAoYiAmIGMpIHwgKCh+YikgJiBkKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYSA8PCA1KSB8IChhID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBlID0gdCArIGYgKyBlICsgMTUxODUwMDI0OSArIGJsb2Nrc1tqXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYiA9IChiIDw8IDMwKSB8IChiID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gKGEgJiBiKSB8ICgofmEpICYgYyk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGUgPDwgNSkgfCAoZSA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgZCA9IHQgKyBmICsgZCArIDE1MTg1MDAyNDkgKyBibG9ja3NbaiArIDFdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBhID0gKGEgPDwgMzApIHwgKGEgPj4+IDIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGYgPSAoZSAmIGEpIHwgKCh+ZSkgJiBiKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZCA8PCA1KSB8IChkID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBjID0gdCArIGYgKyBjICsgMTUxODUwMDI0OSArIGJsb2Nrc1tqICsgMl0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGUgPSAoZSA8PCAzMCkgfCAoZSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChkICYgZSkgfCAoKH5kKSAmIGEpO1xyXG4gICAgICAgICAgICAgICAgdCA9IChjIDw8IDUpIHwgKGMgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGIgPSB0ICsgZiArIGIgKyAxNTE4NTAwMjQ5ICsgYmxvY2tzW2ogKyAzXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZCA9IChkIDw8IDMwKSB8IChkID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gKGMgJiBkKSB8ICgofmMpICYgZSk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGIgPDwgNSkgfCAoYiA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgYSA9IHQgKyBmICsgYSArIDE1MTg1MDAyNDkgKyBibG9ja3NbaiArIDRdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBjID0gKGMgPDwgMzApIHwgKGMgPj4+IDIpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBmb3IoOyBqIDwgNDA7IGogKz0gNSkge1xyXG4gICAgICAgICAgICAgICAgZiA9IGIgXiBjIF4gZDtcclxuICAgICAgICAgICAgICAgIHQgPSAoYSA8PCA1KSB8IChhID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBlID0gdCArIGYgKyBlICsgMTg1OTc3NTM5MyArIGJsb2Nrc1tqXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYiA9IChiIDw8IDMwKSB8IChiID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gYSBeIGIgXiBjO1xyXG4gICAgICAgICAgICAgICAgdCA9IChlIDw8IDUpIHwgKGUgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGQgPSB0ICsgZiArIGQgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAxXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYSA9IChhIDw8IDMwKSB8IChhID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZSBeIGEgXiBiO1xyXG4gICAgICAgICAgICAgICAgdCA9IChkIDw8IDUpIHwgKGQgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGMgPSB0ICsgZiArIGMgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAyXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZSA9IChlIDw8IDMwKSB8IChlID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZCBeIGUgXiBhO1xyXG4gICAgICAgICAgICAgICAgdCA9IChjIDw8IDUpIHwgKGMgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGIgPSB0ICsgZiArIGIgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAzXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZCA9IChkIDw8IDMwKSB8IChkID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gYyBeIGQgXiBlO1xyXG4gICAgICAgICAgICAgICAgdCA9IChiIDw8IDUpIHwgKGIgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGEgPSB0ICsgZiArIGEgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyA0XSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYyA9IChjIDw8IDMwKSB8IChjID4+PiAyKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKDsgaiA8IDYwOyBqICs9IDUpIHtcclxuICAgICAgICAgICAgICAgIGYgPSAoYiAmIGMpIHwgKGIgJiBkKSB8IChjICYgZCk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGEgPDwgNSkgfCAoYSA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgZSA9IHQgKyBmICsgZSAtIDE4OTQwMDc1ODggKyBibG9ja3Nbal0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGIgPSAoYiA8PCAzMCkgfCAoYiA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChhICYgYikgfCAoYSAmIGMpIHwgKGIgJiBjKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZSA8PCA1KSB8IChlID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBkID0gdCArIGYgKyBkIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgMV0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGEgPSAoYSA8PCAzMCkgfCAoYSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChlICYgYSkgfCAoZSAmIGIpIHwgKGEgJiBiKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZCA8PCA1KSB8IChkID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBjID0gdCArIGYgKyBjIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgMl0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGUgPSAoZSA8PCAzMCkgfCAoZSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChkICYgZSkgfCAoZCAmIGEpIHwgKGUgJiBhKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYyA8PCA1KSB8IChjID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBiID0gdCArIGYgKyBiIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgM10gPDwgMDtcclxuICAgICAgICAgICAgICAgIGQgPSAoZCA8PCAzMCkgfCAoZCA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChjICYgZCkgfCAoYyAmIGUpIHwgKGQgJiBlKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYiA8PCA1KSB8IChiID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBhID0gdCArIGYgKyBhIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgNF0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGMgPSAoYyA8PCAzMCkgfCAoYyA+Pj4gMik7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcig7IGogPCA4MDsgaiArPSA1KSB7XHJcbiAgICAgICAgICAgICAgICBmID0gYiBeIGMgXiBkO1xyXG4gICAgICAgICAgICAgICAgdCA9IChhIDw8IDUpIHwgKGEgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGUgPSB0ICsgZiArIGUgLSA4OTk0OTc1MTQgKyBibG9ja3Nbal0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGIgPSAoYiA8PCAzMCkgfCAoYiA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IGEgXiBiIF4gYztcclxuICAgICAgICAgICAgICAgIHQgPSAoZSA8PCA1KSB8IChlID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBkID0gdCArIGYgKyBkIC0gODk5NDk3NTE0ICsgYmxvY2tzW2ogKyAxXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYSA9IChhIDw8IDMwKSB8IChhID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZSBeIGEgXiBiO1xyXG4gICAgICAgICAgICAgICAgdCA9IChkIDw8IDUpIHwgKGQgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGMgPSB0ICsgZiArIGMgLSA4OTk0OTc1MTQgKyBibG9ja3NbaiArIDJdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBlID0gKGUgPDwgMzApIHwgKGUgPj4+IDIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGYgPSBkIF4gZSBeIGE7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGMgPDwgNSkgfCAoYyA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgYiA9IHQgKyBmICsgYiAtIDg5OTQ5NzUxNCArIGJsb2Nrc1tqICsgM10gPDwgMDtcclxuICAgICAgICAgICAgICAgIGQgPSAoZCA8PCAzMCkgfCAoZCA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IGMgXiBkIF4gZTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYiA8PCA1KSB8IChiID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBhID0gdCArIGYgKyBhIC0gODk5NDk3NTE0ICsgYmxvY2tzW2ogKyA0XSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYyA9IChjIDw8IDMwKSB8IChjID4+PiAyKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgdGhpcy5oMCA9IHRoaXMuaDAgKyBhIDw8IDA7XHJcbiAgICAgICAgICAgIHRoaXMuaDEgPSB0aGlzLmgxICsgYiA8PCAwO1xyXG4gICAgICAgICAgICB0aGlzLmgyID0gdGhpcy5oMiArIGMgPDwgMDtcclxuICAgICAgICAgICAgdGhpcy5oMyA9IHRoaXMuaDMgKyBkIDw8IDA7XHJcbiAgICAgICAgICAgIHRoaXMuaDQgPSB0aGlzLmg0ICsgZSA8PCAwO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIFNoYTEucHJvdG90eXBlLmhleCA9IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG5cclxuICAgICAgICAgICAgdmFyIGgwID0gdGhpcy5oMCwgaDEgPSB0aGlzLmgxLCBoMiA9IHRoaXMuaDIsIGgzID0gdGhpcy5oMywgaDQgPSB0aGlzLmg0O1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIEhFWF9DSEFSU1soaDAgPj4gMjgpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDI0KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDAgPj4gMjApICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDE2KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDAgPj4gMTIpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDgpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMCA+PiA0KSAmIDB4MEZdICsgSEVYX0NIQVJTW2gwICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAyOCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gMjQpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAyMCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gMTYpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAxMikgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gOCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgxID4+IDQpICYgMHgwRl0gKyBIRVhfQ0hBUlNbaDEgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDI4KSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiAyNCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDIwKSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiAxNikgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDEyKSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiA4KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDIgPj4gNCkgJiAweDBGXSArIEhFWF9DSEFSU1toMiAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMjgpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDI0KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMjApICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDE2KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMTIpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDgpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMyA+PiA0KSAmIDB4MEZdICsgSEVYX0NIQVJTW2gzICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAyOCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gMjQpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAyMCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gMTYpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAxMikgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gOCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGg0ID4+IDQpICYgMHgwRl0gKyBIRVhfQ0hBUlNbaDQgJiAweDBGXTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS50b1N0cmluZyA9IFNoYTEucHJvdG90eXBlLmhleDtcclxuXHJcbiAgICAgICAgU2hhMS5wcm90b3R5cGUuZGlnZXN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICB0aGlzLmZpbmFsaXplKCk7XHJcblxyXG4gICAgICAgICAgICB2YXIgaDAgPSB0aGlzLmgwLCBoMSA9IHRoaXMuaDEsIGgyID0gdGhpcy5oMiwgaDMgPSB0aGlzLmgzLCBoNCA9IHRoaXMuaDQ7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gW1xyXG4gICAgICAgICAgICAgICAgKGgwID4+IDI0KSAmIDB4RkYsIChoMCA+PiAxNikgJiAweEZGLCAoaDAgPj4gOCkgJiAweEZGLCBoMCAmIDB4RkYsXHJcbiAgICAgICAgICAgICAgICAoaDEgPj4gMjQpICYgMHhGRiwgKGgxID4+IDE2KSAmIDB4RkYsIChoMSA+PiA4KSAmIDB4RkYsIGgxICYgMHhGRixcclxuICAgICAgICAgICAgICAgIChoMiA+PiAyNCkgJiAweEZGLCAoaDIgPj4gMTYpICYgMHhGRiwgKGgyID4+IDgpICYgMHhGRiwgaDIgJiAweEZGLFxyXG4gICAgICAgICAgICAgICAgKGgzID4+IDI0KSAmIDB4RkYsIChoMyA+PiAxNikgJiAweEZGLCAoaDMgPj4gOCkgJiAweEZGLCBoMyAmIDB4RkYsXHJcbiAgICAgICAgICAgICAgICAoaDQgPj4gMjQpICYgMHhGRiwgKGg0ID4+IDE2KSAmIDB4RkYsIChoNCA+PiA4KSAmIDB4RkYsIGg0ICYgMHhGRlxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIFNoYTEucHJvdG90eXBlLmFycmF5ID0gU2hhMS5wcm90b3R5cGUuZGlnZXN0O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS5hcnJheUJ1ZmZlciA9IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG5cclxuICAgICAgICAgICAgdmFyIGJ1ZmZlciA9IG5ldyBBcnJheUJ1ZmZlcigyMCk7XHJcbiAgICAgICAgICAgIHZhciBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyhidWZmZXIpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgdGhpcy5oMCk7XHJcbiAgICAgICAgICAgIGRhdGFWaWV3LnNldFVpbnQzMig0LCB0aGlzLmgxKTtcclxuICAgICAgICAgICAgZGF0YVZpZXcuc2V0VWludDMyKDgsIHRoaXMuaDIpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMTIsIHRoaXMuaDMpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMTYsIHRoaXMuaDQpO1xyXG4gICAgICAgICAgICByZXR1cm4gYnVmZmVyO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHZhciBleHBvcnRzID0gY3JlYXRlTWV0aG9kKCk7XHJcblxyXG4gICAgICAgIGlmIChDT01NT05fSlMpIHtcclxuICAgICAgICAgICAgbW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHJvb3QuX3NoYTEgPSBleHBvcnRzO1xyXG4gICAgICAgICAgICBpZiAoQU1EKSB7XHJcbiAgICAgICAgICAgICAgICBkZWZpbmUoZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBleHBvcnRzO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBlbmNvZGVfdGV4dChidWZmZXI6IHN0cmluZykgOiBBcnJheUJ1ZmZlciB7XHJcbiAgICAgICAgaWYgKCh3aW5kb3cgYXMgYW55KS5UZXh0RW5jb2Rlcikge1xyXG4gICAgICAgICAgICByZXR1cm4gbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKGJ1ZmZlcikuYnVmZmVyO1xyXG4gICAgICAgIH1cclxuICAgICAgICBsZXQgdXRmOCA9IHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChidWZmZXIpKTtcclxuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkodXRmOC5sZW5ndGgpO1xyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdXRmOC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICByZXN1bHRbaV0gPSB1dGY4LmNoYXJDb2RlQXQoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiByZXN1bHQuYnVmZmVyO1xyXG4gICAgfVxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHNoYTEobWVzc2FnZTogc3RyaW5nIHwgQXJyYXlCdWZmZXIpIDogUHJvbWlzZUxpa2U8QXJyYXlCdWZmZXI+IHtcclxuICAgICAgICBpZighKHR5cGVvZihtZXNzYWdlKSA9PT0gXCJzdHJpbmdcIiB8fCBtZXNzYWdlIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpKSB0aHJvdyBcIkludmFsaWQgdHlwZSFcIjtcclxuXHJcbiAgICAgICAgbGV0IGJ1ZmZlciA9IG1lc3NhZ2UgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlciA/IG1lc3NhZ2UgOiBlbmNvZGVfdGV4dChtZXNzYWdlIGFzIHN0cmluZyk7XHJcblxyXG4gICAgICAgIGlmKCFjcnlwdG8gfHwgIWNyeXB0by5zdWJ0bGUgfHwgIWNyeXB0by5zdWJ0bGUuZGlnZXN0IHx8IC9FZGdlLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpKVxyXG4gICAgICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8QXJyYXlCdWZmZXI+KHJlc29sdmUgPT4ge1xyXG4gICAgICAgICAgICAgICAgcmVzb2x2ZShfc2hhMS5hcnJheUJ1ZmZlcihidWZmZXIgYXMgQXJyYXlCdWZmZXIpKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICByZXR1cm4gY3J5cHRvLnN1YnRsZS5kaWdlc3QoXCJTSEEtMVwiLCBidWZmZXIpO1xyXG4gICAgfVxyXG5cclxufSIsImV4cG9ydCBmdW5jdGlvbiBndWlkKCkge1xyXG4gICAgZnVuY3Rpb24gczQoKSB7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKDEgKyBNYXRoLnJhbmRvbSgpKSAqIDB4MTAwMDApXHJcbiAgICAgICAgICAgIC50b1N0cmluZygxNilcclxuICAgICAgICAgICAgLnN1YnN0cmluZygxKTtcclxuICAgIH1cclxuICAgIHJldHVybiBzNCgpICsgczQoKSArICctJyArIHM0KCkgKyAnLScgKyBzNCgpICsgJy0nICsgczQoKSArICctJyArIHM0KCkgKyBzNCgpICsgczQoKTtcclxufSIsImltcG9ydCB7Z3VpZH0gZnJvbSBcIi4uL2NyeXB0by91aWRcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vbG9nXCI7XHJcbmltcG9ydCB7TWVzc2FnZUhlbHBlcn0gZnJvbSBcIi4uL3VpL2ZyYW1lcy9jaGF0XCI7XHJcbmltcG9ydCB7U3RhdGljU2V0dGluZ3N9IGZyb20gXCIuLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge2NyZWF0ZUVycm9yTW9kYWx9IGZyb20gXCIuLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5cclxuZXhwb3J0IG5hbWVzcGFjZSBpMThuIHtcclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25LZXkge1xyXG4gICAgICAgIG1lc3NhZ2U6IHN0cmluZztcclxuICAgICAgICBsaW5lPzogbnVtYmVyO1xyXG4gICAgICAgIGNoYXJhY3Rlcj86IG51bWJlcjtcclxuICAgICAgICBmaWxlbmFtZT86IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIFRyYW5zbGF0aW9uIHtcclxuICAgICAgICBrZXk6IFRyYW5zbGF0aW9uS2V5O1xyXG4gICAgICAgIHRyYW5zbGF0ZWQ6IHN0cmluZztcclxuICAgICAgICBmbGFncz86IHN0cmluZ1tdO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ29udHJpYnV0b3Ige1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBlbWFpbDogc3RyaW5nO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25GaWxlIHtcclxuICAgICAgICBwYXRoOiBzdHJpbmc7XHJcbiAgICAgICAgZnVsbF91cmw6IHN0cmluZztcclxuXHJcbiAgICAgICAgdHJhbnNsYXRpb25zOiBUcmFuc2xhdGlvbltdO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgUmVwb3NpdG9yeVRyYW5zbGF0aW9uIHtcclxuICAgICAgICBrZXk6IHN0cmluZztcclxuICAgICAgICBwYXRoOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIGNvdW50cnlfY29kZTogc3RyaW5nO1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBjb250cmlidXRvcnM6IENvbnRyaWJ1dG9yW107XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25SZXBvc2l0b3J5IHtcclxuICAgICAgICB1bmlxdWVfaWQ6IHN0cmluZztcclxuICAgICAgICB1cmw6IHN0cmluZztcclxuICAgICAgICBuYW1lPzogc3RyaW5nO1xyXG4gICAgICAgIGNvbnRhY3Q/OiBzdHJpbmc7XHJcbiAgICAgICAgdHJhbnNsYXRpb25zPzogUmVwb3NpdG9yeVRyYW5zbGF0aW9uW107XHJcbiAgICAgICAgbG9hZF90aW1lc3RhbXA/OiBudW1iZXI7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGxldCB0cmFuc2xhdGlvbnM6IFRyYW5zbGF0aW9uW10gPSBbXTtcclxuICAgIGxldCBmYXN0X3RyYW5zbGF0ZTogeyBba2V5OnN0cmluZ106c3RyaW5nOyB9ID0ge307XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gdHIobWVzc2FnZTogc3RyaW5nLCBrZXk/OiBzdHJpbmcpIHtcclxuICAgICAgICBjb25zdCBzbG9wcHkgPSBmYXN0X3RyYW5zbGF0ZVttZXNzYWdlXTtcclxuICAgICAgICBpZihzbG9wcHkpIHJldHVybiBzbG9wcHk7XHJcblxyXG4gICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LkkxOE4sIFwiVHJhbnNsYXRpbmcgXFxcIiVzXFxcIi4gRGVmYXVsdDogXFxcIiVzXFxcIlwiLCBrZXksIG1lc3NhZ2UpO1xyXG5cclxuICAgICAgICBsZXQgdHJhbnNsYXRlZCA9IG1lc3NhZ2U7XHJcbiAgICAgICAgZm9yKGNvbnN0IHRyYW5zbGF0aW9uIG9mIHRyYW5zbGF0aW9ucykge1xyXG4gICAgICAgICAgICBpZih0cmFuc2xhdGlvbi5rZXkubWVzc2FnZSA9PSBtZXNzYWdlKSB7XHJcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGVkID0gdHJhbnNsYXRpb24udHJhbnNsYXRlZDtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBmYXN0X3RyYW5zbGF0ZVttZXNzYWdlXSA9IHRyYW5zbGF0ZWQ7XHJcbiAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHRyYShtZXNzYWdlOiBzdHJpbmcsIC4uLmFyZ3M6IGFueVtdKSB7XHJcbiAgICAgICAgbWVzc2FnZSA9IHRyKG1lc3NhZ2UpO1xyXG4gICAgICAgIHJldHVybiBNZXNzYWdlSGVscGVyLmZvcm1hdE1lc3NhZ2UobWVzc2FnZSwgLi4uYXJncyk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZnVuY3Rpb24gbG9hZF90cmFuc2xhdGlvbl9maWxlKHVybDogc3RyaW5nLCBwYXRoOiBzdHJpbmcpIDogUHJvbWlzZTxUcmFuc2xhdGlvbkZpbGU+IHtcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8VHJhbnNsYXRpb25GaWxlPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICQuYWpheCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IHVybCxcclxuICAgICAgICAgICAgICAgIGFzeW5jOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgc3VjY2VzczogcmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWxlID0gKHR5cGVvZihyZXN1bHQpID09PSBcInN0cmluZ1wiID8gSlNPTi5wYXJzZShyZXN1bHQpIDogcmVzdWx0KSBhcyBUcmFuc2xhdGlvbkZpbGU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFmaWxlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJJbnZhbGlkIGpzb25cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUuZnVsbF91cmwgPSB1cmw7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aCA9IHBhdGg7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAvL1RPRE86IFZhbGlkYXRlIGZpbGVcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShmaWxlKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkkxOE4sIHRyKFwiRmFpbGVkIHRvIGxvYWQgdHJhbnNsYXRpb24gZmlsZSAlcy4gRmFpbGVkIHRvIHBhcnNlIG9yIHByb2Nlc3MganNvbjogJW9cIiksIHVybCwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJGYWlsZWQgdG8gcHJvY2VzcyBvciBwYXJzZSBqc29uIVwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBlcnJvcikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlamVjdCh0cihcIkZhaWxlZCB0byBsb2FkIGZpbGU6IFwiKSArIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSlcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9hZF9maWxlKHVybDogc3RyaW5nLCBwYXRoOiBzdHJpbmcpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIGxvYWRfdHJhbnNsYXRpb25fZmlsZSh1cmwsIHBhdGgpLnRoZW4oYXN5bmMgcmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgLyogVE9ETzogSW1wcm92ZSB0aGlzIHRlc3Q/ISovXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0cihcIkR1bW15IHRyYW5zbGF0aW9uIHRlc3RcIik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IFwiZHVtbXkgdGVzdCBmYWlsZWRcIjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJTdWNjZXNzZnVsbHkgaW5pdGlhbGl6ZWQgdXAgdHJhbnNsYXRpb24gZmlsZSBmcm9tICVzXCIpLCB1cmwpO1xyXG4gICAgICAgICAgICB0cmFuc2xhdGlvbnMgPSByZXN1bHQudHJhbnNsYXRpb25zO1xyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJGYWlsZWQgdG8gbG9hZCB0cmFuc2xhdGlvbiBmaWxlIGZyb20gXFxcIiVzXFxcIi4gRXJyb3I6ICVvXCIpLCB1cmwsIGVycm9yKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBmdW5jdGlvbiBsb2FkX3JlcG9zaXRvcnkwKHJlcG86IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSwgcmVsb2FkOiBib29sZWFuKSB7XHJcbiAgICAgICAgaWYoIXJlcG8ubG9hZF90aW1lc3RhbXAgfHwgcmVwby5sb2FkX3RpbWVzdGFtcCA8IDEwMDAgfHwgcmVsb2FkKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm9fanNvbiA9IGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgICAgICQuYWpheCh7XHJcbiAgICAgICAgICAgICAgICAgICAgdXJsOiByZXBvLnVybCArIFwiL2luZm8uanNvblwiLFxyXG4gICAgICAgICAgICAgICAgICAgIGFzeW5jOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgIGNhY2hlOiAhcmVsb2FkLFxyXG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGZpbGUgPSAodHlwZW9mKHJlc3VsdCkgPT09IFwic3RyaW5nXCIgPyBKU09OLnBhcnNlKHJlc3VsdCkgOiByZXN1bHQpIGFzIFRyYW5zbGF0aW9uRmlsZTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIWZpbGUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChcIkludmFsaWQganNvblwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShmaWxlKTtcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBlcnJvcikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJGYWlsZWQgdG8gbG9hZCBmaWxlOiBcIikgKyBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHJlcG8sIGluZm9fanNvbik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZighcmVwby51bmlxdWVfaWQpXHJcbiAgICAgICAgICAgIHJlcG8udW5pcXVlX2lkID0gZ3VpZCgpO1xyXG5cclxuICAgICAgICByZXBvLnRyYW5zbGF0aW9ucyA9IHJlcG8udHJhbnNsYXRpb25zIHx8IFtdO1xyXG4gICAgICAgIHJlcG8ubG9hZF90aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkX3JlcG9zaXRvcnkodXJsOiBzdHJpbmcpIDogUHJvbWlzZTxUcmFuc2xhdGlvblJlcG9zaXRvcnk+IHtcclxuICAgICAgICBjb25zdCByZXN1bHQgPSB7fSBhcyBUcmFuc2xhdGlvblJlcG9zaXRvcnk7XHJcbiAgICAgICAgcmVzdWx0LnVybCA9IHVybDtcclxuICAgICAgICBhd2FpdCBsb2FkX3JlcG9zaXRvcnkwKHJlc3VsdCwgZmFsc2UpO1xyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IG5hbWVzcGFjZSBjb25maWcge1xyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25Db25maWcge1xyXG4gICAgICAgICAgICBjdXJyZW50X3JlcG9zaXRvcnlfdXJsPzogc3RyaW5nO1xyXG4gICAgICAgICAgICBjdXJyZW50X2xhbmd1YWdlPzogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgY3VycmVudF90cmFuc2xhdGlvbl91cmw/OiBzdHJpbmc7XHJcbiAgICAgICAgICAgIGN1cnJlbnRfdHJhbnNsYXRpb25fcGF0aD86IHN0cmluZztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgUmVwb3NpdG9yeUNvbmZpZyB7XHJcbiAgICAgICAgICAgIHJlcG9zaXRvcmllcz86IHtcclxuICAgICAgICAgICAgICAgIHVybD86IHN0cmluZztcclxuICAgICAgICAgICAgICAgIHJlcG9zaXRvcnk/OiBUcmFuc2xhdGlvblJlcG9zaXRvcnk7XHJcbiAgICAgICAgICAgIH1bXTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IHJlcG9zaXRvcnlfY29uZmlnX2tleSA9IFwiaTE4bi5yZXBvc2l0b3J5XCI7XHJcbiAgICAgICAgbGV0IF9jYWNoZWRfcmVwb3NpdG9yeV9jb25maWc6IFJlcG9zaXRvcnlDb25maWc7XHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHJlcG9zaXRvcnlfY29uZmlnKCkge1xyXG4gICAgICAgICAgICBpZihfY2FjaGVkX3JlcG9zaXRvcnlfY29uZmlnKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIF9jYWNoZWRfcmVwb3NpdG9yeV9jb25maWc7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBjb25maWdfc3RyaW5nID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0ocmVwb3NpdG9yeV9jb25maWdfa2V5KTtcclxuICAgICAgICAgICAgbGV0IGNvbmZpZzogUmVwb3NpdG9yeUNvbmZpZztcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGNvbmZpZyA9IGNvbmZpZ19zdHJpbmcgPyBKU09OLnBhcnNlKGNvbmZpZ19zdHJpbmcpIDoge307XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JMThOLCB0cihcIkZhaWxlZCB0byBwYXJzZSByZXBvc2l0b3J5IGNvbmZpZzogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBjb25maWcucmVwb3NpdG9yaWVzID0gY29uZmlnLnJlcG9zaXRvcmllcyB8fCBbXTtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IHJlcG8gb2YgY29uZmlnLnJlcG9zaXRvcmllcylcclxuICAgICAgICAgICAgICAgIChyZXBvLnJlcG9zaXRvcnkgfHwge2xvYWRfdGltZXN0YW1wOiAwfSkubG9hZF90aW1lc3RhbXAgPSAwO1xyXG5cclxuICAgICAgICAgICAgaWYoY29uZmlnLnJlcG9zaXRvcmllcy5sZW5ndGggPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgLy9BZGQgdGhlIGRlZmF1bHQgVGVhU3BlYWsgcmVwb3NpdG9yeVxyXG4gICAgICAgICAgICAgICAgbG9hZF9yZXBvc2l0b3J5KFN0YXRpY1NldHRpbmdzLmluc3RhbmNlLnN0YXRpYyhcImkxOG4uZGVmYXVsdF9yZXBvc2l0b3J5XCIsIFwiaHR0cHM6Ly93ZWIudGVhc3BlYWsuZGUvaTE4bi9cIikpLnRoZW4ocmVwbyA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJTdWNjZXNzZnVsbHkgYWRkZWQgZGVmYXVsdCByZXBvc2l0b3J5IGZyb20gXFxcIiVzXFxcIi5cIiksIHJlcG8udXJsKTtcclxuICAgICAgICAgICAgICAgICAgICByZWdpc3Rlcl9yZXBvc2l0b3J5KHJlcG8pO1xyXG4gICAgICAgICAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkkxOE4sIHRyKFwiRmFpbGVkIHRvIGFkZCBkZWZhdWx0IHJlcG9zaXRvcnkuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiBfY2FjaGVkX3JlcG9zaXRvcnlfY29uZmlnID0gY29uZmlnO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHNhdmVfcmVwb3NpdG9yeV9jb25maWcoKSB7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKHJlcG9zaXRvcnlfY29uZmlnX2tleSwgSlNPTi5zdHJpbmdpZnkoX2NhY2hlZF9yZXBvc2l0b3J5X2NvbmZpZykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc3QgdHJhbnNsYXRpb25fY29uZmlnX2tleSA9IFwiaTE4bi50cmFuc2xhdGlvblwiO1xyXG4gICAgICAgIGxldCBfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZzogVHJhbnNsYXRpb25Db25maWc7XHJcblxyXG4gICAgICAgIGV4cG9ydCBmdW5jdGlvbiB0cmFuc2xhdGlvbl9jb25maWcoKSA6IFRyYW5zbGF0aW9uQ29uZmlnIHtcclxuICAgICAgICAgICAgaWYoX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWcpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWc7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBjb25maWdfc3RyaW5nID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0odHJhbnNsYXRpb25fY29uZmlnX2tleSk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZyA9IGNvbmZpZ19zdHJpbmcgPyBKU09OLnBhcnNlKGNvbmZpZ19zdHJpbmcpIDoge307XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JMThOLCB0cihcIkZhaWxlZCB0byBpbml0aWFsaXplIHRyYW5zbGF0aW9uIGNvbmZpZy4gVXNpbmcgZGVmYXVsdCBvbmUuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWcgPSB7fSBhcyBhbnk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIF9jYWNoZWRfdHJhbnNsYXRpb25fY29uZmlnO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHNhdmVfdHJhbnNsYXRpb25fY29uZmlnKCkge1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSh0cmFuc2xhdGlvbl9jb25maWdfa2V5LCBKU09OLnN0cmluZ2lmeShfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZykpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJfcmVwb3NpdG9yeShyZXBvc2l0b3J5OiBUcmFuc2xhdGlvblJlcG9zaXRvcnkpIHtcclxuICAgICAgICBpZighcmVwb3NpdG9yeSkgcmV0dXJuO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgcmVwbyBvZiBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXMpXHJcbiAgICAgICAgICAgIGlmKHJlcG8udXJsID09IHJlcG9zaXRvcnkudXJsKSByZXR1cm47XHJcblxyXG4gICAgICAgIGNvbmZpZy5yZXBvc2l0b3J5X2NvbmZpZygpLnJlcG9zaXRvcmllcy5wdXNoKHJlcG9zaXRvcnkpO1xyXG4gICAgICAgIGNvbmZpZy5zYXZlX3JlcG9zaXRvcnlfY29uZmlnKCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGV4cG9ydCBmdW5jdGlvbiByZWdpc3RlcmVkX3JlcG9zaXRvcmllcygpIDogVHJhbnNsYXRpb25SZXBvc2l0b3J5W10ge1xyXG4gICAgICAgIHJldHVybiBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXMubWFwKGUgPT4gZS5yZXBvc2l0b3J5IHx8IHt1cmw6IGUudXJsLCBsb2FkX3RpbWVzdGFtcDogMH0gYXMgVHJhbnNsYXRpb25SZXBvc2l0b3J5KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlbGV0ZV9yZXBvc2l0b3J5KHJlcG9zaXRvcnk6IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSkge1xyXG4gICAgICAgIGlmKCFyZXBvc2l0b3J5KSByZXR1cm47XHJcblxyXG4gICAgICAgIGZvcihjb25zdCByZXBvIG9mIFsuLi5jb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXNdKVxyXG4gICAgICAgICAgICBpZihyZXBvLnVybCA9PSByZXBvc2l0b3J5LnVybCkge1xyXG4gICAgICAgICAgICAgICAgY29uZmlnLnJlcG9zaXRvcnlfY29uZmlnKCkucmVwb3NpdG9yaWVzLnJlbW92ZShyZXBvKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIGNvbmZpZy5zYXZlX3JlcG9zaXRvcnlfY29uZmlnKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGl0ZXJhdGVfcmVwb3NpdG9yaWVzKGNhbGxiYWNrX2VudHJ5OiAocmVwb3NpdG9yeTogVHJhbnNsYXRpb25SZXBvc2l0b3J5KSA9PiBhbnkpIHtcclxuICAgICAgICBjb25zdCBwcm9taXNlcyA9IFtdO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgcmVwb3NpdG9yeSBvZiByZWdpc3RlcmVkX3JlcG9zaXRvcmllcygpKSB7XHJcbiAgICAgICAgICAgIHByb21pc2VzLnB1c2gobG9hZF9yZXBvc2l0b3J5MChyZXBvc2l0b3J5LCBmYWxzZSkudGhlbigoKSA9PiBjYWxsYmFja19lbnRyeShyZXBvc2l0b3J5KSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSTE4TiwgXCJGYWlsZWQgdG8gZmV0Y2ggcmVwb3NpdG9yeSAlcy4gZXJyb3I6ICVvXCIsIHJlcG9zaXRvcnkudXJsLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH0pKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHByb21pc2VzKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc2VsZWN0X3RyYW5zbGF0aW9uKHJlcG9zaXRvcnk6IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSwgZW50cnk6IFJlcG9zaXRvcnlUcmFuc2xhdGlvbikge1xyXG4gICAgICAgIGNvbnN0IGNmZyA9IGNvbmZpZy50cmFuc2xhdGlvbl9jb25maWcoKTtcclxuXHJcbiAgICAgICAgaWYoZW50cnkgJiYgcmVwb3NpdG9yeSkge1xyXG4gICAgICAgICAgICBjZmcuY3VycmVudF9sYW5ndWFnZSA9IGVudHJ5Lm5hbWU7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3JlcG9zaXRvcnlfdXJsID0gcmVwb3NpdG9yeS51cmw7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3RyYW5zbGF0aW9uX3VybCA9IHJlcG9zaXRvcnkudXJsICsgZW50cnkucGF0aDtcclxuICAgICAgICAgICAgY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fcGF0aCA9IGVudHJ5LnBhdGg7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgY2ZnLmN1cnJlbnRfbGFuZ3VhZ2UgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3JlcG9zaXRvcnlfdXJsID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICBjZmcuY3VycmVudF90cmFuc2xhdGlvbl91cmwgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3RyYW5zbGF0aW9uX3BhdGggPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25maWcuc2F2ZV90cmFuc2xhdGlvbl9jb25maWcoKTtcclxuICAgIH1cclxuXHJcbiAgICAvKiBBVFRFTlRJT046IFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBiZWZvcmUgbW9zdCBvdGhlciBsaWJyYXJ5IGluaXppYWxpc2F0aW9ucyEgKi9cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIGNvbnN0IHJjZmcgPSBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKTsgLyogaW5pdGlhbGl6ZSAqL1xyXG4gICAgICAgIGNvbnN0IGNmZyA9IGNvbmZpZy50cmFuc2xhdGlvbl9jb25maWcoKTtcclxuXHJcbiAgICAgICAgaWYoY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBhd2FpdCBsb2FkX2ZpbGUoY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsLCBjZmcuY3VycmVudF90cmFuc2xhdGlvbl9wYXRoKTtcclxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBzZWxlY3RlZCB0cmFuc2xhdGlvbjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIGNvbnN0IHNob3dfZXJyb3IgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY3JlYXRlRXJyb3JNb2RhbCh0cihcIlRyYW5zbGF0aW9uIFN5c3RlbVwiKSwgdHJhKFwiRmFpbGVkIHRvIGxvYWQgY3VycmVudCBzZWxlY3RlZCB0cmFuc2xhdGlvbiBmaWxlLns6YnI6fUZpbGU6IHswfXs6YnI6fUVycm9yOiB7MX17OmJyOn17OmJyOn1Vc2luZyBkZWZhdWx0IGZhbGxiYWNrIHRyYW5zbGF0aW9ucy5cIiwgY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsLCBlcnJvcikpLm9wZW4oKVxyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgIGlmKGxvYWRlci5ydW5uaW5nKCkpXHJcbiAgICAgICAgICAgICAgICAgICAgbG9hZGVyLnJlZ2lzdGVyX3Rhc2sobG9hZGVyLlN0YWdlLkpBVkFTQ1JJUFRfSU5JVElBTElaSU5HLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yaXR5OiAxMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHNob3dfZXJyb3IoKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogXCJJMThOIGVycm9yIGRpc3BsYXlcIlxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICAgICAgICAgIHNob3dfZXJyb3IoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBhd2FpdCBsb2FkX2ZpbGUoXCJodHRwOi8vbG9jYWxob3N0L2hvbWUvVGVhU3BlYWsvVGVhU3BlYWsvV2ViLUNsaWVudC93ZWIvZW52aXJvbm1lbnQvZGV2ZWxvcG1lbnQvaTE4bi9kZV9ERS50cmFuc2xhdGlvblwiKTtcclxuICAgICAgICAvLyBhd2FpdCBsb2FkX2ZpbGUoXCJodHRwOi8vbG9jYWxob3N0L2hvbWUvVGVhU3BlYWsvVGVhU3BlYWsvV2ViLUNsaWVudC93ZWIvZW52aXJvbm1lbnQvZGV2ZWxvcG1lbnQvaTE4bi90ZXN0Lmpzb25cIik7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8vIEB0cy1pZ25vcmVcclxuY29uc3QgdHI6IHR5cGVvZiBpMThuLnRyID0gaTE4bi50cjtcclxuY29uc3QgdHJhOiB0eXBlb2YgaTE4bi50cmEgPSBpMThuLnRyYTtcclxuXHJcbih3aW5kb3cgYXMgYW55KS50ciA9IGkxOG4udHI7XHJcbih3aW5kb3cgYXMgYW55KS50cmEgPSBpMThuLnRyYTsiLCJpbXBvcnQgc3Bhd25ZZXNObyA9IE1vZGFscy5zcGF3blllc05vO1xyXG5pbXBvcnQge0Nvbm5lY3Rpb25IYW5kbGVyfSBmcm9tIFwiLi9Db25uZWN0aW9uSGFuZGxlclwiO1xyXG5pbXBvcnQge2JpcGN9IGZyb20gXCIuL0Jyb3dzZXJJUENcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi9sb2dcIjtcclxuaW1wb3J0IHtwcm9maWxlc30gZnJvbSBcIi4vcHJvZmlsZXMvQ29ubmVjdGlvblByb2ZpbGVcIjtcclxuaW1wb3J0IHtNb2RhbHN9IGZyb20gXCIuL3VpL21vZGFsL01vZGFsQ29ubmVjdFwiO1xyXG5pbXBvcnQge3NldHRpbmdzLCBTZXR0aW5nc30gZnJvbSBcIi4vc2V0dGluZ3NcIjtcclxuaW1wb3J0IHtpMThufSBmcm9tIFwiLi9pMThuL2xvY2FsaXplXCI7XHJcbmltcG9ydCB7Y3JlYXRlSW5mb01vZGFsfSBmcm9tIFwiLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5pbXBvcnQge01lc3NhZ2VIZWxwZXJ9IGZyb20gXCIuL3VpL2ZyYW1lcy9jaGF0XCI7XHJcblxyXG5leHBvcnQgY29uc3QganNfcmVuZGVyID0gd2luZG93LmpzcmVuZGVyIHx8ICQ7XHJcbmV4cG9ydCBjb25zdCBuYXRpdmVfY2xpZW50ID0gd2luZG93LnJlcXVpcmUgIT09IHVuZGVmaW5lZDtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRVc2VyTWVkaWFGdW5jdGlvblByb21pc2UoKSA6IChjb25zdHJhaW50czogTWVkaWFTdHJlYW1Db25zdHJhaW50cykgPT4gUHJvbWlzZTxNZWRpYVN0cmVhbT4ge1xyXG4gICAgaWYoJ21lZGlhRGV2aWNlcycgaW4gbmF2aWdhdG9yICYmICdnZXRVc2VyTWVkaWEnIGluIG5hdmlnYXRvci5tZWRpYURldmljZXMpXHJcbiAgICAgICAgcmV0dXJuIGNvbnN0cmFpbnRzID0+IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzKTtcclxuXHJcbiAgICBjb25zdCBfY2FsbGJhY2tlZF9mdW5jdGlvbiA9IG5hdmlnYXRvci5nZXRVc2VyTWVkaWEgfHwgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYSB8fCBuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhO1xyXG4gICAgaWYoIV9jYWxsYmFja2VkX2Z1bmN0aW9uKVxyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcblxyXG4gICAgcmV0dXJuIGNvbnN0cmFpbnRzID0+IG5ldyBQcm9taXNlPE1lZGlhU3RyZWFtPigocmVzb2x2ZSwgcmVqZWN0KSA9PiBfY2FsbGJhY2tlZF9mdW5jdGlvbihjb25zdHJhaW50cywgcmVzb2x2ZSwgcmVqZWN0KSk7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgV2luZG93IHtcclxuICAgIG9wZW5fY29ubmVjdGVkX3F1ZXN0aW9uOiAoKSA9PiBQcm9taXNlPGJvb2xlYW4+O1xyXG59XHJcblxyXG5leHBvcnQgZGVjbGFyZSBjb25zdCBub2RlUmVxdWlyZTogdHlwZW9mIHJlcXVpcmU7XHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cF9jbG9zZSgpIHtcclxuICAgIHdpbmRvdy5vbmJlZm9yZXVubG9hZCA9IGV2ZW50ID0+IHtcclxuICAgICAgICBpZihwcm9maWxlcy5yZXF1aXJlc19zYXZlKCkpXHJcbiAgICAgICAgICAgIHByb2ZpbGVzLnNhdmUoKTtcclxuXHJcbiAgICAgICAgaWYoIXNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRElTQUJMRV9VTkxPQURfRElBTE9HLCBmYWxzZSkpIHtcclxuICAgICAgICAgICAgY29uc3QgYWN0aXZlX2Nvbm5lY3Rpb25zID0gc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKCkuZmlsdGVyKGUgPT4gZS5jb25uZWN0ZWQpO1xyXG4gICAgICAgICAgICBpZihhY3RpdmVfY29ubmVjdGlvbnMubGVuZ3RoID09IDApIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgIGlmKCFuYXRpdmVfY2xpZW50KSB7XHJcbiAgICAgICAgICAgICAgICBldmVudC5yZXR1cm5WYWx1ZSA9IFwiQXJlIHlvdSByZWFsbHkgc3VyZT88YnI+WW91J3JlIHN0aWxsIGNvbm5lY3RlZCFcIjtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IGRvX2V4aXQgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZHAgPSBzZXJ2ZXJfY29ubmVjdGlvbnMuc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcnMoKS5tYXAoZSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGUuc2VydmVyQ29ubmVjdGlvbi5jb25uZWN0ZWQoKSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBlLnNlcnZlckNvbm5lY3Rpb24uZGlzY29ubmVjdCh0cihcImNsaWVudCBjbG9zZWRcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkubWFwKGUgPT4gZS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybih0cihcIkZhaWxlZCB0byBkaXNjb25uZWN0IGZyb20gc2VydmVyIG9uIGNsaWVudCBjbG9zZTogJW9cIiksIGUpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZXhpdCA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qge3JlbW90ZX0gPSBub2RlUmVxdWlyZSgnZWxlY3Ryb24nKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3RlLmdldEN1cnJlbnRXaW5kb3coKS5jbG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIFByb21pc2UuYWxsKGRwKS50aGVuKGV4aXQpO1xyXG4gICAgICAgICAgICAgICAgICAgIC8qIGZvcmNlIGV4aXQgYWZ0ZXIgMjUwMG1zICovXHJcbiAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dChleGl0LCAyNTAwKTtcclxuICAgICAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgICAgICBpZih3aW5kb3cub3Blbl9jb25uZWN0ZWRfcXVlc3Rpb24pIHtcclxuICAgICAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIGV2ZW50LnJldHVyblZhbHVlID0gXCJxdWVzdGlvblwiO1xyXG4gICAgICAgICAgICAgICAgICAgIHdpbmRvdy5vcGVuX2Nvbm5lY3RlZF9xdWVzdGlvbigpLnRoZW4ocmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYocmVzdWx0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBwcmV2ZW50IHF1aXR0aW5nIGJlY2F1c2Ugd2UgdHJ5IHRvIGRpc2Nvbm5lY3QgKi9cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5vbmJlZm9yZXVubG9hZCA9IGUgPT4gZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIGFsbG93IGEgZm9yY2UgcXVpdCBhZnRlciA1IHNlY29uZHMgKi9cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gd2luZG93Lm9uYmVmb3JldW5sb2FkLCA1MDAwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2V4aXQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAvKiB3ZSdyZSBpbiBkZWJ1Z2dpbmcgbW9kZSAqL1xyXG4gICAgICAgICAgICAgICAgICAgIGRvX2V4aXQoKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWNsYXJlIGZ1bmN0aW9uIG1vbWVudCguLi5hcmd1bWVudHMpIDogYW55O1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBfanNyZW5kZXIoKSA6IGJvb2xlYW4ge1xyXG4gICAgaWYoIWpzX3JlbmRlcikge1xyXG4gICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcihcIk1pc3NpbmcganNyZW5kZXIgZXh0ZW5zaW9uIVwiKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgICBpZighanNfcmVuZGVyLnZpZXdzKSB7XHJcbiAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKFwiTWlzc2luZyBqc3JlbmRlciB2aWV3ZXIgZXh0ZW5zaW9uIVwiKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgICBqc19yZW5kZXIudmlld3Muc2V0dGluZ3MuYWxsb3dDb2RlKHRydWUpO1xyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJybmRcIiwgKGFyZ3VtZW50KSA9PiB7XHJcbiAgICAgICAgbGV0IG1pbiA9IHBhcnNlSW50KGFyZ3VtZW50LnN1YnN0cigwLCBhcmd1bWVudC5pbmRleE9mKCd+JykpKTtcclxuICAgICAgICBsZXQgbWF4ID0gcGFyc2VJbnQoYXJndW1lbnQuc3Vic3RyKGFyZ3VtZW50LmluZGV4T2YoJ34nKSArIDEpKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIChNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAobWluICsgbWF4ICsgMSkgLSBtaW4pKS50b1N0cmluZygpO1xyXG4gICAgfSk7XHJcblxyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJmbXRfZGF0ZVwiLCAoLi4uYXJncykgPT4ge1xyXG4gICAgICAgIHJldHVybiBtb21lbnQoYXJnc1swXSkuZm9ybWF0KGFyZ3NbMV0pO1xyXG4gICAgfSk7XHJcblxyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJ0clwiLCAoLi4uYXJncykgPT4ge1xyXG4gICAgICAgIHJldHVybiB0cihhcmdzWzBdKTtcclxuICAgIH0pO1xyXG5cclxuICAgICQoXCIuanNyZW5kZXItdGVtcGxhdGVcIikuZWFjaCgoaWR4LCBfZW50cnkpID0+IHtcclxuICAgICAgICBpZighanNfcmVuZGVyLnRlbXBsYXRlcyhfZW50cnkuaWQsIF9lbnRyeS5pbm5lckhUTUwpKSB7XHJcbiAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5HRU5FUkFMLCB0cihcIkZhaWxlZCB0byBzZXR1cCBjYWNoZSBmb3IganMgcmVuZGVyZXIgdGVtcGxhdGUgJXMhXCIpLCBfZW50cnkuaWQpO1xyXG4gICAgICAgIH0gZWxzZVxyXG4gICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5HRU5FUkFMLCB0cihcIlN1Y2Nlc3NmdWxseSBsb2FkZWQganNyZW5kZXIgdGVtcGxhdGUgJXNcIiksIF9lbnRyeS5pZCk7XHJcbiAgICB9KTtcclxuICAgIHJldHVybiB0cnVlO1xyXG59XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZSgpIHtcclxuICAgIFNldHRpbmdzLmluaXRpYWxpemUoKTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICAgIGF3YWl0IGkxOG4uaW5pdGlhbGl6ZSgpO1xyXG4gICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZWQgdGhlIHRyYW5zbGF0aW9uIHN5c3RlbSFcXG5FcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICBsb2FkZXIuY3JpdGljYWxfZXJyb3IoXCJGYWlsZWQgdG8gc2V0dXAgdGhlIHRyYW5zbGF0aW9uIHN5c3RlbVwiKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgYmlwYy5zZXR1cCgpO1xyXG59XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZV9hcHAoKSB7XHJcbiAgICB0cnkgeyAvL0luaXRpYWxpemUgbWFpbiB0ZW1wbGF0ZVxyXG4gICAgICAgIGNvbnN0IG1haW4gPSAkKFwiI3RtcGxfbWFpblwiKS5yZW5kZXJUYWcoe1xyXG4gICAgICAgICAgICBtdWx0aV9zZXNzaW9uOiAgIXNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0RJU0FCTEVfTVVMVElfU0VTU0lPTiksXHJcbiAgICAgICAgICAgIGFwcF92ZXJzaW9uOiBhcHAudWlfdmVyc2lvbigpXHJcbiAgICAgICAgfSkuZGl2aWRlcmZ5KCk7XHJcblxyXG4gICAgICAgICQoXCJib2R5XCIpLmFwcGVuZChtYWluKTtcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgZXJyb3IpO1xyXG4gICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcih0cihcIkZhaWxlZCB0byBzZXR1cCBtYWluIHBhZ2UhXCIpKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgY29udHJvbF9iYXIgPSBuZXcgQ29udHJvbEJhcigkKFwiI2NvbnRyb2xfYmFyXCIpKTsgLyogc2V0dXAgdGhlIGNvbnRyb2wgYmFyICovXHJcblxyXG4gICAgaWYoIWF1ZGlvLnBsYXllci5pbml0aWFsaXplKCkpXHJcbiAgICAgICAgY29uc29sZS53YXJuKHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgYXVkaW8gY29udHJvbGxlciFcIikpO1xyXG5cclxuICAgIGF1ZGlvLnBsYXllci5vbl9yZWFkeSgoKSA9PiB7XHJcbiAgICAgICAgaWYoYXVkaW8ucGxheWVyLnNldF9tYXN0ZXJfdm9sdW1lKVxyXG4gICAgICAgICAgICBhdWRpby5wbGF5ZXIub25fcmVhZHkoKCkgPT4gYXVkaW8ucGxheWVyLnNldF9tYXN0ZXJfdm9sdW1lKHNldHRpbmdzLmdsb2JhbChTZXR0aW5ncy5LRVlfU09VTkRfTUFTVEVSKSAvIDEwMCkpO1xyXG4gICAgICAgIGVsc2VcclxuICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJDbGllbnQgZG9lcyBub3Qgc3VwcG9ydCBhdWRpby5wbGF5ZXIuc2V0X21hc3Rlcl92b2x1bWUoKS4uLiBNYXkgY2xpZW50IGlzIHRvbyBvbGQ/XCIpKTtcclxuICAgICAgICBpZihhdWRpby5yZWNvcmRlci5kZXZpY2VfcmVmcmVzaF9hdmFpbGFibGUoKSlcclxuICAgICAgICAgICAgYXVkaW8ucmVjb3JkZXIucmVmcmVzaF9kZXZpY2VzKCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBkZWZhdWx0X3JlY29yZGVyID0gbmV3IFJlY29yZGVyUHJvZmlsZShcImRlZmF1bHRcIik7XHJcbiAgICBkZWZhdWx0X3JlY29yZGVyLmluaXRpYWxpemUoKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LkFVRElPLCB0cihcIkZhaWxlZCB0byBpbml0aWFsaXplIGRlZmF1bHQgcmVjb3JkZXI6ICVvXCIpLCBlcnJvcik7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzb3VuZC5pbml0aWFsaXplKCkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuQVVESU8sIHRyKFwiU291bmRzIGluaXRpYWxpemVkXCIpKTtcclxuICAgIH0pO1xyXG4gICAgc291bmQuc2V0X21hc3Rlcl92b2x1bWUoc2V0dGluZ3MuZ2xvYmFsKFNldHRpbmdzLktFWV9TT1VORF9NQVNURVJfU09VTkRTKSAvIDEwMCk7XHJcblxyXG4gICAgYXdhaXQgcHJvZmlsZXMubG9hZCgpO1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgICAgYXdhaXQgcHB0LmluaXRpYWxpemUoKTtcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBwcHQhXFxuRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgcHB0IVwiKSk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIHNldHVwX2Nsb3NlKCk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBzdHIyYWI4KHN0cikge1xyXG4gICAgY29uc3QgYnVmID0gbmV3IEFycmF5QnVmZmVyKHN0ci5sZW5ndGgpO1xyXG4gICAgY29uc3QgYnVmVmlldyA9IG5ldyBVaW50OEFycmF5KGJ1Zik7XHJcbiAgICBmb3IgKGxldCBpID0gMCwgc3RyTGVuID0gc3RyLmxlbmd0aDsgaSA8IHN0ckxlbjsgaSsrKSB7XHJcbiAgICAgICAgYnVmVmlld1tpXSA9IHN0ci5jaGFyQ29kZUF0KGkpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGJ1ZjtcclxufVxyXG5cclxuLyogRklYTUUgRG9udCB1c2UgYXRvYiwgYmVjYXVzZSBpdCBzdWNrcyBmb3Igbm9uIFVURi04IHRpbmdzICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhcnJheUJ1ZmZlckJhc2U2NChiYXNlNjQ6IHN0cmluZykge1xyXG4gICAgYmFzZTY0ID0gYXRvYihiYXNlNjQpO1xyXG4gICAgY29uc3QgYnVmID0gbmV3IEFycmF5QnVmZmVyKGJhc2U2NC5sZW5ndGgpO1xyXG4gICAgY29uc3QgYnVmVmlldyA9IG5ldyBVaW50OEFycmF5KGJ1Zik7XHJcbiAgICBmb3IgKGxldCBpID0gMCwgc3RyTGVuID0gYmFzZTY0Lmxlbmd0aDsgaSA8IHN0ckxlbjsgaSsrKSB7XHJcbiAgICAgICAgYnVmVmlld1tpXSA9IGJhc2U2NC5jaGFyQ29kZUF0KGkpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGJ1ZjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGJhc2U2NF9lbmNvZGVfYWIoc291cmNlOiBBcnJheUJ1ZmZlckxpa2UpIHtcclxuICAgIGNvbnN0IGVuY29kaW5ncyA9IFwiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrL1wiO1xyXG4gICAgbGV0IGJhc2U2NCAgICAgID0gXCJcIjtcclxuXHJcbiAgICBjb25zdCBieXRlcyAgICAgICAgICA9IG5ldyBVaW50OEFycmF5KHNvdXJjZSk7XHJcbiAgICBjb25zdCBieXRlX2xlbmd0aCAgICA9IGJ5dGVzLmJ5dGVMZW5ndGg7XHJcbiAgICBjb25zdCBieXRlX3JlbWluZGVyICA9IGJ5dGVfbGVuZ3RoICUgMztcclxuICAgIGNvbnN0IG1haW5fbGVuZ3RoICAgID0gYnl0ZV9sZW5ndGggLSBieXRlX3JlbWluZGVyO1xyXG5cclxuICAgIGxldCBhLCBiLCBjLCBkO1xyXG4gICAgbGV0IGNodW5rO1xyXG5cclxuICAgIC8vIE1haW4gbG9vcCBkZWFscyB3aXRoIGJ5dGVzIGluIGNodW5rcyBvZiAzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG1haW5fbGVuZ3RoOyBpID0gaSArIDMpIHtcclxuICAgICAgICAvLyBDb21iaW5lIHRoZSB0aHJlZSBieXRlcyBpbnRvIGEgc2luZ2xlIGludGVnZXJcclxuICAgICAgICBjaHVuayA9IChieXRlc1tpXSA8PCAxNikgfCAoYnl0ZXNbaSArIDFdIDw8IDgpIHwgYnl0ZXNbaSArIDJdO1xyXG5cclxuICAgICAgICAvLyBVc2UgYml0bWFza3MgdG8gZXh0cmFjdCA2LWJpdCBzZWdtZW50cyBmcm9tIHRoZSB0cmlwbGV0XHJcbiAgICAgICAgYSA9IChjaHVuayAmIDE2NTE1MDcyKSA+PiAxODsgLy8gMTY1MTUwNzIgPSAoMl42IC0gMSkgPDwgMThcclxuICAgICAgICBiID0gKGNodW5rICYgMjU4MDQ4KSAgID4+IDEyOyAvLyAyNTgwNDggICA9ICgyXjYgLSAxKSA8PCAxMlxyXG4gICAgICAgIGMgPSAoY2h1bmsgJiA0MDMyKSAgICAgPj4gIDY7IC8vIDQwMzIgICAgID0gKDJeNiAtIDEpIDw8ICA2XHJcbiAgICAgICAgZCA9IChjaHVuayAmIDYzKSAgICAgICA+PiAgMDsgLy8gNjMgICAgICAgPSAoMl42IC0gMSkgPDwgIDBcclxuXHJcbiAgICAgICAgLy8gQ29udmVydCB0aGUgcmF3IGJpbmFyeSBzZWdtZW50cyB0byB0aGUgYXBwcm9wcmlhdGUgQVNDSUkgZW5jb2RpbmdcclxuICAgICAgICBiYXNlNjQgKz0gZW5jb2RpbmdzW2FdICsgZW5jb2RpbmdzW2JdICsgZW5jb2RpbmdzW2NdICsgZW5jb2RpbmdzW2RdO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlYWwgd2l0aCB0aGUgcmVtYWluaW5nIGJ5dGVzIGFuZCBwYWRkaW5nXHJcbiAgICBpZiAoYnl0ZV9yZW1pbmRlciA9PSAxKSB7XHJcbiAgICAgICAgY2h1bmsgPSBieXRlc1ttYWluX2xlbmd0aF07XHJcblxyXG4gICAgICAgIGEgPSAoY2h1bmsgJiAyNTIpID4+IDI7IC8vIDI1MiA9ICgyXjYgLSAxKSA8PCAyXHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgNCBsZWFzdCBzaWduaWZpY2FudCBiaXRzIHRvIHplcm9cclxuICAgICAgICBiID0gKGNodW5rICYgMykgICA8PCA0OyAvLyAzICAgPSAyXjIgLSAxXHJcblxyXG4gICAgICAgIGJhc2U2NCArPSBlbmNvZGluZ3NbYV0gKyBlbmNvZGluZ3NbYl0gKyAnPT0nO1xyXG4gICAgfSBlbHNlIGlmIChieXRlX3JlbWluZGVyID09IDIpIHtcclxuICAgICAgICBjaHVuayA9IChieXRlc1ttYWluX2xlbmd0aF0gPDwgOCkgfCBieXRlc1ttYWluX2xlbmd0aCArIDFdO1xyXG5cclxuICAgICAgICBhID0gKGNodW5rICYgNjQ1MTIpID4+IDEwOyAvLyA2NDUxMiA9ICgyXjYgLSAxKSA8PCAxMFxyXG4gICAgICAgIGIgPSAoY2h1bmsgJiAxMDA4KSAgPj4gIDQ7IC8vIDEwMDggID0gKDJeNiAtIDEpIDw8ICA0XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgMiBsZWFzdCBzaWduaWZpY2FudCBiaXRzIHRvIHplcm9cclxuICAgICAgICBjID0gKGNodW5rICYgMTUpICAgIDw8ICAyOyAvLyAxNSAgICA9IDJeNCAtIDFcclxuXHJcbiAgICAgICAgYmFzZTY0ICs9IGVuY29kaW5nc1thXSArIGVuY29kaW5nc1tiXSArIGVuY29kaW5nc1tjXSArICc9JztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gYmFzZTY0XHJcbn1cclxuXHJcbi8qXHJcbmNsYXNzIFRlc3RQcm94eSBleHRlbmRzIGJpcGMuTWV0aG9kUHJveHkge1xyXG4gICAgY29uc3RydWN0b3IocGFyYW1zOiBiaXBjLk1ldGhvZFByb3h5Q29ubmVjdFBhcmFtZXRlcnMpIHtcclxuICAgICAgICBzdXBlcihiaXBjLmdldF9oYW5kbGVyKCksIHBhcmFtcy5jaGFubmVsX2lkICYmIHBhcmFtcy5jbGllbnRfaWQgPyBwYXJhbXMgOiB1bmRlZmluZWQpO1xyXG5cclxuICAgICAgICBpZighdGhpcy5pc19zbGF2ZSgpKSB7XHJcbiAgICAgICAgICAgIHRoaXMucmVnaXN0ZXJfbWV0aG9kKHRoaXMuYWRkX3NsYXZlKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYoIXRoaXMuaXNfbWFzdGVyKCkpIHtcclxuICAgICAgICAgICAgdGhpcy5yZWdpc3Rlcl9tZXRob2QodGhpcy5zYXlfaGVsbG8pO1xyXG4gICAgICAgICAgICB0aGlzLnJlZ2lzdGVyX21ldGhvZCh0aGlzLmFkZF9tYXN0ZXIpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzZXR1cCgpIHtcclxuICAgICAgICBzdXBlci5zZXR1cCgpO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBvbl9jb25uZWN0ZWQoKSB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSVBDLCBcIlRlc3QgcHJveHkgY29ubmVjdGVkXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBvbl9kaXNjb25uZWN0ZWQoKSB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSVBDLCBcIlRlc3QgcHJveHkgZGlzY29ubmVjdGVkXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgYXN5bmMgc2F5X2hlbGxvKCkgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIFwiSGVsbG8gV29ybGRcIik7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBhZGRfc2xhdmUoYTogbnVtYmVyLCBiOiBudW1iZXIpIDogUHJvbWlzZTxudW1iZXI+IHtcclxuICAgICAgICByZXR1cm4gYSArIGI7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBhZGRfbWFzdGVyKGE6IG51bWJlciwgYjogbnVtYmVyKSA6IFByb21pc2U8bnVtYmVyPiB7XHJcbiAgICAgICAgcmV0dXJuIGEgKiBiO1xyXG4gICAgfVxyXG59XHJcbmludGVyZmFjZSBXaW5kb3cge1xyXG4gICAgcHJveHlfaW5zdGFuY2U6IFRlc3RQcm94eSAmIHt1cmw6ICgpID0+IHN0cmluZ307XHJcbn1cclxuKi9cclxuXHJcbmZ1bmN0aW9uIGhhbmRsZV9jb25uZWN0X3JlcXVlc3QocHJvcGVydGllczogYmlwYy5jb25uZWN0LkNvbm5lY3RSZXF1ZXN0RGF0YSwgY29ubmVjdGlvbjogQ29ubmVjdGlvbkhhbmRsZXIpIHtcclxuICAgIGNvbnN0IHByb2ZpbGVfdXVpZCA9IHByb3BlcnRpZXMucHJvZmlsZSB8fCAocHJvZmlsZXMuZGVmYXVsdF9wcm9maWxlKCkgfHwge2lkOiAnZGVmYXVsdCd9KS5pZDtcclxuICAgIGNvbnN0IHByb2ZpbGUgPSBwcm9maWxlcy5maW5kX3Byb2ZpbGUocHJvZmlsZV91dWlkKSB8fCBwcm9maWxlcy5kZWZhdWx0X3Byb2ZpbGUoKTtcclxuICAgIGNvbnN0IHVzZXJuYW1lID0gcHJvcGVydGllcy51c2VybmFtZSB8fCBwcm9maWxlLmNvbm5lY3RfdXNlcm5hbWUoKTtcclxuXHJcbiAgICBjb25zdCBwYXNzd29yZCA9IHByb3BlcnRpZXMucGFzc3dvcmQgPyBwcm9wZXJ0aWVzLnBhc3N3b3JkLnZhbHVlIDogXCJcIjtcclxuICAgIGNvbnN0IHBhc3N3b3JkX2hhc2hlZCA9IHByb3BlcnRpZXMucGFzc3dvcmQgPyBwcm9wZXJ0aWVzLnBhc3N3b3JkLmhhc2hlZCA6IGZhbHNlO1xyXG5cclxuICAgIGlmKHByb2ZpbGUgJiYgcHJvZmlsZS52YWxpZCgpKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbi5zdGFydENvbm5lY3Rpb24ocHJvcGVydGllcy5hZGRyZXNzLCBwcm9maWxlLCB0cnVlLCB7XHJcbiAgICAgICAgICAgIG5pY2tuYW1lOiB1c2VybmFtZSxcclxuICAgICAgICAgICAgcGFzc3dvcmQ6IHBhc3N3b3JkLmxlbmd0aCA+IDAgPyB7XHJcbiAgICAgICAgICAgICAgICBwYXNzd29yZDogcGFzc3dvcmQsXHJcbiAgICAgICAgICAgICAgICBoYXNoZWQ6IHBhc3N3b3JkX2hhc2hlZFxyXG4gICAgICAgICAgICB9IDogdW5kZWZpbmVkXHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgc2VydmVyX2Nvbm5lY3Rpb25zLnNldF9hY3RpdmVfY29ubmVjdGlvbl9oYW5kbGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICBNb2RhbHMuc3Bhd25Db25uZWN0TW9kYWwoe30se1xyXG4gICAgICAgICAgICB1cmw6IHByb3BlcnRpZXMuYWRkcmVzcyxcclxuICAgICAgICAgICAgZW5mb3JjZTogdHJ1ZVxyXG4gICAgICAgIH0sIHtcclxuICAgICAgICAgICAgcHJvZmlsZTogcHJvZmlsZSxcclxuICAgICAgICAgICAgZW5mb3JjZTogdHJ1ZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBtYWluKCkge1xyXG4gICAgLypcclxuICAgIHdpbmRvdy5wcm94eV9pbnN0YW5jZSA9IG5ldyBUZXN0UHJveHkoe1xyXG4gICAgICAgIGNsaWVudF9pZDogc2V0dGluZ3Muc3RhdGljX2dsb2JhbDxzdHJpbmc+KFwicHJveHlfY2xpZW50X2lkXCIsIHVuZGVmaW5lZCksXHJcbiAgICAgICAgY2hhbm5lbF9pZDogc2V0dGluZ3Muc3RhdGljX2dsb2JhbDxzdHJpbmc+KFwicHJveHlfY2hhbm5lbF9pZFwiLCB1bmRlZmluZWQpXHJcbiAgICB9KSBhcyBhbnk7XHJcbiAgICBpZih3aW5kb3cucHJveHlfaW5zdGFuY2UuaXNfbWFzdGVyKCkpIHtcclxuICAgICAgICB3aW5kb3cucHJveHlfaW5zdGFuY2Uuc2V0dXAoKTtcclxuICAgICAgICB3aW5kb3cucHJveHlfaW5zdGFuY2UudXJsID0gKCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBkYXRhID0gd2luZG93LnByb3h5X2luc3RhbmNlLmdlbmVyYXRlX2Nvbm5lY3RfcGFyYW1ldGVycygpO1xyXG4gICAgICAgICAgICByZXR1cm4gXCJwcm94eV9jaGFubmVsX2lkPVwiICsgZGF0YS5jaGFubmVsX2lkICsgXCImcHJveHlfY2xpZW50X2lkPVwiICsgZGF0YS5jbGllbnRfaWQ7XHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuICAgICovXHJcbiAgICAvL2h0dHA6Ly9sb2NhbGhvc3Q6NjMzNDMvV2ViLUNsaWVudC9pbmRleC5waHA/X2lqdD1vbWNwbXQ4YjlobmpsZmd1aDhhamdyZ29sciZkZWZhdWx0X2Nvbm5lY3RfdXJsPXRydWUmZGVmYXVsdF9jb25uZWN0X3R5cGU9dGVhbXNwZWFrJmRlZmF1bHRfY29ubmVjdF91cmw9bG9jYWxob3N0JTNBOTk4NyZkaXNhYmxlVW5sb2FkRGlhbG9nPTEmbG9hZGVyX2lnbm9yZV9hZ2U9MVxyXG5cclxuICAgIC8qIGluaXRpYWxpemUgZm9udCAqL1xyXG4gICAge1xyXG4gICAgICAgIGNvbnN0IGZvbnQgPSBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9GT05UX1NJWkUsIDE0KTsgLy9wYXJzZUludChnZXRDb21wdXRlZFN0eWxlKGRvY3VtZW50LmJvZHkpLmZvbnRTaXplKVxyXG4gICAgICAgICQoZG9jdW1lbnQuYm9keSkuY3NzKFwiZm9udC1zaXplXCIsIGZvbnQgKyBcInB4XCIpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qIGNvbnRleHQgbWVudSBwcmV2ZW50ICovXHJcbiAgICAkKGRvY3VtZW50KS5vbignY29udGV4dG1lbnUnLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQuaXNEZWZhdWx0UHJldmVudGVkKCkpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaWYoIXNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0RJU0FCTEVfR0xPQkFMX0NPTlRFWFRfTUVOVSkpXHJcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0b3BfbWVudS5pbml0aWFsaXplKCk7XHJcblxyXG4gICAgc2VydmVyX2Nvbm5lY3Rpb25zID0gbmV3IFNlcnZlckNvbm5lY3Rpb25NYW5hZ2VyKCQoXCIjY29ubmVjdGlvbi1oYW5kbGVyc1wiKSk7XHJcbiAgICBjb250cm9sX2Jhci5pbml0aWFsaXNlKCk7IC8qIGJlZm9yZSBjb25uZWN0aW9uIGhhbmRsZXIgdG8gYWxsb3cgcHJvcGVydHkgYXBwbHkgKi9cclxuXHJcbiAgICBjb25zdCBpbml0aWFsX2hhbmRsZXIgPSBzZXJ2ZXJfY29ubmVjdGlvbnMuc3Bhd25fc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgaW5pdGlhbF9oYW5kbGVyLmFjcXVpcmVfcmVjb3JkZXIoZGVmYXVsdF9yZWNvcmRlciwgZmFsc2UpO1xyXG4gICAgY29udHJvbF9iYXIuc2V0X2Nvbm5lY3Rpb25faGFuZGxlcihpbml0aWFsX2hhbmRsZXIpO1xyXG4gICAgLyoqIFNldHVwIHRoZSBYRiBmb3J1bSBpZGVudGl0eSAqKi9cclxuICAgIHByb2ZpbGVzLmlkZW50aXRpZXMudXBkYXRlX2ZvcnVtKCk7XHJcblxyXG4gICAgbGV0IF9yZXNpemVfdGltZW91dDogTm9kZUpTLlRpbWVyO1xyXG4gICAgJCh3aW5kb3cpLm9uKCdyZXNpemUnLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQudGFyZ2V0ICE9PSB3aW5kb3cpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaWYoX3Jlc2l6ZV90aW1lb3V0KVxyXG4gICAgICAgICAgICBjbGVhclRpbWVvdXQoX3Jlc2l6ZV90aW1lb3V0KTtcclxuICAgICAgICBfcmVzaXplX3RpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IGNvbm5lY3Rpb24gb2Ygc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKCkpXHJcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmludm9rZV9yZXNpemVkX29uX2FjdGl2YXRlID0gdHJ1ZTtcclxuICAgICAgICAgICAgY29uc3QgYWN0aXZlX2Nvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgICAgICBpZihhY3RpdmVfY29ubmVjdGlvbilcclxuICAgICAgICAgICAgICAgIGFjdGl2ZV9jb25uZWN0aW9uLnJlc2l6ZV9lbGVtZW50cygpO1xyXG4gICAgICAgICAgICAkKFwiLndpbmRvdy1yZXNpemUtbGlzdGVuZXJcIikudHJpZ2dlcigncmVzaXplJyk7XHJcbiAgICAgICAgfSwgMTAwMCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzdGF0cy5pbml0aWFsaXplKHtcclxuICAgICAgICB2ZXJib3NlOiB0cnVlLFxyXG4gICAgICAgIGFub255bWl6ZV9pcF9hZGRyZXNzZXM6IHRydWUsXHJcbiAgICAgICAgdm9sYXRpbGVfY29sbGVjdGlvbl9vbmx5OiBmYWxzZVxyXG4gICAgfSk7XHJcbiAgICBzdGF0cy5yZWdpc3Rlcl91c2VyX2NvdW50X2xpc3RlbmVyKHN0YXR1cyA9PiB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuU1RBVElTVElDUywgdHIoXCJSZWNlaXZlZCB1c2VyIGNvdW50IHVwZGF0ZTogJW9cIiksIHN0YXR1cyk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzZXJ2ZXJfY29ubmVjdGlvbnMuc2V0X2FjdGl2ZV9jb25uZWN0aW9uX2hhbmRsZXIoc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKClbMF0pO1xyXG5cclxuXHJcbiAgICAoPGFueT53aW5kb3cpLnRlc3RfdXBsb2FkID0gKG1lc3NhZ2U/OiBzdHJpbmcpID0+IHtcclxuICAgICAgICBtZXNzYWdlID0gbWVzc2FnZSB8fCBcIkhlbGxvIFdvcmxkXCI7XHJcblxyXG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgIGNvbm5lY3Rpb24uZmlsZU1hbmFnZXIudXBsb2FkX2ZpbGUoe1xyXG4gICAgICAgICAgICBzaXplOiBtZXNzYWdlLmxlbmd0aCxcclxuICAgICAgICAgICAgb3ZlcndyaXRlOiB0cnVlLFxyXG4gICAgICAgICAgICBjaGFubmVsOiBjb25uZWN0aW9uLmdldENsaWVudCgpLmN1cnJlbnRDaGFubmVsKCksXHJcbiAgICAgICAgICAgIG5hbWU6ICcvSGVsbG9Xb3JsZC50eHQnLFxyXG4gICAgICAgICAgICBwYXRoOiAnJ1xyXG4gICAgICAgIH0pLnRoZW4oa2V5ID0+IHtcclxuICAgICAgICAgICAgY29uc3QgdXBsb2FkID0gbmV3IFJlcXVlc3RGaWxlVXBsb2FkKGtleSk7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheShtZXNzYWdlLmxlbmd0aCk7XHJcbiAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgIGZvcihsZXQgaW5kZXggPSAwOyBpbmRleCA8IG1lc3NhZ2UubGVuZ3RoOyBpbmRleCsrKVxyXG4gICAgICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleF0gPSBtZXNzYWdlLmNoYXJDb2RlQXQoaW5kZXgpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB1cGxvYWQucHV0X2RhdGEoYnVmZmVyKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSlcclxuICAgIH07XHJcblxyXG4gICAgLyogc2NoZWR1bGUgaXQgYSBiaXQgbGF0ZXIgdGhlbiB0aGUgbWFpbiBiZWNhdXNlIHRoZSBtYWluIGZ1bmN0aW9uIGlzIHN0aWxsIHdpdGhpbiB0aGUgbG9hZGVyICovXHJcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICBjb25zdCBjb25uZWN0aW9uID0gc2VydmVyX2Nvbm5lY3Rpb25zLmFjdGl2ZV9jb25uZWN0aW9uX2hhbmRsZXIoKTtcclxuICAgICAgICAvKlxyXG4gICAgICAgIE1vZGFscy5jcmVhdGVDaGFubmVsTW9kYWwoY29ubmVjdGlvbiwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIGNvbm5lY3Rpb24ucGVybWlzc2lvbnMsIChjYiwgcGVybXMpID0+IHtcclxuICAgICAgICAgICAgXHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgKi9cclxuICAgICAgIC8vIE1vZGFscy5vcGVuU2VydmVySW5mbyhjb25uZWN0aW9uLmNoYW5uZWxUcmVlLnNlcnZlcik7XHJcbiAgICAgICAgLy9Nb2RhbHMuY3JlYXRlU2VydmVyTW9kYWwoY29ubmVjdGlvbi5jaGFubmVsVHJlZS5zZXJ2ZXIsIHByb3BlcnRpZXMgPT4gUHJvbWlzZS5yZXNvbHZlKCkpO1xyXG5cclxuICAgICAgICAvL01vZGFscy5vcGVuQ2xpZW50SW5mbyhjb25uZWN0aW9uLmdldENsaWVudCgpKTtcclxuICAgICAgICAvL01vZGFscy5vcGVuU2VydmVySW5mb0JhbmR3aWR0aChjb25uZWN0aW9uLmNoYW5uZWxUcmVlLnNlcnZlcik7XHJcblxyXG4gICAgICAgIC8vTW9kYWxzLm9wZW5CYW5MaXN0KGNvbm5lY3Rpb24pO1xyXG4gICAgICAgIC8qXHJcbiAgICAgICAgTW9kYWxzLnNwYXduQmFuQ2xpZW50KGNvbm5lY3Rpb24sW1xyXG4gICAgICAgICAgICB7bmFtZTogXCJXb2x2ZXJpbkRFVlwiLCB1bmlxdWVfaWQ6IFwiWFhYWFwifSxcclxuICAgICAgICAgICAge25hbWU6IFwiV29sdmVyaW5ERVZcIiwgdW5pcXVlX2lkOiBcIlhYWFhcIn0sXHJcbiAgICAgICAgICAgIHtuYW1lOiBcIldvbHZlcmluREVWXCIsIHVuaXF1ZV9pZDogXCJYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFwifSxcclxuICAgICAgICAgICAge25hbWU6IFwiV29sdmVyaW5ERVZcIiwgdW5pcXVlX2lkOiBcIllZWVwifVxyXG4gICAgICAgIF0sICgpID0+IHt9KTtcclxuICAgICAgICAqL1xyXG4gICAgfSwgNDAwMCk7XHJcbiAgICAvL01vZGFscy5zcGF3blNldHRpbmdzTW9kYWwoXCJpZGVudGl0eS1wcm9maWxlc1wiKTtcclxuICAgIC8vTW9kYWxzLnNwYXduS2V5U2VsZWN0KGNvbnNvbGUubG9nKTtcclxuICAgIC8vTW9kYWxzLnNwYXduQm9va21hcmtNb2RhbCgpO1xyXG5cclxuICAgIC8qXHJcbiAgICB7XHJcbiAgICAgICAgY29uc3QgbW9kYWwgPSBjcmVhdGVNb2RhbCh7XHJcbiAgICAgICAgICAgIGhlYWRlcjogdHIoXCJUZXN0IE5ldCBHcmFwaFwiKSxcclxuICAgICAgICAgICAgYm9keTogKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgY2FudmFzID0gJC5zcGF3bihcImNhbnZhc1wiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5jc3MoXCJwb3NpdGlvblwiLCBcImFic29sdXRlXCIpXHJcbiAgICAgICAgICAgICAgICAgICAgLmNzcyh7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvcDogMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tOiAwLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICByaWdodDogMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgbGVmdDogMFxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiAkLnNwYXduKFwiZGl2XCIpXHJcbiAgICAgICAgICAgICAgICAgICAgLmNzcyhcImhlaWdodFwiLCBcIjVlbVwiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5jc3MoXCJ3aWR0aFwiLCBcIjMwZW1cIilcclxuICAgICAgICAgICAgICAgICAgICAuY3NzKFwicG9zaXRpb25cIiwgXCJyZWxhdGl2ZVwiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5hcHBlbmQoY2FudmFzKTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZm9vdGVyOiBudWxsXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGdyYXBoID0gbmV3IG5ldC5ncmFwaC5HcmFwaChtb2RhbC5odG1sVGFnLmZpbmQoXCJjYW52YXNcIilbMF0gYXMgYW55KTtcclxuICAgICAgICBncmFwaC5pbml0aWFsaXplKCk7XHJcblxyXG4gICAgICAgIG1vZGFsLmNsb3NlX2xpc3RlbmVyLnB1c2goKCkgPT4gZ3JhcGgudGVybWluYXRlKCkpO1xyXG4gICAgICAgIG1vZGFsLm9wZW4oKTtcclxuICAgIH1cclxuICAgICAqL1xyXG5cclxuXHJcbiAgICAvKiBmb3IgdGVzdGluZyAqL1xyXG4gICAgaWYoc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfVVNFUl9JU19ORVcpKSB7XHJcbiAgICAgICAgY29uc3QgbW9kYWwgPSBNb2RhbHMub3Blbk1vZGFsTmV3Y29tZXIoKTtcclxuICAgICAgICBtb2RhbC5jbG9zZV9saXN0ZW5lci5wdXNoKCgpID0+IHNldHRpbmdzLmNoYW5nZUdsb2JhbChTZXR0aW5ncy5LRVlfVVNFUl9JU19ORVcsIGZhbHNlKSk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmNvbnN0IHRhc2tfdGVhd2ViX3N0YXJ0ZXI6IGxvYWRlci5UYXNrID0ge1xyXG4gICAgbmFtZTogXCJ2b2ljZSBhcHAgc3RhcnRlclwiLFxyXG4gICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplX2FwcCgpO1xyXG4gICAgICAgICAgICBtYWluKCk7XHJcbiAgICAgICAgICAgIGlmKCFhdWRpby5wbGF5ZXIuaW5pdGlhbGl6ZWQoKSkge1xyXG4gICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuVk9JQ0UsIHRyKFwiSW5pdGlhbGl6ZSBhdWRpbyBjb250cm9sbGVyIGxhdGVyIVwiKSk7XHJcbiAgICAgICAgICAgICAgICBpZighYXVkaW8ucGxheWVyLmluaXRpYWxpemVGcm9tR2VzdHVyZSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJNaXNzaW5nIGF1ZGlvLnBsYXllci5pbml0aWFsaXplRnJvbUdlc3R1cmVcIikpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkub25lKCdjbGljaycsIGV2ZW50ID0+IGF1ZGlvLnBsYXllci5pbml0aWFsaXplRnJvbUdlc3R1cmUoKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChleCkge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGV4LnN0YWNrKTtcclxuICAgICAgICAgICAgaWYoZXggaW5zdGFuY2VvZiBSZWZlcmVuY2VFcnJvciB8fCBleCBpbnN0YW5jZW9mIFR5cGVFcnJvcilcclxuICAgICAgICAgICAgICAgIGV4ID0gZXgubmFtZSArIFwiOiBcIiArIGV4Lm1lc3NhZ2U7XHJcbiAgICAgICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcihcIkZhaWxlZCB0byBpbnZva2UgbWFpbiBmdW5jdGlvbjo8YnI+XCIgKyBleCk7XHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByaW9yaXR5OiAxMFxyXG59O1xyXG5cclxuY29uc3QgdGFza19jb25uZWN0X2hhbmRsZXI6IGxvYWRlci5UYXNrID0ge1xyXG4gICAgbmFtZTogXCJDb25uZWN0IGhhbmRsZXJcIixcclxuICAgIGZ1bmN0aW9uOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgICAgY29uc3QgYWRkcmVzcyA9IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfQ09OTkVDVF9BRERSRVNTLCBcIlwiKTtcclxuICAgICAgICBjb25zdCBjaGFuZGxlciA9IGJpcGMuZ2V0X2Nvbm5lY3RfaGFuZGxlcigpO1xyXG4gICAgICAgIGlmKHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRkxBR19DT05ORUNUX0RFRkFVTFQsIGZhbHNlKSAmJiBhZGRyZXNzKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGNvbm5lY3RfZGF0YSA9IHtcclxuICAgICAgICAgICAgICAgIGFkZHJlc3M6IGFkZHJlc3MsXHJcblxyXG4gICAgICAgICAgICAgICAgcHJvZmlsZTogc2V0dGluZ3Muc3RhdGljKFNldHRpbmdzLktFWV9DT05ORUNUX1BST0ZJTEUsIFwiXCIpLFxyXG4gICAgICAgICAgICAgICAgdXNlcm5hbWU6IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfQ09OTkVDVF9VU0VSTkFNRSwgXCJcIiksXHJcblxyXG4gICAgICAgICAgICAgICAgcGFzc3dvcmQ6IHtcclxuICAgICAgICAgICAgICAgICAgICB2YWx1ZTogc2V0dGluZ3Muc3RhdGljKFNldHRpbmdzLktFWV9DT05ORUNUX1BBU1NXT1JELCBcIlwiKSxcclxuICAgICAgICAgICAgICAgICAgICBoYXNoZWQ6IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRkxBR19DT05ORUNUX1BBU1NXT1JELCBmYWxzZSlcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIGlmKGNoYW5kbGVyKSB7XHJcbiAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGNoYW5kbGVyLnBvc3RfY29ubmVjdF9yZXF1ZXN0KGNvbm5lY3RfZGF0YSwgKCkgPT4gbmV3IFByb21pc2U8Ym9vbGVhbj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBzcGF3blllc05vKHRyKFwiQW5vdGhlciBUZWFXZWIgaW5zdGFuY2UgaXMgYWxyZWFkeSBydW5uaW5nXCIpLCB0cmEoXCJBbm90aGVyIFRlYVdlYiBpbnN0YW5jZSBpcyBhbHJlYWR5IHJ1bm5pbmcuezpicjp9V291bGQgeW91IGxpa2UgdG8gY29ubmVjdCB0aGVyZT9cIiksIHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9LCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLm9wZW4oKTtcclxuICAgICAgICAgICAgICAgICAgICB9KSk7XHJcbiAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuQ0xJRU5ULCB0cihcIkV4ZWN1dGVkIGNvbm5lY3Qgc3VjY2Vzc2Z1bGx5IGluIGFub3RoZXIgYnJvd3NlciB3aW5kb3cuIENsb3NpbmcgdGhpcyB3aW5kb3dcIikpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJZb3UncmUgY29ubmVjdGluZyB0byB7MH0gd2l0aGluIHRoZSBvdGhlciBUZWFXZWIgaW5zdGFuY2Uuezpicjp9XCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIllvdSBjb3VsZCBub3cgY2xvc2UgdGhpcyBwYWdlLlwiO1xyXG4gICAgICAgICAgICAgICAgICAgIGNyZWF0ZUluZm9Nb2RhbChcclxuICAgICAgICAgICAgICAgICAgICAgICAgdHIoXCJDb25uZWN0aW5nIHN1Y2Nlc3NmdWxseSB3aXRoaW4gb3RoZXIgaW5zdGFuY2VcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIE1lc3NhZ2VIZWxwZXIuZm9ybWF0TWVzc2FnZSh0cihtZXNzYWdlKSwgY29ubmVjdF9kYXRhLmFkZHJlc3MpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9vdGVyOiB1bmRlZmluZWRcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICkub3BlbigpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5DTElFTlQsIHRyKFwiRmFpbGVkIHRvIGV4ZWN1dGUgY29ubmVjdCB3aXRoaW4gb3RoZXIgVGVhV2ViIGluc3RhbmNlLiBVc2luZyB0aGlzIG9uZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHtcclxuICAgICAgICAgICAgICAgIHByaW9yaXR5OiAwLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IGhhbmRsZV9jb25uZWN0X3JlcXVlc3QoY29ubmVjdF9kYXRhLCBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpIHx8IHNlcnZlcl9jb25uZWN0aW9ucy5zcGF3bl9zZXJ2ZXJfY29ubmVjdGlvbl9oYW5kbGVyKCkpLFxyXG4gICAgICAgICAgICAgICAgbmFtZTogdHIoXCJkZWZhdWx0IHVybCBjb25uZWN0XCIpXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZihjaGFuZGxlcikge1xyXG4gICAgICAgICAgICAvKiBubyBpbnN0YW5jZSBhdmFpbCwgc28gbGV0cyBtYWtlIHVzIGF2YWlsICovXHJcbiAgICAgICAgICAgIGNoYW5kbGVyLmNhbGxiYWNrX2F2YWlsYWJsZSA9IGRhdGEgPT4ge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuICFzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9ESVNBQkxFX01VTFRJX1NFU1NJT04pO1xyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY2hhbmRsZXIuY2FsbGJhY2tfZXhlY3V0ZSA9IGRhdGEgPT4ge1xyXG4gICAgICAgICAgICAgICAgaGFuZGxlX2Nvbm5lY3RfcmVxdWVzdChkYXRhLCBzZXJ2ZXJfY29ubmVjdGlvbnMuc3Bhd25fc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcigpKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfdGVhd2ViX3N0YXJ0ZXIpO1xyXG4gICAgfSxcclxuICAgIHByaW9yaXR5OiAxMFxyXG59O1xyXG5cclxuY29uc3QgdGFza19jZXJ0aWZpY2F0ZV9jYWxsYmFjazogbG9hZGVyLlRhc2sgPSB7XHJcbiAgICBuYW1lOiBcImNlcnRpZmljYXRlIGFjY2VwdCB0ZXN0ZXJcIixcclxuICAgIGZ1bmN0aW9uOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgICAgY29uc3QgY2VydGlmaWNhdGVfYWNjZXB0ID0gc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfQ0VSVElGSUNBVEVfQ0FMTEJBQ0ssIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgaWYoY2VydGlmaWNhdGVfYWNjZXB0KSB7XHJcbiAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJVc2luZyB0aGlzIGluc3RhbmNlIGFzIGNlcnRpZmljYXRlIGNhbGxiYWNrLiBJRDogJXNcIiksIGNlcnRpZmljYXRlX2FjY2VwdCk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGJpcGMuZ2V0X2hhbmRsZXIoKS5wb3N0X2NlcnRpZmljYXRlX2FjY3BlY3RlZChjZXJ0aWZpY2F0ZV9hY2NlcHQpO1xyXG4gICAgICAgICAgICAgICAgfSBjYXRjaChlKSB7fSAvL0ZJWE1FIHJlbW92ZSFcclxuICAgICAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJPdGhlciBpbnN0YW5jZSBoYXMgYWNrbm93bGVkZ2VkIG91dCB3b3JrLiBDbG9zaW5nIHRoaXMgd2luZG93LlwiKSk7XHJcblxyXG4gICAgICAgICAgICAgICAgY29uc3Qgc2Vjb25kc190YWcgPSAkLnNwYXduKFwiYVwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICBsZXQgc2Vjb25kcyA9IDU7XHJcbiAgICAgICAgICAgICAgICBsZXQgaW50ZXJ2YWxfaWQ7XHJcbiAgICAgICAgICAgICAgICBpbnRlcnZhbF9pZCA9IHNldEludGVydmFsKCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBzZWNvbmRzLS07XHJcbiAgICAgICAgICAgICAgICAgICAgc2Vjb25kc190YWcudGV4dChzZWNvbmRzLnRvU3RyaW5nKCkpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZihzZWNvbmRzIDw9IDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KGludGVydmFsX2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJDbG9zaW5nIHdpbmRvd1wiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5jbG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSwgMTAwMCk7XHJcblxyXG4gICAgICAgICAgICAgICAgY29uc3QgbWVzc2FnZSA9XHJcbiAgICAgICAgICAgICAgICAgICAgXCJZb3UndmUgc3VjY2Vzc2Z1bGx5IGFjY2VwdGVkIHRoZSBjZXJ0aWZpY2F0ZS57OmJyOn1cIiArXHJcbiAgICAgICAgICAgICAgICAgICAgXCJUaGlzIHBhZ2Ugd2lsbCBjbG9zZSBpbiB7MH0gc2Vjb25kcy5cIjtcclxuICAgICAgICAgICAgICAgIGNyZWF0ZUluZm9Nb2RhbChcclxuICAgICAgICAgICAgICAgICAgICB0cihcIkNlcnRpZmljYXRlIGFjY2NlcHRlZCBzdWNjZXNzZnVsbHlcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgTWVzc2FnZUhlbHBlci5mb3JtYXRNZXNzYWdlKHRyKG1lc3NhZ2UpLCBzZWNvbmRzX3RhZyksXHJcbiAgICAgICAgICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBmb290ZXI6IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICkub3BlbigpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiRmFpbGVkIHRvIHN1Y2Nlc3NmdWxseSBwb3N0IGNlcnRpZmljYXRlIGFjY2VwdCBzdGF0dXM6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiV2UncmUgbm90IHVzZWQgdG8gYWNjZXB0IGNlcnRpZmljYXRlZC4gQm9vdGluZyBhcHAuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfY29ubmVjdF9oYW5kbGVyKTtcclxuICAgIH0sXHJcbiAgICBwcmlvcml0eTogMTBcclxufTtcclxuXHJcbmxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgbmFtZTogXCJqcmVuZGVyZSBpbml0aWFsaXplXCIsXHJcbiAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGlmKCFzZXR1cF9qc3JlbmRlcigpKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIGxvYWRcIjtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2FkZXIuY3JpdGljYWxfZXJyb3IodHIoXCJGYWlsZWQgdG8gc2V0dXAganNyZW5kZXJcIikpO1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIGxvYWQganNyZW5kZXIhICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG4gICAgcHJpb3JpdHk6IDEwMFxyXG59KTtcclxuXHJcbmxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgbmFtZTogXCJhcHAgc3RhcnRlclwiLFxyXG4gICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplKCk7XHJcblxyXG4gICAgICAgICAgICBpZihhcHAuaXNfd2ViKCkpIHtcclxuICAgICAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfY2VydGlmaWNhdGVfY2FsbGJhY2spO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgbG9hZGVyLnJlZ2lzdGVyX3Rhc2sobG9hZGVyLlN0YWdlLkxPQURFRCwgdGFza190ZWF3ZWJfc3RhcnRlcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChleCkge1xyXG4gICAgICAgICAgICBpZihleCBpbnN0YW5jZW9mIEVycm9yIHx8IHR5cGVvZihleC5zdGFjaykgIT09IFwidW5kZWZpbmVkXCIpXHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCh0ciB8fCAobXNnID0+IG1zZykpKFwiQ3JpdGljYWwgZXJyb3Igc3RhY2sgdHJhY2U6ICVvXCIpLCBleC5zdGFjayk7XHJcblxyXG4gICAgICAgICAgICBpZihleCBpbnN0YW5jZW9mIFJlZmVyZW5jZUVycm9yIHx8IGV4IGluc3RhbmNlb2YgVHlwZUVycm9yKVxyXG4gICAgICAgICAgICAgICAgZXggPSBleC5uYW1lICsgXCI6IFwiICsgZXgubWVzc2FnZTtcclxuICAgICAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKFwiRmFpbGVkIHRvIGJvb3QgYXBwIGZ1bmN0aW9uOjxicj5cIiArIGV4KTtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG4gICAgcHJpb3JpdHk6IDEwMDBcclxufSk7XHJcblxyXG4iLCJpbXBvcnQge01lc3NhZ2VIZWxwZXJ9IGZyb20gXCIuLi91aS9mcmFtZXMvY2hhdFwiO1xyXG5pbXBvcnQge2NyZWF0ZUVycm9yTW9kYWx9IGZyb20gXCIuLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5pbXBvcnQge2d1aWR9IGZyb20gXCIuLi9jcnlwdG8vdWlkXCI7XHJcbmltcG9ydCB7ZGVjb2RlX2lkZW50aXR5LCBJZGVudGl0aWZ5VHlwZSwgSWRlbnRpdHl9IGZyb20gXCIuL0lkZW50aXR5XCI7XHJcbmltcG9ydCB7c3RhdGljX2ZvcnVtX2lkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL1RlYUZvcnVtSWRlbnRpdHlcIjtcclxuaW1wb3J0IHtUZWFTcGVha0lkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL1RlYW1TcGVha0lkZW50aXR5XCI7XHJcbmltcG9ydCB7QWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9ufSBmcm9tIFwiLi4vY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZVwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuXHJcbmltcG9ydCBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcjtcclxuXHJcbmV4cG9ydCBjbGFzcyBDb25uZWN0aW9uUHJvZmlsZSB7XHJcbiAgICBpZDogc3RyaW5nO1xyXG5cclxuICAgIHByb2ZpbGVfbmFtZTogc3RyaW5nO1xyXG4gICAgZGVmYXVsdF91c2VybmFtZTogc3RyaW5nO1xyXG4gICAgZGVmYXVsdF9wYXNzd29yZDogc3RyaW5nO1xyXG5cclxuICAgIHNlbGVjdGVkX2lkZW50aXR5X3R5cGU6IHN0cmluZyA9IFwidW5zZXRcIjtcclxuICAgIGlkZW50aXRpZXM6IHsgW2tleTogc3RyaW5nXTogSWRlbnRpdHkgfSA9IHt9O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGlkOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmlkID0gaWQ7XHJcbiAgICB9XHJcblxyXG4gICAgY29ubmVjdF91c2VybmFtZSgpOiBzdHJpbmcge1xyXG4gICAgICAgIGlmICh0aGlzLmRlZmF1bHRfdXNlcm5hbWUgJiYgdGhpcy5kZWZhdWx0X3VzZXJuYW1lICE9PSBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiKVxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5kZWZhdWx0X3VzZXJuYW1lO1xyXG5cclxuICAgICAgICBsZXQgc2VsZWN0ZWQgPSB0aGlzLnNlbGVjdGVkX2lkZW50aXR5KCk7XHJcbiAgICAgICAgbGV0IG5hbWUgPSBzZWxlY3RlZCA/IHNlbGVjdGVkLmZhbGxiYWNrX25hbWUoKSA6IHVuZGVmaW5lZDtcclxuICAgICAgICByZXR1cm4gbmFtZSB8fCBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiO1xyXG4gICAgfVxyXG5cclxuICAgIHNlbGVjdGVkX2lkZW50aXR5KGN1cnJlbnRfdHlwZT86IElkZW50aXRpZnlUeXBlKTogSWRlbnRpdHkge1xyXG4gICAgICAgIGlmICghY3VycmVudF90eXBlKVxyXG4gICAgICAgICAgICBjdXJyZW50X3R5cGUgPSB0aGlzLnNlbGVjdGVkX3R5cGUoKTtcclxuXHJcbiAgICAgICAgaWYgKGN1cnJlbnRfdHlwZSA9PT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG5cclxuICAgICAgICBpZiAoY3VycmVudF90eXBlID09IElkZW50aXRpZnlUeXBlLlRFQUZPUk8pIHtcclxuICAgICAgICAgICAgcmV0dXJuIHN0YXRpY19mb3J1bV9pZGVudGl0eSgpO1xyXG4gICAgICAgIH0gZWxzZSBpZiAoY3VycmVudF90eXBlID09IElkZW50aXRpZnlUeXBlLlRFQU1TUEVBSyB8fCBjdXJyZW50X3R5cGUgPT0gSWRlbnRpdGlmeVR5cGUuTklDS05BTUUpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaWRlbnRpdGllc1tJZGVudGl0aWZ5VHlwZVtjdXJyZW50X3R5cGVdLnRvTG93ZXJDYXNlKCldO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZWxlY3RlZF90eXBlPygpOiBJZGVudGl0aWZ5VHlwZSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRfaWRlbnRpdHlfdHlwZSA/IElkZW50aXRpZnlUeXBlW3RoaXMuc2VsZWN0ZWRfaWRlbnRpdHlfdHlwZS50b1VwcGVyQ2FzZSgpXSA6IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZXRfaWRlbnRpdHkodHlwZTogSWRlbnRpdGlmeVR5cGUsIGlkZW50aXR5OiBJZGVudGl0eSkge1xyXG4gICAgICAgIHRoaXMuaWRlbnRpdGllc1tJZGVudGl0aWZ5VHlwZVt0eXBlXS50b0xvd2VyQ2FzZSgpXSA9IGlkZW50aXR5O1xyXG4gICAgfVxyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyPyhjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5zZWxlY3RlZF9pZGVudGl0eSgpO1xyXG4gICAgICAgIGlmICghaWRlbnRpdHkpXHJcbiAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICAgICAgcmV0dXJuIGlkZW50aXR5LnNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZT8oKTogc3RyaW5nIHtcclxuICAgICAgICBjb25zdCBpZGVudGl0eV9kYXRhID0ge307XHJcbiAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gdGhpcy5pZGVudGl0aWVzKVxyXG4gICAgICAgICAgICBpZiAodGhpcy5pZGVudGl0aWVzW2tleV0pXHJcbiAgICAgICAgICAgICAgICBpZGVudGl0eV9kYXRhW2tleV0gPSB0aGlzLmlkZW50aXRpZXNba2V5XS5lbmNvZGUoKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgICAgdmVyc2lvbjogMSxcclxuICAgICAgICAgICAgdXNlcm5hbWU6IHRoaXMuZGVmYXVsdF91c2VybmFtZSxcclxuICAgICAgICAgICAgcGFzc3dvcmQ6IHRoaXMuZGVmYXVsdF9wYXNzd29yZCxcclxuICAgICAgICAgICAgcHJvZmlsZV9uYW1lOiB0aGlzLnByb2ZpbGVfbmFtZSxcclxuICAgICAgICAgICAgaWRlbnRpdHlfdHlwZTogdGhpcy5zZWxlY3RlZF9pZGVudGl0eV90eXBlLFxyXG4gICAgICAgICAgICBpZGVudGl0eV9kYXRhOiBpZGVudGl0eV9kYXRhLFxyXG4gICAgICAgICAgICBpZDogdGhpcy5pZFxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHZhbGlkKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5zZWxlY3RlZF9pZGVudGl0eSgpO1xyXG4gICAgICAgIGlmICghaWRlbnRpdHkgfHwgIWlkZW50aXR5LnZhbGlkKCkpIHJldHVybiBmYWxzZTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbn1cclxuXHJcbmFzeW5jIGZ1bmN0aW9uIGRlY29kZV9wcm9maWxlKGRhdGEpOiBQcm9taXNlPENvbm5lY3Rpb25Qcm9maWxlIHwgc3RyaW5nPiB7XHJcbiAgICBkYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgIGlmIChkYXRhLnZlcnNpb24gIT09IDEpXHJcbiAgICAgICAgcmV0dXJuIFwiaW52YWxpZCB2ZXJzaW9uXCI7XHJcblxyXG4gICAgY29uc3QgcmVzdWx0OiBDb25uZWN0aW9uUHJvZmlsZSA9IG5ldyBDb25uZWN0aW9uUHJvZmlsZShkYXRhLmlkKTtcclxuICAgIHJlc3VsdC5kZWZhdWx0X3VzZXJuYW1lID0gZGF0YS51c2VybmFtZTtcclxuICAgIHJlc3VsdC5kZWZhdWx0X3Bhc3N3b3JkID0gZGF0YS5wYXNzd29yZDtcclxuICAgIHJlc3VsdC5wcm9maWxlX25hbWUgPSBkYXRhLnByb2ZpbGVfbmFtZTtcclxuICAgIHJlc3VsdC5zZWxlY3RlZF9pZGVudGl0eV90eXBlID0gKGRhdGEuaWRlbnRpdHlfdHlwZSB8fCBcIlwiKS50b0xvd2VyQ2FzZSgpO1xyXG5cclxuICAgIGlmIChkYXRhLmlkZW50aXR5X2RhdGEpIHtcclxuICAgICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhLmlkZW50aXR5X2RhdGEpIHtcclxuICAgICAgICAgICAgY29uc3QgdHlwZSA9IElkZW50aXRpZnlUeXBlW2tleS50b1VwcGVyQ2FzZSgpIGFzIHN0cmluZ107XHJcbiAgICAgICAgICAgIGNvbnN0IF9kYXRhID0gZGF0YS5pZGVudGl0eV9kYXRhW2tleV07XHJcbiAgICAgICAgICAgIGlmICh0eXBlID09IHVuZGVmaW5lZCkgY29udGludWU7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBpZGVudGl0eSA9IGF3YWl0IGRlY29kZV9pZGVudGl0eSh0eXBlLCBfZGF0YSk7XHJcbiAgICAgICAgICAgIGlmIChpZGVudGl0eSA9PSB1bmRlZmluZWQpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgcmVzdWx0LmlkZW50aXRpZXNba2V5LnRvTG93ZXJDYXNlKCldID0gaWRlbnRpdHk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmludGVyZmFjZSBQcm9maWxlc0RhdGEge1xyXG4gICAgdmVyc2lvbjogbnVtYmVyO1xyXG4gICAgcHJvZmlsZXM6IHN0cmluZ1tdO1xyXG59XHJcblxyXG5sZXQgYXZhaWxhYmxlX3Byb2ZpbGVzOiBDb25uZWN0aW9uUHJvZmlsZVtdID0gW107XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZCgpIHtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcyA9IFtdO1xyXG5cclxuICAgIGNvbnN0IHByb2ZpbGVzX2pzb24gPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInByb2ZpbGVzXCIpO1xyXG4gICAgbGV0IHByb2ZpbGVzX2RhdGE6IFByb2ZpbGVzRGF0YSA9ICgoKSA9PiB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmV0dXJuIHByb2ZpbGVzX2pzb24gPyBKU09OLnBhcnNlKHByb2ZpbGVzX2pzb24pIDoge3ZlcnNpb246IDB9IGFzIGFueTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBkZWJ1Z2dlcjtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkludmFsaWQgcHJvZmlsZSBqc29uISBSZXNldHRpbmcgcHJvZmlsZXMgOiggKCVvKVwiKSwgcHJvZmlsZXNfanNvbik7XHJcbiAgICAgICAgICAgIGNyZWF0ZUVycm9yTW9kYWwodHIoXCJQcm9maWxlIGRhdGEgaW52YWxpZFwiKSwgTWVzc2FnZUhlbHBlci5mb3JtYXRNZXNzYWdlKHRyKFwiVGhlIHByb2ZpbGUgZGF0YSBpcyBpbnZhbGlkLns6YnI6fVRoaXMgbWlnaHQgY2F1c2UgZGF0YSBsb3NzLlwiKSkpLm9wZW4oKTtcclxuICAgICAgICAgICAgcmV0dXJuIHt2ZXJzaW9uOiAwfTtcclxuICAgICAgICB9XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIGlmIChwcm9maWxlc19kYXRhLnZlcnNpb24gPT09IDApIHtcclxuICAgICAgICBwcm9maWxlc19kYXRhID0ge1xyXG4gICAgICAgICAgICB2ZXJzaW9uOiAxLFxyXG4gICAgICAgICAgICBwcm9maWxlczogW11cclxuICAgICAgICB9O1xyXG4gICAgfVxyXG4gICAgaWYgKHByb2ZpbGVzX2RhdGEudmVyc2lvbiA9PSAxKSB7XHJcbiAgICAgICAgZm9yIChjb25zdCBwcm9maWxlX2RhdGEgb2YgcHJvZmlsZXNfZGF0YS5wcm9maWxlcykge1xyXG4gICAgICAgICAgICBjb25zdCBwcm9maWxlID0gYXdhaXQgZGVjb2RlX3Byb2ZpbGUocHJvZmlsZV9kYXRhKTtcclxuICAgICAgICAgICAgaWYgKHR5cGVvZiAocHJvZmlsZSkgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIGxvYWQgcHJvZmlsZS4gUmVhc29uOiAlcywgUHJvZmlsZSBkYXRhOiAlc1wiKSwgcHJvZmlsZSwgcHJvZmlsZXNfZGF0YSk7XHJcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBhdmFpbGFibGVfcHJvZmlsZXMucHVzaChwcm9maWxlKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFmaW5kX3Byb2ZpbGUoXCJkZWZhdWx0XCIpKSB7IC8vQ3JlYXRlIGEgZGVmYXVsdCBwcm9maWxlIGFuZCB0ZWFmb3JvIHByb2ZpbGVcclxuICAgICAgICB7XHJcbiAgICAgICAgICAgIGNvbnN0IHByb2ZpbGUgPSBjcmVhdGVfbmV3X3Byb2ZpbGUoXCJkZWZhdWx0XCIsIFwiZGVmYXVsdFwiKTtcclxuICAgICAgICAgICAgcHJvZmlsZS5kZWZhdWx0X3Bhc3N3b3JkID0gXCJcIjtcclxuICAgICAgICAgICAgcHJvZmlsZS5kZWZhdWx0X3VzZXJuYW1lID0gXCJcIjtcclxuICAgICAgICAgICAgcHJvZmlsZS5wcm9maWxlX25hbWUgPSBcIkRlZmF1bHQgUHJvZmlsZVwiO1xyXG5cclxuICAgICAgICAgICAgLyogZ2VuZXJhdGUgZGVmYXVsdCBpZGVudGl0eSAqL1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgaWRlbnRpdHkgPSBhd2FpdCBUZWFTcGVha0lkZW50aXR5LmdlbmVyYXRlX25ldygpO1xyXG4gICAgICAgICAgICAgICAgbGV0IGFjdGl2ZSA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBhY3RpdmUgPSBmYWxzZTtcclxuICAgICAgICAgICAgICAgIH0sIDEwMDApO1xyXG4gICAgICAgICAgICAgICAgYXdhaXQgaWRlbnRpdHkuaW1wcm92ZV9sZXZlbCg4LCAxLCAoKSA9PiBhY3RpdmUpO1xyXG4gICAgICAgICAgICAgICAgcHJvZmlsZS5zZXRfaWRlbnRpdHkoSWRlbnRpdGlmeVR5cGUuVEVBTVNQRUFLLCBpZGVudGl0eSk7XHJcbiAgICAgICAgICAgICAgICBwcm9maWxlLnNlbGVjdGVkX2lkZW50aXR5X3R5cGUgPSBJZGVudGl0aWZ5VHlwZVtJZGVudGl0aWZ5VHlwZS5URUFNU1BFQUtdO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgY3JlYXRlRXJyb3JNb2RhbCh0cihcIkZhaWxlZCB0byBnZW5lcmF0ZSBkZWZhdWx0IGlkZW50aXR5XCIpLCB0cihcIkZhaWxlZCB0byBnZW5lcmF0ZSBkZWZhdWx0IGlkZW50aXR5ITxicj5QbGVhc2UgbWFudWFsbHkgZ2VuZXJhdGUgdGhlIGlkZW50aXR5IHdpdGhpbiB5b3VyIHNldHRpbmdzID0+IHByb2ZpbGVzXCIpKS5vcGVuKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHsgLyogZm9ydW0gaWRlbnRpdHkgKHdvcmtzIG9ubHkgd2hlbiBjb25uZWN0ZWQgdG8gdGhlIGZvcnVtKSAqL1xyXG4gICAgICAgICAgICBjb25zdCBwcm9maWxlID0gY3JlYXRlX25ld19wcm9maWxlKFwiVGVhU3BlYWsgRm9ydW1cIiwgXCJ0ZWFmb3JvXCIpO1xyXG4gICAgICAgICAgICBwcm9maWxlLmRlZmF1bHRfcGFzc3dvcmQgPSBcIlwiO1xyXG4gICAgICAgICAgICBwcm9maWxlLmRlZmF1bHRfdXNlcm5hbWUgPSBcIlwiO1xyXG4gICAgICAgICAgICBwcm9maWxlLnByb2ZpbGVfbmFtZSA9IFwiVGVhU3BlYWsgRm9ydW0gcHJvZmlsZVwiO1xyXG5cclxuICAgICAgICAgICAgcHJvZmlsZS5zZXRfaWRlbnRpdHkoSWRlbnRpdGlmeVR5cGUuVEVBRk9STywgc3RhdGljX2ZvcnVtX2lkZW50aXR5KCkpO1xyXG4gICAgICAgICAgICBwcm9maWxlLnNlbGVjdGVkX2lkZW50aXR5X3R5cGUgPSBJZGVudGl0aWZ5VHlwZVtJZGVudGl0aWZ5VHlwZS5URUFGT1JPXTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHNhdmUoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZV9uZXdfcHJvZmlsZShuYW1lOiBzdHJpbmcsIGlkPzogc3RyaW5nKTogQ29ubmVjdGlvblByb2ZpbGUge1xyXG4gICAgY29uc3QgcHJvZmlsZSA9IG5ldyBDb25uZWN0aW9uUHJvZmlsZShpZCB8fCBndWlkKCkpO1xyXG4gICAgcHJvZmlsZS5wcm9maWxlX25hbWUgPSBuYW1lO1xyXG4gICAgcHJvZmlsZS5kZWZhdWx0X3VzZXJuYW1lID0gXCJcIjtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcy5wdXNoKHByb2ZpbGUpO1xyXG4gICAgcmV0dXJuIHByb2ZpbGU7XHJcbn1cclxuXHJcbmxldCBfcmVxdWlyZXNfc2F2ZSA9IGZhbHNlO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHNhdmUoKSB7XHJcbiAgICBjb25zdCBwcm9maWxlczogc3RyaW5nW10gPSBbXTtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBhdmFpbGFibGVfcHJvZmlsZXMpXHJcbiAgICAgICAgcHJvZmlsZXMucHVzaChwcm9maWxlLmVuY29kZSgpKTtcclxuXHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgIHZlcnNpb246IDEsXHJcbiAgICAgICAgcHJvZmlsZXM6IHByb2ZpbGVzXHJcbiAgICB9KTtcclxuICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwicHJvZmlsZXNcIiwgZGF0YSk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBtYXJrX25lZWRfc2F2ZSgpIHtcclxuICAgIF9yZXF1aXJlc19zYXZlID0gdHJ1ZTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmVzX3NhdmUoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gX3JlcXVpcmVzX3NhdmU7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBwcm9maWxlcygpOiBDb25uZWN0aW9uUHJvZmlsZVtdIHtcclxuICAgIHJldHVybiBhdmFpbGFibGVfcHJvZmlsZXM7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBmaW5kX3Byb2ZpbGUoaWQ6IHN0cmluZyk6IENvbm5lY3Rpb25Qcm9maWxlIHwgdW5kZWZpbmVkIHtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBwcm9maWxlcygpKVxyXG4gICAgICAgIGlmIChwcm9maWxlLmlkID09IGlkKVxyXG4gICAgICAgICAgICByZXR1cm4gcHJvZmlsZTtcclxuXHJcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZmluZF9wcm9maWxlX2J5X25hbWUobmFtZTogc3RyaW5nKTogQ29ubmVjdGlvblByb2ZpbGUgfCB1bmRlZmluZWQge1xyXG4gICAgbmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKTtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBwcm9maWxlcygpKVxyXG4gICAgICAgIGlmICgocHJvZmlsZS5wcm9maWxlX25hbWUgfHwgXCJcIikudG9Mb3dlckNhc2UoKSA9PSBuYW1lKVxyXG4gICAgICAgICAgICByZXR1cm4gcHJvZmlsZTtcclxuXHJcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG59XHJcblxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRfcHJvZmlsZSgpOiBDb25uZWN0aW9uUHJvZmlsZSB7XHJcbiAgICByZXR1cm4gZmluZF9wcm9maWxlKFwiZGVmYXVsdFwiKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHNldF9kZWZhdWx0X3Byb2ZpbGUocHJvZmlsZTogQ29ubmVjdGlvblByb2ZpbGUpIHtcclxuICAgIGNvbnN0IG9sZF9kZWZhdWx0ID0gZGVmYXVsdF9wcm9maWxlKCk7XHJcbiAgICBpZiAob2xkX2RlZmF1bHQgJiYgb2xkX2RlZmF1bHQgIT0gcHJvZmlsZSkge1xyXG4gICAgICAgIG9sZF9kZWZhdWx0LmlkID0gZ3VpZCgpO1xyXG4gICAgfVxyXG4gICAgcHJvZmlsZS5pZCA9IFwiZGVmYXVsdFwiO1xyXG4gICAgcmV0dXJuIG9sZF9kZWZhdWx0O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZGVsZXRlX3Byb2ZpbGUocHJvZmlsZTogQ29ubmVjdGlvblByb2ZpbGUpIHtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcy5yZW1vdmUocHJvZmlsZSk7XHJcbn0iLCJpbXBvcnQge0Fic3RyYWN0Q29tbWFuZEhhbmRsZXIsIEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgU2VydmVyQ29tbWFuZH0gZnJvbSBcIi4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtjb25uZWN0aW9ufSBmcm9tIFwiLi4vY29ubmVjdGlvbi9IYW5kc2hha2VIYW5kbGVyXCI7XHJcblxyXG5pbXBvcnQgSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyID0gY29ubmVjdGlvbi5IYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbmltcG9ydCB7TmFtZUlkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL05hbWVJZGVudGl0eVwiO1xyXG5pbXBvcnQge1RlYUZvcnVtSWRlbnRpdHl9IGZyb20gXCIuL2lkZW50aXRpZXMvVGVhRm9ydW1JZGVudGl0eVwiO1xyXG5pbXBvcnQge1RlYVNwZWFrSWRlbnRpdHl9IGZyb20gXCIuL2lkZW50aXRpZXMvVGVhbVNwZWFrSWRlbnRpdHlcIjtcclxuXHJcbmV4cG9ydCBlbnVtIElkZW50aXRpZnlUeXBlIHtcclxuICAgIFRFQUZPUk8sXHJcbiAgICBURUFNU1BFQUssXHJcbiAgICBOSUNLTkFNRVxyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIElkZW50aXR5IHtcclxuICAgIGZhbGxiYWNrX25hbWUoKTogc3RyaW5nIHwgdW5kZWZpbmVkIDtcclxuICAgIHVpZCgpIDogc3RyaW5nO1xyXG4gICAgdHlwZSgpIDogSWRlbnRpdGlmeVR5cGU7XHJcblxyXG4gICAgdmFsaWQoKSA6IGJvb2xlYW47XHJcblxyXG4gICAgZW5jb2RlPygpIDogc3RyaW5nO1xyXG4gICAgZGVjb2RlKGRhdGE6IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+O1xyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikgOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZWNvZGVfaWRlbnRpdHkodHlwZTogSWRlbnRpdGlmeVR5cGUsIGRhdGE6IHN0cmluZykgOiBQcm9taXNlPElkZW50aXR5PiB7XHJcbiAgICBsZXQgaWRlbnRpdHk6IElkZW50aXR5O1xyXG4gICAgc3dpdGNoICh0eXBlKSB7XHJcbiAgICAgICAgY2FzZSBJZGVudGl0aWZ5VHlwZS5OSUNLTkFNRTpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgTmFtZUlkZW50aXR5KCk7XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgSWRlbnRpdGlmeVR5cGUuVEVBRk9STzpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgVGVhRm9ydW1JZGVudGl0eSh1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlIElkZW50aXRpZnlUeXBlLlRFQU1TUEVBSzpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgVGVhU3BlYWtJZGVudGl0eSh1bmRlZmluZWQsIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgfVxyXG4gICAgaWYoIWlkZW50aXR5KVxyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgICBhd2FpdCBpZGVudGl0eS5kZWNvZGUoZGF0YSlcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAvKiB0b2RvIGJldHRlciBlcnJvciBoYW5kbGluZyEgKi9cclxuICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcclxuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBpZGVudGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZV9pZGVudGl0eSh0eXBlOiBJZGVudGl0aWZ5VHlwZSkge1xyXG4gICAgbGV0IGlkZW50aXR5OiBJZGVudGl0eTtcclxuICAgIHN3aXRjaCAodHlwZSkge1xyXG4gICAgICAgIGNhc2UgSWRlbnRpdGlmeVR5cGUuTklDS05BTUU6XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IE5hbWVJZGVudGl0eSgpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlIElkZW50aXRpZnlUeXBlLlRFQUZPUk86XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IFRlYUZvcnVtSWRlbnRpdHkodW5kZWZpbmVkKTtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSBJZGVudGl0aWZ5VHlwZS5URUFNU1BFQUs6XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IFRlYVNwZWFrSWRlbnRpdHkodW5kZWZpbmVkLCB1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgIH1cclxuICAgIHJldHVybiBpZGVudGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyPFQgZXh0ZW5kcyBBYnN0cmFjdEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcj4gZXh0ZW5kcyBBYnN0cmFjdENvbW1hbmRIYW5kbGVyIHtcclxuICAgIHJlYWRvbmx5IGhhbmRsZTogVDtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24sIGhhbmRsZTogVCkge1xyXG4gICAgICAgIHN1cGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlID0gaGFuZGxlO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICBoYW5kbGVfY29tbWFuZChjb21tYW5kOiBTZXJ2ZXJDb21tYW5kKTogYm9vbGVhbiB7XHJcbiAgICAgICAgaWYoJC5pc0Z1bmN0aW9uKHRoaXNbY29tbWFuZC5jb21tYW5kXSkpXHJcbiAgICAgICAgICAgIHRoaXNbY29tbWFuZC5jb21tYW5kXShjb21tYW5kLmFyZ3VtZW50cyk7XHJcbiAgICAgICAgZWxzZSBpZihjb21tYW5kLmNvbW1hbmQgPT0gXCJlcnJvclwiKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJSZWNlaXZlZCB1bmtub3duIGNvbW1hbmQgd2hpbGUgaGFuZHNoYWtpbmcgKCVvKVwiKSwgY29tbWFuZCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgaW1wbGVtZW50cyBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciB7XHJcbiAgICBjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb247XHJcblxyXG4gICAgcHJvdGVjdGVkIGNhbGxiYWNrczogKChzdWNjZXNzOiBib29sZWFuLCBtZXNzYWdlPzogc3RyaW5nKSA9PiBhbnkpW10gPSBbXTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcclxuICAgIH1cclxuXHJcbiAgICByZWdpc3Rlcl9jYWxsYmFjayhjYWxsYmFjazogKHN1Y2Nlc3M6IGJvb2xlYW4sIG1lc3NhZ2U/OiBzdHJpbmcpID0+IGFueSkge1xyXG4gICAgICAgIHRoaXMuY2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xyXG4gICAgfVxyXG5cclxuICAgIGFic3RyYWN0IHN0YXJ0X2hhbmRzaGFrZSgpO1xyXG5cclxuICAgIHByb3RlY3RlZCB0cmlnZ2VyX3N1Y2Nlc3MoKSB7XHJcbiAgICAgICAgZm9yKGNvbnN0IGNhbGxiYWNrIG9mIHRoaXMuY2FsbGJhY2tzKVxyXG4gICAgICAgICAgICBjYWxsYmFjayh0cnVlKTtcclxuICAgIH1cclxuXHJcbiAgICBwcm90ZWN0ZWQgdHJpZ2dlcl9mYWlsKG1lc3NhZ2U6IHN0cmluZykge1xyXG4gICAgICAgIGZvcihjb25zdCBjYWxsYmFjayBvZiB0aGlzLmNhbGxiYWNrcylcclxuICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UsIG1lc3NhZ2UpO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtDb21tYW5kUmVzdWx0fSBmcm9tIFwiLi4vLi4vY29ubmVjdGlvbi9TZXJ2ZXJDb25uZWN0aW9uRGVjbGFyYXRpb25cIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7QWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9ufSBmcm9tIFwiLi4vLi4vY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZVwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi8uLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuXHJcbmltcG9ydCBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcjtcclxuaW1wb3J0IHtBYnN0cmFjdEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciwgSGFuZHNoYWtlQ29tbWFuZEhhbmRsZXIsIElkZW50aXRpZnlUeXBlLCBJZGVudGl0eX0gZnJvbSBcIi4uL0lkZW50aXR5XCI7XHJcblxyXG5jbGFzcyBOYW1lSGFuZHNoYWtlSGFuZGxlciBleHRlbmRzIEFic3RyYWN0SGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyIHtcclxuICAgIHJlYWRvbmx5IGlkZW50aXR5OiBOYW1lSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxOYW1lSGFuZHNoYWtlSGFuZGxlcj47XHJcblxyXG4gICAgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uLCBpZGVudGl0eTogTmFtZUlkZW50aXR5KSB7XHJcbiAgICAgICAgc3VwZXIoY29ubmVjdGlvbik7XHJcbiAgICAgICAgdGhpcy5pZGVudGl0eSA9IGlkZW50aXR5O1xyXG5cclxuICAgICAgICB0aGlzLmhhbmRsZXIgPSBuZXcgSGFuZHNoYWtlQ29tbWFuZEhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyW1wiaGFuZHNoYWtlaWRlbnRpdHlwcm9vZlwiXSA9ICgpID0+IHRoaXMudHJpZ2dlcl9mYWlsKFwic2VydmVyIHJlcXVlc3RlZCB1bmV4cGVjdGVkIHByb29mXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBjbGllbnRfbmlja25hbWU6IHRoaXMuaWRlbnRpdHkubmFtZSgpXHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBuYW1lIGJhc2VkIGhhbmRzaGFrZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIGJlZ2luIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIE5hbWVJZGVudGl0eSBpbXBsZW1lbnRzIElkZW50aXR5IHtcclxuICAgIHByaXZhdGUgX25hbWU6IHN0cmluZztcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihuYW1lPzogc3RyaW5nKSB7XHJcbiAgICAgICAgdGhpcy5fbmFtZSA9IG5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0X25hbWUobmFtZTogc3RyaW5nKSB7IHRoaXMuX25hbWUgPSBuYW1lOyB9XHJcblxyXG4gICAgbmFtZSgpIDogc3RyaW5nIHsgcmV0dXJuIHRoaXMuX25hbWU7IH1cclxuXHJcbiAgICBmYWxsYmFja19uYW1lKCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgdWlkKCk6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIGJ0b2EodGhpcy5fbmFtZSk7IC8vRklYTUUgaGFzaCFcclxuICAgIH1cclxuXHJcbiAgICB0eXBlKCk6IElkZW50aXRpZnlUeXBlIHtcclxuICAgICAgICByZXR1cm4gSWRlbnRpdGlmeVR5cGUuTklDS05BTUU7XHJcbiAgICB9XHJcblxyXG4gICAgdmFsaWQoKTogYm9vbGVhbiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWUgIT0gdW5kZWZpbmVkICYmIHRoaXMuX25hbWUubGVuZ3RoID49IDU7XHJcbiAgICB9XHJcblxyXG4gICAgZGVjb2RlKGRhdGEpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgZGF0YSA9IEpTT04ucGFyc2UoZGF0YSk7XHJcbiAgICAgICAgaWYoZGF0YS52ZXJzaW9uICE9PSAxKVxyXG4gICAgICAgICAgICB0aHJvdyBcImludmFsaWQgdmVyc2lvblwiO1xyXG5cclxuICAgICAgICB0aGlzLl9uYW1lID0gZGF0YVtcIm5hbWVcIl07XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZT8oKSA6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgICAgdmVyc2lvbjogMSxcclxuICAgICAgICAgICAgbmFtZTogdGhpcy5fbmFtZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikgOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIHJldHVybiBuZXcgTmFtZUhhbmRzaGFrZUhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICB9XHJcbn0iLCJpbXBvcnQge0Fic3RyYWN0U2VydmVyQ29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7Zm9ydW19IGZyb20gXCIuL3RlYXNwZWFrLWZvcnVtXCI7XHJcbmltcG9ydCB7Y29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vSGFuZHNoYWtlSGFuZGxlclwiO1xyXG5pbXBvcnQgSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyID0gY29ubmVjdGlvbi5IYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbmltcG9ydCB7QWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIsIEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyLCBJZGVudGl0aWZ5VHlwZSwgSWRlbnRpdHl9IGZyb20gXCIuLi9JZGVudGl0eVwiO1xyXG5cclxuY2xhc3MgVGVhRm9ydW1IYW5kc2hha2VIYW5kbGVyIGV4dGVuZHMgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgcmVhZG9ubHkgaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxUZWFGb3J1bUhhbmRzaGFrZUhhbmRsZXI+O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHkpIHtcclxuICAgICAgICBzdXBlcihjb25uZWN0aW9uKTtcclxuICAgICAgICB0aGlzLmlkZW50aXR5ID0gaWRlbnRpdHk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyID0gbmV3IEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlcltcImhhbmRzaGFrZWlkZW50aXR5cHJvb2ZcIl0gPSB0aGlzLmhhbmRsZV9wcm9vZi5iaW5kKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBkYXRhOiB0aGlzLmlkZW50aXR5LmRhdGEoKS5kYXRhX2pzb24oKVxyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgVGVhRm9ydW0gYmFzZWQgaGFuZHNoYWtlLiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIGJlZ2luIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9wcm9vZihqc29uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWluZGVudGl0eXByb29mXCIsIHtcclxuICAgICAgICAgICAgcHJvb2Y6IHRoaXMuaWRlbnRpdHkuZGF0YSgpLmRhdGFfc2lnbigpXHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gcHJvb2YgdGhlIGlkZW50aXR5LiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIHByb29mIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFRlYUZvcnVtSWRlbnRpdHkgaW1wbGVtZW50cyBJZGVudGl0eSB7XHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGlkZW50aXR5X2RhdGE6IGZvcnVtLkRhdGE7XHJcblxyXG4gICAgdmFsaWQoKSA6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiAhIXRoaXMuaWRlbnRpdHlfZGF0YSAmJiAhdGhpcy5pZGVudGl0eV9kYXRhLmlzX2V4cGlyZWQoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdHJ1Y3RvcihkYXRhOiBmb3J1bS5EYXRhKSB7XHJcbiAgICAgICAgdGhpcy5pZGVudGl0eV9kYXRhID0gZGF0YTtcclxuICAgIH1cclxuXHJcbiAgICBkYXRhKCkgOiBmb3J1bS5EYXRhIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5pZGVudGl0eV9kYXRhO1xyXG4gICAgfVxyXG5cclxuICAgIGRlY29kZShkYXRhKSA6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIGRhdGEgPSBKU09OLnBhcnNlKGRhdGEpO1xyXG4gICAgICAgIGlmKGRhdGEudmVyc2lvbiAhPT0gMSlcclxuICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIHZlcnNpb25cIjtcclxuXHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZSgpIDogc3RyaW5nIHtcclxuICAgICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICB2ZXJzaW9uOiAxXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3Bhd25faWRlbnRpdHlfaGFuZHNoYWtlX2hhbmRsZXIoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSA6IEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBUZWFGb3J1bUhhbmRzaGFrZUhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICB9XHJcblxyXG4gICAgZmFsbGJhY2tfbmFtZSgpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmlkZW50aXR5X2RhdGEgPyB0aGlzLmlkZW50aXR5X2RhdGEubmFtZSgpIDogdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIHR5cGUoKTogSWRlbnRpdGlmeVR5cGUge1xyXG4gICAgICAgIHJldHVybiBJZGVudGl0aWZ5VHlwZS5URUFGT1JPO1xyXG4gICAgfVxyXG5cclxuICAgIHVpZCgpOiBzdHJpbmcge1xyXG4gICAgICAgIC8vRklYTUU6IFJlYWwgVUlEIVxyXG4gICAgICAgIHJldHVybiBcIlRlYUZvcm8jXCIgKyAoKHRoaXMuaWRlbnRpdHlfZGF0YSA/IHRoaXMuaWRlbnRpdHlfZGF0YS5uYW1lKCkgOiBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiKSk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmxldCBzdGF0aWNfaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHk7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gc2V0X3N0YXRpY19pZGVudGl0eShpZGVudGl0eTogVGVhRm9ydW1JZGVudGl0eSkge1xyXG4gICAgc3RhdGljX2lkZW50aXR5ID0gaWRlbnRpdHk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVfZm9ydW0oKSB7XHJcbiAgICBpZihmb3J1bS5sb2dnZWRfaW4oKSAmJiAoIXN0YXRpY19pZGVudGl0eSB8fCBzdGF0aWNfaWRlbnRpdHkuZGF0YSgpICE9PSBmb3J1bS5kYXRhKCkpKSB7XHJcbiAgICAgICAgc3RhdGljX2lkZW50aXR5ID0gbmV3IFRlYUZvcnVtSWRlbnRpdHkoZm9ydW0uZGF0YSgpKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgc3RhdGljX2lkZW50aXR5ID0gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRfc3RhdGljX2ZvcnVtX2lkZW50aXR5KCkgOiBib29sZWFuIHtcclxuICAgIHJldHVybiBzdGF0aWNfaWRlbnRpdHkgJiYgc3RhdGljX2lkZW50aXR5LnZhbGlkKCk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBzdGF0aWNfZm9ydW1faWRlbnRpdHkoKSA6IFRlYUZvcnVtSWRlbnRpdHkgfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHN0YXRpY19pZGVudGl0eTtcclxufSIsImltcG9ydCB7YXJyYXlCdWZmZXJCYXNlNjQsIGJhc2U2NF9lbmNvZGVfYWIsIHN0cjJhYjh9IGZyb20gXCIuLi8uLi9tYWluXCI7XHJcbmltcG9ydCB7c2hhfSBmcm9tIFwiLi4vLi4vY3J5cHRvL3NoYVwiO1xyXG5pbXBvcnQge2FzbjF9IGZyb20gXCIuLi8uLi9jcnlwdG8vYXNuMVwiO1xyXG5pbXBvcnQge0Fic3RyYWN0U2VydmVyQ29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7c2V0dGluZ3N9IGZyb20gXCIuLi8uLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi8uLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuaW1wb3J0IEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciA9IGNvbm5lY3Rpb24uSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyO1xyXG5pbXBvcnQge0Fic3RyYWN0SGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyLCBIYW5kc2hha2VDb21tYW5kSGFuZGxlciwgSWRlbnRpdGlmeVR5cGUsIElkZW50aXR5fSBmcm9tIFwiLi4vSWRlbnRpdHlcIjtcclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgQ3J5cHRvSGVscGVyIHtcclxuICAgIGV4cG9ydCBmdW5jdGlvbiBiYXNlNjRfdXJsX2VuY29kZShzdHIpe1xyXG4gICAgICAgIHJldHVybiBzdHIucmVwbGFjZSgvXFwrL2csICctJykucmVwbGFjZSgvXFwvL2csICdfJykucmVwbGFjZSgvXFw9KyQvLCAnJyk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGJhc2U2NF91cmxfZGVjb2RlKHN0cjogc3RyaW5nLCBwYWQ/OiBib29sZWFuKXtcclxuICAgICAgICBpZih0eXBlb2YocGFkKSA9PT0gJ3VuZGVmaW5lZCcgfHwgcGFkKVxyXG4gICAgICAgICAgICBzdHIgPSAoc3RyICsgJz09PScpLnNsaWNlKDAsIHN0ci5sZW5ndGggKyAoc3RyLmxlbmd0aCAlIDQpKTtcclxuICAgICAgICByZXR1cm4gc3RyLnJlcGxhY2UoLy0vZywgJysnKS5yZXBsYWNlKC9fL2csICcvJyk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGFycmF5YnVmZmVyX3RvX3N0cmluZyhidWYpIHtcclxuICAgICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBuZXcgVWludDE2QXJyYXkoYnVmKSk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4cG9ydF9lY2Nfa2V5KGNyeXB0b19rZXk6IENyeXB0b0tleSwgcHVibGljX2tleTogYm9vbGVhbikge1xyXG4gICAgICAgIC8qXHJcbiAgICAgICAgICAgIFRvbWNyeXB0IHB1YmxpYyBrZXkgZXhwb3J0OlxyXG4gICAgICAgICAgICBpZiAodHlwZSA9PSBQS19QUklWQVRFKSB7XHJcbiAgICAgICAgICAgICAgIGZsYWdzWzBdID0gMTtcclxuICAgICAgICAgICAgICAgZXJyID0gZGVyX2VuY29kZV9zZXF1ZW5jZV9tdWx0aShvdXQsIG91dGxlbixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9CSVRfU1RSSU5HLCAgICAgIDFVTCwgZmxhZ3MsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTFRDX0FTTjFfU0hPUlRfSU5URUdFUiwgICAxVUwsICZrZXlfc2l6ZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5wdWJrZXkueCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5wdWJrZXkueSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5rLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0VPTCwgICAgICAgICAgICAgMFVMLCBOVUxMKTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgZmxhZ3NbMF0gPSAwO1xyXG4gICAgICAgICAgICAgICBlcnIgPSBkZXJfZW5jb2RlX3NlcXVlbmNlX211bHRpKG91dCwgb3V0bGVuLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0JJVF9TVFJJTkcsICAgICAgMVVMLCBmbGFncyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9TSE9SVF9JTlRFR0VSLCAgIDFVTCwgJmtleV9zaXplLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0lOVEVHRVIsICAgICAgICAgMVVMLCBrZXktPnB1YmtleS54LFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0lOVEVHRVIsICAgICAgICAgMVVMLCBrZXktPnB1YmtleS55LFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0VPTCwgICAgICAgICAgICAgMFVMLCBOVUxMKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgKi9cclxuXHJcbiAgICAgICAgY29uc3Qga2V5X2RhdGEgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmV4cG9ydEtleShcImp3a1wiLCBjcnlwdG9fa2V5KTtcclxuXHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuICAgICAgICBjb25zdCBsZW5ndGggPSBwdWJsaWNfa2V5ID8gNzkgOiAxMTQ7IC8qIG1heCBsZW5ndGhzISBEZXBlbmRzIG9uIHRoZSBwYWRkaW5nIGNvdWxkIGJlIGxlc3MgKi9cclxuICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheShsZW5ndGgpOyAvKiBmaXhlZCBBU04xIGxlbmd0aCAqL1xyXG4gICAgICAgIHsgLyogdGhlIGluaXRpYWwgc2VxdWVuY2UgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgzMDsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAwOyAvKiB3ZSB3aWxsIHNldCB0aGUgc2VxdWVuY2UgbGVuZ3RoIGxhdGVyICovXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHsgLyogdGhlIGZsYWdzIGJpdCBzdHJpbmcgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMzsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAyOyAvKiBsZW5ndGggKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwNzsgLyogZGF0YSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSBwdWJsaWNfa2V5ID8gMHgwMCA6IDB4ODA7IC8qIGZsYWcgMSBvciAwICgxID0gcHJpdmF0ZSBrZXkpKi9cclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBrZXkgc2l6ZSAoY29uc3QgMzIgZm9yIFAtMjU2KSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAyOyAvKiB0eXBlICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDE7IC8qIGxlbmd0aCAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IFggKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMjsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwOyAvKiBsZW5ndGggKi9cclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEueCwgZmFsc2UpKTtcclxuICAgICAgICAgICAgaWYocmF3LmNoYXJDb2RlQXQoMCkgPiAweDdGKSB7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXggLSAxXSArPSAxO1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IDMyOyBpKyspXHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgRE9NRXhjZXB0aW9uKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeCBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IFkgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMjsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwOyAvKiBsZW5ndGggKi9cclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEueSwgZmFsc2UpKTtcclxuICAgICAgICAgICAgaWYocmF3LmNoYXJDb2RlQXQoMCkgPiAweDdGKSB7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXggLSAxXSArPSAxO1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IDMyOyBpKyspXHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgRE9NRXhjZXB0aW9uKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeSBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZighcHVibGljX2tleSkge1xyXG4gICAgICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IEsgKi9cclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MjA7IC8qIGxlbmd0aCAqL1xyXG5cclxuICAgICAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEuZCwgZmFsc2UpKTtcclxuICAgICAgICAgICAgICAgIGlmKHJhdy5jaGFyQ29kZUF0KDApID4gMHg3Rikge1xyXG4gICAgICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCAtIDFdICs9IDE7XHJcbiAgICAgICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgMzI7IGkrKylcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pXHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeSBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBidWZmZXJbMV0gPSBpbmRleCAtIDI7IC8qIHNldCB0aGUgZmluYWwgc2VxdWVuY2UgbGVuZ3RoICovXHJcblxyXG4gICAgICAgIHJldHVybiBiYXNlNjRfZW5jb2RlX2FiKGJ1ZmZlci5idWZmZXIuc2xpY2UoMCwgaW5kZXgpKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBjcnlwdF9rZXkgPSBcImI5ZGZhYTdiZWU2YWM1N2FjN2I2NWYxMDk0YTFjMTU1ZTc0NzMyN2JjMmZlNWQ1MWM1MTIwMjNmZTU0YTI4MDIwMTAwNGU5MGFkMWRhYWFlMTA3NWQ1M2I3ZDU3MWMzMGUwNjNiNWE2MmE0YTAxN2JiMzk0ODMzYWEwOTgzZTZlXCI7XHJcbiAgICBmdW5jdGlvbiBjX3N0cmxlbihidWZmZXI6IFVpbnQ4QXJyYXksIG9mZnNldDogbnVtYmVyKSA6IG51bWJlciB7XHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuICAgICAgICB3aGlsZShpbmRleCArIG9mZnNldCA8IGJ1ZmZlci5sZW5ndGggJiYgYnVmZmVyW2luZGV4ICsgb2Zmc2V0XSAhPSAwKVxyXG4gICAgICAgICAgICBpbmRleCsrO1xyXG4gICAgICAgIHJldHVybiBpbmRleDtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVjcnlwdF90c19pZGVudGl0eShidWZmZXI6IFVpbnQ4QXJyYXkpIDogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgICAgICAvKiBidWZmZXIgY291bGQgY29udGFpbnMgYSB6ZXJvISAqL1xyXG4gICAgICAgIGNvbnN0IGhhc2ggPSBuZXcgVWludDhBcnJheShhd2FpdCBzaGEuc2hhMShidWZmZXIuYnVmZmVyLnNsaWNlKDIwLCAyMCArIGNfc3RybGVuKGJ1ZmZlciwgMjApKSkpO1xyXG4gICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAyMDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gaGFzaFtpXTtcclxuXHJcbiAgICAgICAgY29uc3QgbGVuZ3RoID0gTWF0aC5taW4oYnVmZmVyLmxlbmd0aCwgMTAwKTtcclxuICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpXSBePSBjcnlwdF9rZXkuY2hhckNvZGVBdChpKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGFycmF5YnVmZmVyX3RvX3N0cmluZyhidWZmZXIpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbmNyeXB0X3RzX2lkZW50aXR5KGJ1ZmZlcjogVWludDhBcnJheSkgOiBQcm9taXNlPHN0cmluZz4ge1xyXG4gICAgICAgIGNvbnN0IGxlbmd0aCA9IE1hdGgubWluKGJ1ZmZlci5sZW5ndGgsIDEwMCk7XHJcbiAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gY3J5cHRfa2V5LmNoYXJDb2RlQXQoaSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGhhc2ggPSBuZXcgVWludDhBcnJheShhd2FpdCBzaGEuc2hhMShidWZmZXIuYnVmZmVyLnNsaWNlKDIwLCAyMCArIGNfc3RybGVuKGJ1ZmZlciwgMjApKSkpO1xyXG4gICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAyMDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gaGFzaFtpXTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGJhc2U2NF9lbmNvZGVfYWIoYnVmZmVyKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEBwYXJhbSBidWZmZXIgYmFzZTY0IGVuY29kZWQgQVNOLjEgc3RyaW5nXHJcbiAgICAgKi9cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBkZWNvZGVfdG9tY3J5cHRfa2V5KGJ1ZmZlcjogc3RyaW5nKSB7XHJcbiAgICAgICAgbGV0IGRlY29kZWQ7XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGRlY29kZWQgPSBhc24xLmRlY29kZShhdG9iKGJ1ZmZlcikpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pXHJcbiAgICAgICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBwYXJzZSBrZXkgYnVmZmVyIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBsZXQge3gsIHksIGt9ID0ge1xyXG4gICAgICAgICAgICB4OiBkZWNvZGVkLmNoaWxkcmVuWzJdLmNvbnRlbnQoSW5maW5pdHksIGFzbjEuVGFnVHlwZS5WaXNpYmxlU3RyaW5nKSxcclxuICAgICAgICAgICAgeTogZGVjb2RlZC5jaGlsZHJlblszXS5jb250ZW50KEluZmluaXR5LCBhc24xLlRhZ1R5cGUuVmlzaWJsZVN0cmluZyksXHJcbiAgICAgICAgICAgIGs6IGRlY29kZWQuY2hpbGRyZW5bNF0uY29udGVudChJbmZpbml0eSwgYXNuMS5UYWdUeXBlLlZpc2libGVTdHJpbmcpXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaWYoeC5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZih4LmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBYIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgeCA9IHguc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoeS5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZih5LmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBZIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgeSA9IHkuc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoay5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZihrLmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBwcml2YXRlIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgayA9IGsuc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLypcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSB4OiAlcyAoJWQpXCIsIGJ0b2EoeCksIHgubGVuZ3RoKTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSB5OiAlcyAoJWQpXCIsIGJ0b2EoeSksIHkubGVuZ3RoKTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSBrOiAlcyAoJWQpXCIsIGJ0b2EoayksIGsubGVuZ3RoKTtcclxuICAgICAgICAqL1xyXG4gICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNydjogXCJQLTI1NlwiLFxyXG4gICAgICAgICAgICBkOiBiYXNlNjRfdXJsX2VuY29kZShidG9hKGspKSxcclxuICAgICAgICAgICAgeDogYmFzZTY0X3VybF9lbmNvZGUoYnRvYSh4KSksXHJcbiAgICAgICAgICAgIHk6IGJhc2U2NF91cmxfZW5jb2RlKGJ0b2EoeSkpLFxyXG5cclxuICAgICAgICAgICAgZXh0OiB0cnVlLFxyXG4gICAgICAgICAgICBrZXlfb3BzOltcImRlcml2ZUtleVwiLCBcInNpZ25cIl0sXHJcbiAgICAgICAgICAgIGt0eTpcIkVDXCIsXHJcbiAgICAgICAgfTtcclxuICAgIH1cclxufVxyXG5cclxuY2xhc3MgVGVhU3BlYWtIYW5kc2hha2VIYW5kbGVyIGV4dGVuZHMgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgaWRlbnRpdHk6IFRlYVNwZWFrSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxUZWFTcGVha0hhbmRzaGFrZUhhbmRsZXI+O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgaWRlbnRpdHk6IFRlYVNwZWFrSWRlbnRpdHkpIHtcclxuICAgICAgICBzdXBlcihjb25uZWN0aW9uKTtcclxuICAgICAgICB0aGlzLmlkZW50aXR5ID0gaWRlbnRpdHk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyID0gbmV3IEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlcltcImhhbmRzaGFrZWlkZW50aXR5cHJvb2ZcIl0gPSB0aGlzLmhhbmRsZV9wcm9vZi5iaW5kKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBwdWJsaWNLZXk6IHRoaXMuaWRlbnRpdHkucHVibGljX2tleVxyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgVGVhbVNwZWFrIGJhc2VkIGhhbmRzaGFrZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcblxyXG4gICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpXHJcbiAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9yLmV4dHJhX21lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuICAgICAgICAgICAgdGhpcy50cmlnZ2VyX2ZhaWwoXCJmYWlsZWQgdG8gZXhlY3V0ZSBiZWdpbiAoXCIgKyBlcnJvciArIFwiKVwiKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9wcm9vZihqc29uKSB7XHJcbiAgICAgICAgaWYoIWpzb25bMF1bXCJkaWdlc3RcIl0pIHtcclxuICAgICAgICAgICAgdGhpcy50cmlnZ2VyX2ZhaWwoXCJzZXJ2ZXIgdG9vIG9sZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5pZGVudGl0eS5zaWduX21lc3NhZ2UoanNvblswXVtcIm1lc3NhZ2VcIl0sIGpzb25bMF1bXCJkaWdlc3RcIl0pLnRoZW4ocHJvb2YgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwiaGFuZHNoYWtlaW5kZW50aXR5cHJvb2ZcIiwge3Byb29mOiBwcm9vZn0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCB0cihcIkZhaWxlZCB0byBwcm9vZiB0aGUgaWRlbnRpdHkuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9yLmV4dHJhX21lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuICAgICAgICAgICAgICAgIHRoaXMudHJpZ2dlcl9mYWlsKFwiZmFpbGVkIHRvIGV4ZWN1dGUgcHJvb2YgKFwiICsgZXJyb3IgKyBcIilcIik7XHJcbiAgICAgICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBzaWduIG1lc3NhZ2VcIik7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuY2xhc3MgSWRlbnRpdHlQT1dXb3JrZXIge1xyXG4gICAgcHJpdmF0ZSBfd29ya2VyOiBXb3JrZXI7XHJcbiAgICBwcml2YXRlIF9jdXJyZW50X2hhc2g6IHN0cmluZztcclxuICAgIHByaXZhdGUgX2Jlc3RfbGV2ZWw6IG51bWJlcjtcclxuXHJcbiAgICBhc3luYyBpbml0aWFsaXplKGtleTogc3RyaW5nKSB7XHJcbiAgICAgICAgdGhpcy5fd29ya2VyID0gbmV3IFdvcmtlcihzZXR0aW5ncy5zdGF0aWMoXCJ3b3JrZXJfZGlyZWN0b3J5XCIsIFwianMvd29ya2Vycy9cIikgKyBcIldvcmtlclBPVy5qc1wiKTtcclxuXHJcbiAgICAgICAgLyogaW5pdGlhbGl6ZSAqL1xyXG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3QgdGltZW91dF9pZCA9IHNldFRpbWVvdXQoKCkgPT4gcmVqZWN0KFwidGltZW91dFwiKSwgMTAwMCk7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKCFldmVudC5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZighZXZlbnQuZGF0YS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW5pdGlhbGl6ZSBmYWlsZWQgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHRoaXMuaGFuZGxlX21lc3NhZ2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbmVycm9yID0gZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiUE9XIFdvcmtlciBlcnJvciAlb1wiKSwgZXZlbnQpO1xyXG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG4gICAgICAgICAgICAgICAgcmVqZWN0KFwiRmFpbGVkIHRvIGxvYWQgd29ya2VyIChcIiArIGV2ZW50Lm1lc3NhZ2UgKyBcIilcIik7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8qIHNldCBkYXRhICovXHJcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLl93b3JrZXIucG9zdE1lc3NhZ2Uoe1xyXG4gICAgICAgICAgICAgICAgdHlwZTogXCJzZXRfZGF0YVwiLFxyXG4gICAgICAgICAgICAgICAgcHJpdmF0ZV9rZXk6IGtleSxcclxuICAgICAgICAgICAgICAgIGNvZGU6IFwic2V0X2RhdGFcIlxyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHRpbWVvdXRfaWQgPSBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChcInRpbWVvdXQgKGRhdGEpXCIpLCAxMDAwKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbm1lc3NhZ2UgPSBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKCFldmVudC5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEuc3VjY2Vzcykge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChcImluaXRpYWxpemUgb2YgZGF0YSBmYWlsZWQgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHRoaXMuaGFuZGxlX21lc3NhZ2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgbWluZShoYXNoOiBzdHJpbmcsIGl0ZXJhdGlvbnM6IG51bWJlciwgdGFyZ2V0OiBudW1iZXIsIHRpbWVvdXQ/OiBudW1iZXIpIDogUHJvbWlzZTxCb29sZWFuPiB7XHJcbiAgICAgICAgdGhpcy5fY3VycmVudF9oYXNoID0gaGFzaDtcclxuICAgICAgICBpZih0YXJnZXQgPCB0aGlzLl9iZXN0X2xldmVsKVxyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlPEJvb2xlYW4+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5fd29ya2VyLnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgIHR5cGU6IFwibWluZVwiLFxyXG4gICAgICAgICAgICAgICAgaGFzaDogdGhpcy5fY3VycmVudF9oYXNoLFxyXG4gICAgICAgICAgICAgICAgaXRlcmF0aW9uczogaXRlcmF0aW9ucyxcclxuICAgICAgICAgICAgICAgIHRhcmdldDogdGFyZ2V0LFxyXG4gICAgICAgICAgICAgICAgY29kZTogXCJtaW5lXCJcclxuICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0IChtaW5lKVwiKSwgdGltZW91dCB8fCA1MDAwKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbm1lc3NhZ2UgPSBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4gdGhpcy5oYW5kbGVfbWVzc2FnZShldmVudC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJpbnZhbGlkIGRhdGFcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmICghZXZlbnQuZGF0YS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwibWluaW5nIGZhaWxlZCAoXCIgKyBldmVudC5kYXRhLnN1Y2Nlc3MgKyBcIiB8IFwiICsgKGV2ZW50LmRhdGEubWVzc2FnZSB8fCBcInVua25vd24gZXJvcm9yXCIpICsgXCIpXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZihldmVudC5kYXRhLnJlc3VsdCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2Jlc3RfbGV2ZWwgPSBldmVudC5kYXRhLmxldmVsO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2N1cnJlbnRfaGFzaCA9IGV2ZW50LmRhdGEuaGFzaDtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHRydWUpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKGZhbHNlKTsgLyogbm8gcmVzdWx0ICovXHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgY3VycmVudF9oYXNoKCkgOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9jdXJyZW50X2hhc2g7XHJcbiAgICB9XHJcblxyXG4gICAgY3VycmVudF9sZXZlbCgpIDogbnVtYmVyIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fYmVzdF9sZXZlbDtcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBmaW5hbGl6ZSh0aW1lb3V0PzogbnVtYmVyKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBcImZpbmFsaXplXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgY29kZTogXCJmaW5hbGl6ZVwiXHJcbiAgICAgICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0XCIpLCB0aW1lb3V0IHx8IDI1MCk7XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4gdGhpcy5oYW5kbGVfbWVzc2FnZShldmVudC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEuc3VjY2Vzcykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJmYWlsZWQgdG8gZmluYWxpemUgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gZmluYWxpemUgUE9XIHdvcmtlciEgKCVvKVwiKSwgZXJyb3IpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fd29ya2VyLnRlcm1pbmF0ZSgpO1xyXG4gICAgICAgIHRoaXMuX3dvcmtlciA9IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9tZXNzYWdlKG1lc3NhZ2U6IGFueSkge1xyXG4gICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiUmVjZWl2ZWQgbWVzc2FnZTogJW9cIiksIG1lc3NhZ2UpO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgY2xhc3MgVGVhU3BlYWtJZGVudGl0eSBpbXBsZW1lbnRzIElkZW50aXR5IHtcclxuICAgIHN0YXRpYyBhc3luYyBnZW5lcmF0ZV9uZXcoKSA6IFByb21pc2U8VGVhU3BlYWtJZGVudGl0eT4ge1xyXG4gICAgICAgIGxldCBrZXk6IENyeXB0b0tleVBhaXI7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAga2V5ID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5nZW5lcmF0ZUtleSh7bmFtZTonRUNESCcsIG5hbWVkQ3VydmU6ICdQLTI1Nid9LCB0cnVlLCBbXCJkZXJpdmVLZXlcIl0pO1xyXG4gICAgICAgIH0gY2F0Y2goZSkge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJDb3VsZCBub3QgZ2VuZXJhdGUgYSBuZXcga2V5OiAlb1wiKSwgZSk7XHJcbiAgICAgICAgICAgIHRocm93IFwiRmFpbGVkIHRvIGdlbmVyYXRlIGtleXBhaXJcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgcHJpdmF0ZV9rZXkgPSBhd2FpdCBDcnlwdG9IZWxwZXIuZXhwb3J0X2VjY19rZXkoa2V5LnByaXZhdGVLZXksIGZhbHNlKTtcclxuXHJcbiAgICAgICAgY29uc3QgaWRlbnRpdHkgPSBuZXcgVGVhU3BlYWtJZGVudGl0eShwcml2YXRlX2tleSwgXCIwXCIsIHVuZGVmaW5lZCwgZmFsc2UpO1xyXG4gICAgICAgIGF3YWl0IGlkZW50aXR5LmluaXRpYWxpemUoKTtcclxuICAgICAgICByZXR1cm4gaWRlbnRpdHk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljIGFzeW5jIGltcG9ydF90cyh0c19zdHJpbmc6IHN0cmluZywgaW5pPzogYm9vbGVhbikgOiBQcm9taXNlPFRlYVNwZWFrSWRlbnRpdHk+IHtcclxuICAgICAgICBjb25zdCBwYXJzZV9zdHJpbmcgPSBzdHJpbmcgPT4ge1xyXG4gICAgICAgICAgICAvKiBwYXJzaW5nIHdpdGhvdXQgSU5JIHN0cnVjdHVyZSAqL1xyXG4gICAgICAgICAgICBjb25zdCBWX2luZGV4ID0gc3RyaW5nLmluZGV4T2YoJ1YnKTtcclxuICAgICAgICAgICAgaWYoVl9pbmRleCA9PSAtMSkgdGhyb3cgXCJpbnZhbGlkIGlucHV0IChtaXNzaW5nIFYpXCI7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgaGFzaDogc3RyaW5nLnN1YnN0cigwLCBWX2luZGV4KSxcclxuICAgICAgICAgICAgICAgIGRhdGE6IHN0cmluZy5zdWJzdHIoVl9pbmRleCArIDEpLFxyXG4gICAgICAgICAgICAgICAgbmFtZTogXCJUZWFTcGVhayB1c2VyXCJcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIGNvbnN0IHtoYXNoLCBkYXRhLCBuYW1lfSA9ICghaW5pID8gKCkgPT4gcGFyc2Vfc3RyaW5nKHRzX3N0cmluZykgOiAoKSA9PiB7XHJcbiAgICAgICAgICAgIC8qIHBhcnNpbmcgd2l0aCBJTkkgc3RydWN0dXJlICovXHJcbiAgICAgICAgICAgIGxldCBpZGVudGl0eTogc3RyaW5nLCBuYW1lOiBzdHJpbmc7XHJcblxyXG4gICAgICAgICAgICBmb3IoY29uc3QgbGluZSBvZiB0c19zdHJpbmcuc3BsaXQoXCJcXG5cIikpIHtcclxuICAgICAgICAgICAgICAgIGlmKGxpbmUuc3RhcnRzV2l0aChcImlkZW50aXR5PVwiKSlcclxuICAgICAgICAgICAgICAgICAgICBpZGVudGl0eSA9IGxpbmUuc3Vic3RyKDkpO1xyXG4gICAgICAgICAgICAgICAgZWxzZSBpZihsaW5lLnN0YXJ0c1dpdGgoXCJuaWNrbmFtZT1cIikpXHJcbiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGxpbmUuc3Vic3RyKDkpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZighaWRlbnRpdHkpIHRocm93IFwibWlzc2luZyBpZGVudGl0eSBrZXl3b3JkXCI7XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gaWRlbnRpdHkubWF0Y2goL15cIj8oWzAtOV0rVlswLTlhLXpBLVorXFwvXStbPV0rKVwiPyQvKVsxXTtcclxuICAgICAgICAgICAgaWYoIWlkZW50aXR5KSB0aHJvdyBcImludmFsaWQgaWRlbnRpdHkga2V5IHZhbHVlXCI7XHJcblxyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBwYXJzZV9zdHJpbmcoaWRlbnRpdHkpO1xyXG4gICAgICAgICAgICByZXN1bHQubmFtZSA9IG5hbWUgfHwgcmVzdWx0Lm5hbWU7XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfSkoKTtcclxuXHJcbiAgICAgICAgaWYoIXRzX3N0cmluZy5tYXRjaCgvWzAtOV0rL2cpKSB0aHJvdyBcImludmFsaWQgaGFzaCFcIjtcclxuXHJcbiAgICAgICAgbGV0IGJ1ZmZlcjtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBidWZmZXIgPSBuZXcgVWludDhBcnJheShhcnJheUJ1ZmZlckJhc2U2NChkYXRhKSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gZGVjb2RlIGdpdmVuIGJhc2U2NCBkYXRhICglcylcIiksIGRhdGEpO1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBiYXNlIGRhdGEgKGJhc2U2NCBkZWNvZGUgZmFpbGVkKVwiO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBrZXk2NCA9IGF3YWl0IENyeXB0b0hlbHBlci5kZWNyeXB0X3RzX2lkZW50aXR5KG5ldyBVaW50OEFycmF5KGFycmF5QnVmZmVyQmFzZTY0KGRhdGEpKSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gbmV3IFRlYVNwZWFrSWRlbnRpdHkoa2V5NjQsIGhhc2gsIG5hbWUsIGZhbHNlKTtcclxuICAgICAgICBhd2FpdCBpZGVudGl0eS5pbml0aWFsaXplKCk7XHJcbiAgICAgICAgcmV0dXJuIGlkZW50aXR5O1xyXG4gICAgfVxyXG5cclxuICAgIGhhc2hfbnVtYmVyOiBzdHJpbmc7IC8qIGhhc2ggc3VmZml4IGZvciB0aGUgcHJpdmF0ZSBrZXkgKi9cclxuICAgIHByaXZhdGVfa2V5OiBzdHJpbmc7IC8qIGJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgcHJpdmF0ZSBrZXkgKi9cclxuICAgIF9uYW1lOiBzdHJpbmc7XHJcblxyXG4gICAgcHVibGljX2tleTogc3RyaW5nOyAvKiBvbmx5IHNldCB3aGVuIGluaXRpYWxpemVkICovXHJcblxyXG4gICAgcHJpdmF0ZSBfaW5pdGlhbGl6ZWQ6IGJvb2xlYW47XHJcbiAgICBwcml2YXRlIF9jcnlwdG9fa2V5OiBDcnlwdG9LZXk7XHJcbiAgICBwcml2YXRlIF9jcnlwdG9fa2V5X3NpZ246IENyeXB0b0tleTtcclxuXHJcbiAgICBwcml2YXRlIF91bmlxdWVfaWQ6IHN0cmluZztcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihwcml2YXRlX2tleT86IHN0cmluZywgaGFzaD86IHN0cmluZywgbmFtZT86IHN0cmluZywgaW5pdGlhbGl6ZT86IGJvb2xlYW4pIHtcclxuICAgICAgICB0aGlzLnByaXZhdGVfa2V5ID0gcHJpdmF0ZV9rZXk7XHJcbiAgICAgICAgdGhpcy5oYXNoX251bWJlciA9IGhhc2ggfHwgXCIwXCI7XHJcbiAgICAgICAgdGhpcy5fbmFtZSA9IG5hbWU7XHJcblxyXG4gICAgICAgIGlmKHRoaXMucHJpdmF0ZV9rZXkgJiYgKHR5cGVvZihpbml0aWFsaXplKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCBpbml0aWFsaXplKSkge1xyXG4gICAgICAgICAgICB0aGlzLmluaXRpYWxpemUoKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBUZWFTcGVha0lkZW50aXR5ICglcylcIiwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5faW5pdGlhbGl6ZWQgPSBmYWxzZTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZhbGxiYWNrX25hbWUoKTogc3RyaW5nIHwgdW5kZWZpbmVkICB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgdWlkKCk6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3VuaXF1ZV9pZDtcclxuICAgIH1cclxuXHJcbiAgICB0eXBlKCk6IElkZW50aXRpZnlUeXBlIHtcclxuICAgICAgICByZXR1cm4gSWRlbnRpdGlmeVR5cGUuVEVBTVNQRUFLO1xyXG4gICAgfVxyXG5cclxuICAgIHZhbGlkKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9pbml0aWFsaXplZCAmJiAhIXRoaXMuX2NyeXB0b19rZXkgJiYgISF0aGlzLl9jcnlwdG9fa2V5X3NpZ247XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZGVjb2RlKGRhdGE6IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICBjb25zdCBqc29uID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgICAgICBpZighanNvbikgdGhyb3cgXCJpbnZhbGlkIGpzb25cIjtcclxuXHJcbiAgICAgICAgaWYoanNvbi52ZXJzaW9uID09IDIpIHtcclxuICAgICAgICAgICAgdGhpcy5wcml2YXRlX2tleSA9IGpzb24ua2V5O1xyXG4gICAgICAgICAgICB0aGlzLmhhc2hfbnVtYmVyID0ganNvbi5oYXNoO1xyXG4gICAgICAgICAgICB0aGlzLl9uYW1lID0ganNvbi5uYW1lO1xyXG4gICAgICAgIH0gZWxzZSBpZihqc29uLnZlcnNpb24gPT0gMSkge1xyXG4gICAgICAgICAgICBjb25zdCBrZXkgPSBqc29uLmtleTtcclxuICAgICAgICAgICAgdGhpcy5fbmFtZSA9IGpzb24ubmFtZTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IGNsb25lID0gYXdhaXQgVGVhU3BlYWtJZGVudGl0eS5pbXBvcnRfdHMoa2V5LCBmYWxzZSk7XHJcbiAgICAgICAgICAgIHRoaXMucHJpdmF0ZV9rZXkgPSBjbG9uZS5wcml2YXRlX2tleTtcclxuICAgICAgICAgICAgdGhpcy5oYXNoX251bWJlciA9IGNsb25lLmhhc2hfbnVtYmVyO1xyXG4gICAgICAgIH0gZWxzZVxyXG4gICAgICAgICAgICB0aHJvdyBcImludmFsaWQgdmVyc2lvblwiO1xyXG5cclxuICAgICAgICBhd2FpdCB0aGlzLmluaXRpYWxpemUoKTtcclxuICAgIH1cclxuXHJcbiAgICBlbmNvZGU/KCkgOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh7XHJcbiAgICAgICAgICAgIGtleTogdGhpcy5wcml2YXRlX2tleSxcclxuICAgICAgICAgICAgaGFzaDogdGhpcy5oYXNoX251bWJlcixcclxuICAgICAgICAgICAgbmFtZTogdGhpcy5fbmFtZSxcclxuICAgICAgICAgICAgdmVyc2lvbjogMlxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIGxldmVsKCkgOiBQcm9taXNlPG51bWJlcj4ge1xyXG4gICAgICAgIGlmKCF0aGlzLl9pbml0aWFsaXplZCB8fCAhdGhpcy5wdWJsaWNfa2V5KVxyXG4gICAgICAgICAgICB0aHJvdyBcIm5vdCBpbml0aWFsaXplZFwiO1xyXG5cclxuICAgICAgICBjb25zdCBoYXNoID0gbmV3IFVpbnQ4QXJyYXkoYXdhaXQgc2hhLnNoYTEodGhpcy5wdWJsaWNfa2V5ICsgdGhpcy5oYXNoX251bWJlcikpO1xyXG5cclxuICAgICAgICBsZXQgbGV2ZWwgPSAwO1xyXG4gICAgICAgIHdoaWxlKGxldmVsIDwgaGFzaC5ieXRlTGVuZ3RoICYmIGhhc2hbbGV2ZWxdID09IDApXHJcbiAgICAgICAgICAgIGxldmVsKys7XHJcblxyXG4gICAgICAgIGlmKGxldmVsID49IGhhc2guYnl0ZUxlbmd0aCkge1xyXG4gICAgICAgICAgICBsZXZlbCA9IDI1NjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBsZXQgYnl0ZSA9IGhhc2hbbGV2ZWxdO1xyXG4gICAgICAgICAgICBsZXZlbCA8PD0gMztcclxuICAgICAgICAgICAgd2hpbGUoKGJ5dGUgJiAweDEpID09IDApIHtcclxuICAgICAgICAgICAgICAgIGxldmVsKys7XHJcbiAgICAgICAgICAgICAgICBieXRlID4+PSAxO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gbGV2ZWw7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gYVxyXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGJcclxuICAgICAqIEBkZXNjcmlwdGlvbiBiIG11c3QgYmUgc21hbGxlciAoaW4gYnl0ZXMpIHRoZW4gYVxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIHN0cmluZ19hZGQoYTogc3RyaW5nLCBiOiBzdHJpbmcpIHtcclxuICAgICAgICBjb25zdCBjaGFyX3Jlc3VsdDogbnVtYmVyW10gPSBbXTtcclxuICAgICAgICBjb25zdCBjaGFyX2EgPSBbLi4uYV0ucmV2ZXJzZSgpLm1hcChlID0+IGUuY2hhckNvZGVBdCgwKSk7XHJcbiAgICAgICAgY29uc3QgY2hhcl9iID0gWy4uLmJdLnJldmVyc2UoKS5tYXAoZSA9PiBlLmNoYXJDb2RlQXQoMCkpO1xyXG5cclxuICAgICAgICBsZXQgY2FycnkgPSBmYWxzZTtcclxuICAgICAgICB3aGlsZShjaGFyX2IubGVuZ3RoID4gMCkge1xyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gY2hhcl9iLnBvcF9mcm9udCgpICsgY2hhcl9hLnBvcF9mcm9udCgpICsgKGNhcnJ5ID8gMSA6IDApIC0gNDg7XHJcbiAgICAgICAgICAgIGlmKChjYXJyeSA9IHJlc3VsdCA+IDU3KSlcclxuICAgICAgICAgICAgICAgIHJlc3VsdCAtPSAxMDtcclxuICAgICAgICAgICAgY2hhcl9yZXN1bHQucHVzaChyZXN1bHQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgd2hpbGUoY2hhcl9hLmxlbmd0aCA+IDApIHtcclxuICAgICAgICAgICAgbGV0IHJlc3VsdCA9IGNoYXJfYS5wb3BfZnJvbnQoKSArIChjYXJyeSA/IDEgOiAwKTtcclxuICAgICAgICAgICAgaWYoKGNhcnJ5ID0gcmVzdWx0ID4gNTcpKVxyXG4gICAgICAgICAgICAgICAgcmVzdWx0IC09IDEwO1xyXG4gICAgICAgICAgICBjaGFyX3Jlc3VsdC5wdXNoKHJlc3VsdCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihjYXJyeSlcclxuICAgICAgICAgICAgY2hhcl9yZXN1bHQucHVzaCg0OSk7XHJcblxyXG4gICAgICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJfcmVzdWx0LnNsaWNlKCkucmV2ZXJzZSgpKTtcclxuICAgIH1cclxuXHJcblxyXG4gICAgYXN5bmMgaW1wcm92ZV9sZXZlbF9mb3IodGltZTogbnVtYmVyLCB0aHJlYWRzOiBudW1iZXIpIDogUHJvbWlzZTxCb29sZWFuPiB7XHJcbiAgICAgICAgbGV0IGFjdGl2ZSA9IHRydWU7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiBhY3RpdmUgPSBmYWxzZSwgdGltZSk7XHJcblxyXG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmltcHJvdmVfbGV2ZWwoLTEsIHRocmVhZHMsICgpID0+IGFjdGl2ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgaW1wcm92ZV9sZXZlbCh0YXJnZXQ6IG51bWJlciwgdGhyZWFkczogbnVtYmVyLCBhY3RpdmVfY2FsbGJhY2s6ICgpID0+IGJvb2xlYW4sIGNhbGxiYWNrX2xldmVsPzogKGN1cnJlbnQ6IG51bWJlcikgPT4gYW55LCBjYWxsYmFja19zdGF0dXM/OiAoaGFzaF9yYXRlOiBudW1iZXIpID0+IGFueSkgOiBQcm9taXNlPEJvb2xlYW4+IHtcclxuICAgICAgICBpZighdGhpcy5faW5pdGlhbGl6ZWQgfHwgIXRoaXMucHVibGljX2tleSlcclxuICAgICAgICAgICAgdGhyb3cgXCJub3QgaW5pdGlhbGl6ZWRcIjtcclxuICAgICAgICBpZih0YXJnZXQgPT0gLTEpIC8qIGdldCB0aGUgaGlnaGVzdCBsZXZlbCBwb3NzaWJsZSAqL1xyXG4gICAgICAgICAgICB0YXJnZXQgPSAwO1xyXG4gICAgICAgIGVsc2UgaWYodGFyZ2V0IDw9IGF3YWl0IHRoaXMubGV2ZWwoKSlcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcblxyXG4gICAgICAgIGNvbnN0IHdvcmtlcnM6IElkZW50aXR5UE9XV29ya2VyW10gPSBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgaXRlcmF0aW9ucyA9IDEwMDAwMDtcclxuICAgICAgICBsZXQgY3VycmVudF9oYXNoO1xyXG4gICAgICAgIGNvbnN0IG5leHRfaGFzaCA9ICgpID0+IHtcclxuICAgICAgICAgICAgaWYoIWN1cnJlbnRfaGFzaClcclxuICAgICAgICAgICAgICAgIHJldHVybiAoY3VycmVudF9oYXNoID0gdGhpcy5oYXNoX251bWJlcik7XHJcblxyXG4gICAgICAgICAgICBpZihjdXJyZW50X2hhc2gubGVuZ3RoIDwgaXRlcmF0aW9ucy50b1N0cmluZygpLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgY3VycmVudF9oYXNoID0gdGhpcy5zdHJpbmdfYWRkKGl0ZXJhdGlvbnMudG9TdHJpbmcoKSwgY3VycmVudF9oYXNoKTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIGN1cnJlbnRfaGFzaCA9IHRoaXMuc3RyaW5nX2FkZChjdXJyZW50X2hhc2gsIGl0ZXJhdGlvbnMudG9TdHJpbmcoKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIGN1cnJlbnRfaGFzaDtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB7IC8qIGluaXQgKi9cclxuICAgICAgICAgICAgY29uc3QgaW5pdGlhbGl6ZV9wcm9taXNlOiBQcm9taXNlPHZvaWQ+W10gPSBbXTtcclxuICAgICAgICAgICAgZm9yKGxldCBpbmRleCA9IDA7IGluZGV4ICA8IHRocmVhZHM7IGluZGV4KyspIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IHdvcmtlciA9IG5ldyBJZGVudGl0eVBPV1dvcmtlcigpO1xyXG4gICAgICAgICAgICAgICAgd29ya2Vycy5wdXNoKHdvcmtlcik7XHJcbiAgICAgICAgICAgICAgICBpbml0aWFsaXplX3Byb21pc2UucHVzaCh3b3JrZXIuaW5pdGlhbGl6ZSh0aGlzLnB1YmxpY19rZXkpKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGluaXRpYWxpemVfcHJvbWlzZSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBpbml0aWFsaXplXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCByZXN1bHQgPSBmYWxzZTtcclxuICAgICAgICBsZXQgYmVzdF9sZXZlbCA9IDA7XHJcbiAgICAgICAgbGV0IHRhcmdldF9sZXZlbCA9IHRhcmdldCA+IDAgPyB0YXJnZXQgOiBhd2FpdCB0aGlzLmxldmVsKCkgKyAxO1xyXG5cclxuICAgICAgICBjb25zdCB3b3JrZXJfcHJvbWlzZTogUHJvbWlzZTx2b2lkPltdID0gW107XHJcblxyXG4gICAgICAgIGNvbnN0IGhhc2hfdGltZXN0YW1wczogbnVtYmVyW10gPSBbXTtcclxuICAgICAgICBsZXQgbGFzdF9oYXNocmF0ZV91cGRhdGU6IG51bWJlciA9IDA7XHJcblxyXG4gICAgICAgIGNvbnN0IHVwZGF0ZV9oYXNocmF0ZSA9ICgpID0+IHtcclxuICAgICAgICAgICAgaWYoIWNhbGxiYWNrX3N0YXR1cykgcmV0dXJuO1xyXG4gICAgICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgICAgICBoYXNoX3RpbWVzdGFtcHMucHVzaChub3cpO1xyXG5cclxuICAgICAgICAgICAgaWYobGFzdF9oYXNocmF0ZV91cGRhdGUgKyAxMDAwIDwgbm93KSB7XHJcbiAgICAgICAgICAgICAgICBsYXN0X2hhc2hyYXRlX3VwZGF0ZSA9IG5vdztcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0ID0gbm93IC0gMTAgKiAxMDAwOyAvKiAxMHMgKi9cclxuICAgICAgICAgICAgICAgIGNvbnN0IHJvdW5kcyA9IGhhc2hfdGltZXN0YW1wcy5maWx0ZXIoZSA9PiBlID4gdGltZW91dCk7XHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19zdGF0dXMoTWF0aC5jZWlsKChyb3VuZHMubGVuZ3RoICogaXRlcmF0aW9ucykgLyBNYXRoLmNlaWwoKG5vdyAtIHJvdW5kc1swXSkgLyAxMDAwKSkpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXN1bHQgPSBhd2FpdCBuZXcgUHJvbWlzZTxib29sZWFuPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICBsZXQgYWN0aXZlID0gdHJ1ZTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBleGl0ID0gKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHJlc29sdmUodHJ1ZSksIDEwMDApO1xyXG4gICAgICAgICAgICAgICAgICAgIFByb21pc2UuYWxsKHdvcmtlcl9wcm9taXNlKS50aGVuKHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh0cnVlKTtcclxuICAgICAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiByZXNvbHZlKHRydWUpKTtcclxuICAgICAgICAgICAgICAgICAgICBhY3RpdmUgPSBmYWxzZTtcclxuICAgICAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHdvcmtlciBvZiB3b3JrZXJzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgd29ya2VyX21pbmUgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFhY3RpdmUpIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHByb21pc2UgPSB3b3JrZXIubWluZShuZXh0X2hhc2goKSwgaXRlcmF0aW9ucywgdGFyZ2V0X2xldmVsKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcCA9IHByb21pc2UudGhlbihyZXN1bHQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlX2hhc2hyYXRlKCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgd29ya2VyX3Byb21pc2UucmVtb3ZlKHApO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlc3VsdC52YWx1ZU9mKCkpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZih3b3JrZXIuY3VycmVudF9sZXZlbCgpID4gYmVzdF9sZXZlbCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmhhc2hfbnVtYmVyID0gd29ya2VyLmN1cnJlbnRfaGFzaCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgXCJGb3VuZCBuZXcgYmVzdCBhdCAlcyAoJWQpLiBPbGQgd2FzICVkXCIsIHRoaXMuaGFzaF9udW1iZXIsIHdvcmtlci5jdXJyZW50X2xldmVsKCksIGJlc3RfbGV2ZWwpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZXN0X2xldmVsID0gd29ya2VyLmN1cnJlbnRfbGV2ZWwoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoY2FsbGJhY2tfbGV2ZWwpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19sZXZlbChiZXN0X2xldmVsKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKGFjdGl2ZSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZih0YXJnZXQgPiAwKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhpdCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfbGV2ZWwgPSBiZXN0X2xldmVsICsgMTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoYWN0aXZlICYmIChhY3RpdmUgPSBhY3RpdmVfY2FsbGJhY2soKSkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB3b3JrZXJfbWluZSgpLCAwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4aXQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmtlcl9wcm9taXNlLnJlbW92ZShwKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBcIlBPVyB3b3JrZXIgZXJyb3IgJW9cIiwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgd29ya2VyX3Byb21pc2UucHVzaChwKTtcclxuICAgICAgICAgICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB3b3JrZXJfbWluZSgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIC8vZXJyb3IgYWxyZWFkeSBwcmludGVkIGJlZm9yZSByZWplY3QgaGFkIGJlZW4gY2FsbGVkXHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB7IC8qIHNodXRkb3duICovXHJcbiAgICAgICAgICAgIGNvbnN0IGZpbmFsaXplX3Byb21pc2U6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qgd29ya2VyIG9mIHdvcmtlcnMpXHJcbiAgICAgICAgICAgICAgICBmaW5hbGl6ZV9wcm9taXNlLnB1c2god29ya2VyLmZpbmFsaXplKDI1MCkpO1xyXG5cclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGZpbmFsaXplX3Byb21pc2UpO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gZmluYWxpemVcIjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIGlmKCF0aGlzLnByaXZhdGVfa2V5KVxyXG4gICAgICAgICAgICB0aHJvdyBcIkludmFsaWQgcHJpdmF0ZSBrZXlcIjtcclxuXHJcbiAgICAgICAgbGV0IGp3azogYW55O1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGp3ayA9IGF3YWl0IENyeXB0b0hlbHBlci5kZWNvZGVfdG9tY3J5cHRfa2V5KHRoaXMucHJpdmF0ZV9rZXkpO1xyXG4gICAgICAgICAgICBpZighandrKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJyZXN1bHQgdW5kZWZpbmVkXCI7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBwYXJzZSBrZXkgKFwiICsgZXJyb3IgKyBcIilcIjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NyeXB0b19rZXlfc2lnbiA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuaW1wb3J0S2V5KFwiandrXCIsIGp3aywge25hbWU6J0VDRFNBJywgbmFtZWRDdXJ2ZTogJ1AtMjU2J30sIGZhbHNlLCBbXCJzaWduXCJdKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHRocm93IFwiZmFpbGVkIHRvIGNyZWF0ZSBjcnlwdG8gc2lnbiBrZXlcIjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NyeXB0b19rZXkgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmltcG9ydEtleShcImp3a1wiLCBqd2ssIHtuYW1lOidFQ0RIJywgbmFtZWRDdXJ2ZTogJ1AtMjU2J30sIHRydWUsIFtcImRlcml2ZUtleVwiXSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgZXJyb3IpO1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBjcmVhdGUgY3J5cHRvIGtleVwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgdGhpcy5wdWJsaWNfa2V5ID0gYXdhaXQgQ3J5cHRvSGVscGVyLmV4cG9ydF9lY2Nfa2V5KHRoaXMuX2NyeXB0b19rZXksIHRydWUpO1xyXG4gICAgICAgICAgICB0aGlzLl91bmlxdWVfaWQgPSBiYXNlNjRfZW5jb2RlX2FiKGF3YWl0IHNoYS5zaGExKHRoaXMucHVibGljX2tleSkpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIGVycm9yKTtcclxuICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gY2FsY3VsYXRlIHVuaXF1ZSBpZFwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5faW5pdGlhbGl6ZWQgPSB0cnVlO1xyXG4gICAgICAgIC8vY29uc3QgcHVibGljX2tleSA9IGF3YWl0IHByb2ZpbGVzLmlkZW50aXRpZXMuQ3J5cHRvSGVscGVyLmV4cG9ydF9lY2Nfa2V5KGtleSwgdHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZXhwb3J0X3RzKGluaT86IGJvb2xlYW4pIDogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgICAgICBpZighdGhpcy5wcml2YXRlX2tleSlcclxuICAgICAgICAgICAgdGhyb3cgXCJJbnZhbGlkIHByaXZhdGUga2V5XCI7XHJcblxyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5oYXNoX251bWJlciArIFwiVlwiICsgYXdhaXQgQ3J5cHRvSGVscGVyLmVuY3J5cHRfdHNfaWRlbnRpdHkobmV3IFVpbnQ4QXJyYXkoc3RyMmFiOCh0aGlzLnByaXZhdGVfa2V5KSkpO1xyXG4gICAgICAgIGlmKCFpbmkpIHJldHVybiBpZGVudGl0eTtcclxuXHJcbiAgICAgICAgcmV0dXJuIFwiW0lkZW50aXR5XVxcblwiICtcclxuICAgICAgICAgICAgICAgIFwiaWQ9VGVhV2ViLUV4cG9ydGVkXFxuXCIgK1xyXG4gICAgICAgICAgICAgICAgXCJpZGVudGl0eT1cXFwiXCIgKyBpZGVudGl0eSArIFwiXFxcIlxcblwiICtcclxuICAgICAgICAgICAgICAgIFwibmlja25hbWU9XFxcIlwiICsgdGhpcy5mYWxsYmFja19uYW1lKCkgKyBcIlxcXCJcXG5cIiArXHJcbiAgICAgICAgICAgICAgICBcInBob25ldGljX25pY2tuYW1lPVwiO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIHNpZ25fbWVzc2FnZShtZXNzYWdlOiBzdHJpbmcsIGhhc2g6IHN0cmluZyA9IFwiU0hBLTI1NlwiKSA6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICAgICAgLyogYnJpbmcgdGhpcyB0byBsaWJ0b21jcnlwdCBmb3JtYXQgKi9cclxuICAgICAgICBjb25zdCBzaWduX2J1ZmZlciA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuc2lnbih7XHJcbiAgICAgICAgICAgIG5hbWU6IFwiRUNEU0FcIixcclxuICAgICAgICAgICAgaGFzaDogaGFzaFxyXG4gICAgICAgIH0sIHRoaXMuX2NyeXB0b19rZXlfc2lnbiwgc3RyMmFiOChtZXNzYWdlKSk7XHJcbiAgICAgICAgY29uc3Qgc2lnbiA9IG5ldyBVaW50OEFycmF5KHNpZ25fYnVmZmVyKTtcclxuICAgICAgICAvKiBmaXJzdCAzMiByIGJpdHMgfCBsYXN0IDMyIHMgYml0cyAqL1xyXG5cclxuICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheSg3Mik7XHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuXHJcbiAgICAgICAgeyAvKiB0aGUgaW5pdGlhbCBzZXF1ZW5jZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDMwOyAvKiB0eXBlICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDA7IC8qIHdlIHdpbGwgc2V0IHRoZSBzZXF1ZW5jZSBsZW5ndGggbGF0ZXIgKi9cclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBpbnRlZ2VyIHIgICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgyMDsgLyogbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICBpZihzaWduWzBdID4gMHg3Rikge1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4IC0gMV0gKz0gMTtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDA7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAzMjsgaSsrKVxyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gc2lnbltpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBpbnRlZ2VyIHMgICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgyMDsgLyogbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICBpZihzaWduWzMyXSA+IDB4N0YpIHtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCAtIDFdICs9IDE7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAwO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgMzI7IGkrKylcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IHNpZ25bMzIgKyBpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnVmZmVyWzFdID0gaW5kZXggLSAyO1xyXG5cclxuICAgICAgICByZXR1cm4gYmFzZTY0X2VuY29kZV9hYihidWZmZXIuc3ViYXJyYXkoMCwgaW5kZXgpKTtcclxuICAgIH1cclxuXHJcbiAgICBzcGF3bl9pZGVudGl0eV9oYW5kc2hha2VfaGFuZGxlcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIHJldHVybiBuZXcgVGVhU3BlYWtIYW5kc2hha2VIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtTZXR0aW5ncywgc2V0dGluZ3N9IGZyb20gXCIuLi8uLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge3VwZGF0ZV9mb3J1bX0gZnJvbSBcIi4vVGVhRm9ydW1JZGVudGl0eVwiO1xyXG5cclxuZGVjbGFyZSBpbnRlcmZhY2UgV2luZG93IHtcclxuICAgIGdyZWNhcHRjaGE6IEdSZUNhcHRjaGE7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgR1JlQ2FwdGNoYSB7XHJcbiAgICByZW5kZXIoY29udGFpbmVyOiBzdHJpbmcgfCBIVE1MRWxlbWVudCwgcGFyYW1ldGVyczoge1xyXG4gICAgICAgIHNpdGVrZXk6IHN0cmluZztcclxuICAgICAgICB0aGVtZT86IFwiZGFya1wiIHwgXCJsaWdodFwiO1xyXG4gICAgICAgIHNpemU/OiBcImNvbXBhY3RcIiB8IFwibm9ybWFsXCI7XHJcblxyXG4gICAgICAgIHRhYmluZGV4PzogbnVtYmVyO1xyXG5cclxuICAgICAgICBjYWxsYmFjaz86ICh0b2tlbjogc3RyaW5nKSA9PiBhbnk7XHJcbiAgICAgICAgXCJleHBpcmVkLWNhbGxiYWNrXCI/OiAoKSA9PiBhbnk7XHJcbiAgICAgICAgXCJlcnJvci1jYWxsYmFja1wiPzogKGVycm9yOiBhbnkpID0+IGFueTtcclxuICAgIH0pIDogc3RyaW5nOyAvKiB3aWRnZXRfaWQgKi9cclxuXHJcbiAgICByZXNldCh3aWRnZXRfaWQ/OiBzdHJpbmcpO1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIGZvcnVtIHtcclxuICAgIGV4cG9ydCBuYW1lc3BhY2UgZ2NhcHRjaGEge1xyXG4gICAgICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplKCkge1xyXG4gICAgICAgICAgICBpZih0eXBlb2YoKHdpbmRvdyBhcyBhbnkpLmdyZWNhcHRjaGEpID09PSBcInVuZGVmaW5lZFwiKSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgc2NyaXB0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKTtcclxuICAgICAgICAgICAgICAgIHNjcmlwdC5hc3luYyA9IHRydWU7XHJcblxyXG4gICAgICAgICAgICAgICAgbGV0IHRpbWVvdXQ7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBjYWxsYmFja19uYW1lID0gXCJjYXB0Y2hhX2NhbGxiYWNrX1wiICsgTWF0aC5yYW5kb20oKS50b1N0cmluZygpLnJlcGxhY2UoXCIuXCIsIFwiXCIpO1xyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcmlwdC5vbmVycm9yID0gcmVqZWN0O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3dbY2FsbGJhY2tfbmFtZV0gPSByZXNvbHZlO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHQuc3JjID0gXCJodHRwczovL3d3dy5nb29nbGUuY29tL3JlY2FwdGNoYS9hcGkuanM/b25sb2FkPVwiICsgZW5jb2RlVVJJQ29tcG9uZW50KGNhbGxiYWNrX25hbWUpICsgXCImcmVuZGVyPWV4cGxpY2l0XCI7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZChzY3JpcHQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0XCIpLCAxNTAwMCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgc2NyaXB0LnJlbW92ZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHNjcmlwdCA9IHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkZhaWxlZCB0byBmZXRjaCByZWNhcHRjaGEgamF2YXNjcmlwdCBzb3VyY2U6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gZG93bmxvYWQgc291cmNlXCIpO1xyXG4gICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcclxuICAgICAgICAgICAgICAgICAgICBpZihzY3JpcHQpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcmlwdC5vbmVycm9yID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB3aW5kb3dbY2FsbGJhY2tfbmFtZV07XHJcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZih0eXBlb2YoKHdpbmRvdyBhcyBhbnkpLmdyZWNhcHRjaGEpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gbG9hZCByZWNhcHRjaGFcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gc3Bhd24oY29udGFpbmVyOiBKUXVlcnksIGtleTogc3RyaW5nLCBjYWxsYmFja19kYXRhOiAodG9rZW46IHN0cmluZykgPT4gYW55KSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplKCk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBHLVJlY2FwdGNoYS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyB0cihcImluaXRpYWxpc2F0aW9uIGZhaWxlZFwiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihjb250YWluZXIuYXR0cihcImNhcHRjaGEtdXVpZFwiKSlcclxuICAgICAgICAgICAgICAgICh3aW5kb3cgYXMgYW55KS5ncmVjYXB0Y2hhLnJlc2V0KGNvbnRhaW5lci5hdHRyKFwiY2FwdGNoYS11dWlkXCIpKTtcclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBjb250YWluZXIuYXR0cihcImNhcHRjaGEtdXVpZFwiLCAod2luZG93IGFzIGFueSkuZ3JlY2FwdGNoYS5yZW5kZXIoY29udGFpbmVyWzBdLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgXCJzaXRla2V5XCI6IGtleSxcclxuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjazogY2FsbGJhY2tfZGF0YVxyXG4gICAgICAgICAgICAgICAgfSkpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGFwaV91cmwoKSB7XHJcbiAgICAgICAgcmV0dXJuIHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX1RFQUZPUk9fVVJMKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY2xhc3MgRGF0YSB7XHJcbiAgICAgICAgcmVhZG9ubHkgYXV0aF9rZXk6IHN0cmluZztcclxuICAgICAgICByZWFkb25seSByYXc6IHN0cmluZztcclxuICAgICAgICByZWFkb25seSBzaWduOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIHBhcnNlZDoge1xyXG4gICAgICAgICAgICB1c2VyX2lkOiBudW1iZXI7XHJcbiAgICAgICAgICAgIHVzZXJfbmFtZTogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgZGF0YV9hZ2U6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgIHVzZXJfZ3JvdXBfaWQ6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgIGlzX3N0YWZmOiBib29sZWFuO1xyXG4gICAgICAgICAgICB1c2VyX2dyb3VwczogbnVtYmVyW107XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgY29uc3RydWN0b3IoYXV0aDogc3RyaW5nLCByYXc6IHN0cmluZywgc2lnbjogc3RyaW5nKSB7XHJcbiAgICAgICAgICAgIHRoaXMuYXV0aF9rZXkgPSBhdXRoO1xyXG4gICAgICAgICAgICB0aGlzLnJhdyA9IHJhdztcclxuICAgICAgICAgICAgdGhpcy5zaWduID0gc2lnbjtcclxuXHJcbiAgICAgICAgICAgIHRoaXMucGFyc2VkID0gSlNPTi5wYXJzZShyYXcpO1xyXG4gICAgICAgIH1cclxuXHJcblxyXG4gICAgICAgIGRhdGFfanNvbigpIDogc3RyaW5nIHsgcmV0dXJuIHRoaXMucmF3OyB9XHJcbiAgICAgICAgZGF0YV9zaWduKCkgOiBzdHJpbmcgeyByZXR1cm4gdGhpcy5zaWduOyB9XHJcblxyXG4gICAgICAgIG5hbWUoKSA6IHN0cmluZyB7IHJldHVybiB0aGlzLnBhcnNlZC51c2VyX25hbWU7IH1cclxuXHJcbiAgICAgICAgdXNlcl9pZCgpIHsgcmV0dXJuIHRoaXMucGFyc2VkLnVzZXJfaWQ7IH1cclxuICAgICAgICB1c2VyX2dyb3VwKCkgeyByZXR1cm4gdGhpcy5wYXJzZWQudXNlcl9ncm91cF9pZDsgfVxyXG5cclxuICAgICAgICBpc19zdHVmZigpIDogYm9vbGVhbiB7IHJldHVybiB0aGlzLnBhcnNlZC5pc19zdGFmZjsgfVxyXG4gICAgICAgIGlzX3ByZW1pdW0oKSA6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5wYXJzZWQudXNlcl9ncm91cHMuaW5kZXhPZig1KSAhPSAtMTsgfVxyXG5cclxuICAgICAgICBkYXRhX2FnZSgpIDogRGF0ZSB7IHJldHVybiBuZXcgRGF0ZSh0aGlzLnBhcnNlZC5kYXRhX2FnZSk7IH1cclxuXHJcbiAgICAgICAgaXNfZXhwaXJlZCgpIDogYm9vbGVhbiB7IHJldHVybiB0aGlzLnBhcnNlZC5kYXRhX2FnZSArIDQ4ICogNjAgKiA2MCAqIDEwMDAgPCBEYXRlLm5vdygpOyB9XHJcbiAgICAgICAgc2hvdWxkX3JlbmV3KCkgOiBib29sZWFuIHsgcmV0dXJuIHRoaXMucGFyc2VkLmRhdGFfYWdlICsgMjQgKiA2MCAqIDYwICogMTAwMCA8IERhdGUubm93KCk7IH0gLyogcmVuZXcgZGF0YSBhbGwgMjRocnMgKi9cclxuICAgIH1cclxuICAgIGxldCBfZGF0YTogRGF0YSB8IHVuZGVmaW5lZDtcclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9nZ2VkX2luKCkgOiBib29sZWFuIHtcclxuICAgICAgICByZXR1cm4gISFfZGF0YSAmJiAhX2RhdGEuaXNfZXhwaXJlZCgpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBkYXRhKCkgOiBEYXRhIHsgcmV0dXJuIF9kYXRhOyB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBMb2dpblJlc3VsdCB7XHJcbiAgICAgICAgc3RhdHVzOiBcInN1Y2Nlc3NcIiB8IFwiY2FwdGNoYVwiIHwgXCJlcnJvclwiO1xyXG5cclxuICAgICAgICBlcnJvcl9tZXNzYWdlPzogc3RyaW5nO1xyXG4gICAgICAgIGNhcHRjaGE/OiB7XHJcbiAgICAgICAgICAgIHR5cGU6IFwiZ3JlLWNhcHRjaGFcIiB8IFwidW5rbm93blwiO1xyXG4gICAgICAgICAgICBkYXRhOiBhbnk7IC8qIGluIGNhc2Ugb2YgZ3JlLWNhcHRjaGEgaXQgd291bGQgYmUgdGhlIHNpZGUga2V5ICovXHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9naW4odXNlcm5hbWU6IHN0cmluZywgcGFzc3dvcmQ6IHN0cmluZywgY2FwdGNoYT86IGFueSkgOiBQcm9taXNlPExvZ2luUmVzdWx0PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlO1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAkLmFqYXgoe1xyXG4gICAgICAgICAgICAgICAgICAgIHVybDogYXBpX3VybCgpICsgXCI/d2ViLWFwaS92MS9sb2dpblwiLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGU6IFwiUE9TVFwiLFxyXG4gICAgICAgICAgICAgICAgICAgIGNhY2hlOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lOiB1c2VybmFtZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6IHBhc3N3b3JkLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW1lbWJlcjogdHJ1ZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJnLXJlY2FwdGNoYS1yZXNwb25zZVwiOiBjYXB0Y2hhXHJcbiAgICAgICAgICAgICAgICAgICAgfSxcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY3Jvc3NEb21haW46IHRydWUsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHJlc29sdmUsXHJcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6ICh4aHIsIHN0YXR1cywgZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2codHIoXCJMb2dpbiByZXF1ZXN0IGZhaWxlZCAlbzogJW9cIiksIHN0YXR1cywgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJyZXF1ZXN0IGZhaWxlZFwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiB0cihcImZhaWxlZCB0byBzZW5kIGxvZ2luIHJlcXVlc3RcIilcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKHJlc3BvbnNlW1wic3RhdHVzXCJdICE9PSBcIm9rXCIpIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIlJlc3BvbnNlIHN0YXR1cyBub3Qgb2tleS4gRXJyb3IgaGFwcGVuZDogJW9cIiksIHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgIHN0YXR1czogXCJlcnJvclwiLFxyXG4gICAgICAgICAgICAgICAgZXJyb3JfbWVzc2FnZTogKHJlc3BvbnNlW1wiZXJyb3JzXCJdIHx8IFtdKVswXSB8fCB0cihcIlVua25vd24gZXJyb3JcIilcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcInN1Y2Nlc3NcIl0pIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkxvZ2luIGZhaWxlZC4gUmVzcG9uc2UgJW9cIiksIHJlc3BvbnNlKTtcclxuXHJcbiAgICAgICAgICAgIGxldCBtZXNzYWdlID0gdHIoXCJmYWlsZWQgdG8gbG9naW5cIik7XHJcbiAgICAgICAgICAgIGxldCBjYXB0Y2hhO1xyXG4gICAgICAgICAgICAvKiB1c2VyL3Bhc3N3b3JkIHdyb25nIHwgYW5kIG1heWJlIGNhcHRjaGEgcmVxdWlyZWQgKi9cclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDEgfHwgcmVzcG9uc2VbXCJjb2RlXCJdID09IDMpXHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlID0gdHIoXCJJbnZhbGlkIHVzZXJuYW1lIG9yIHBhc3N3b3JkXCIpO1xyXG4gICAgICAgICAgICBpZihyZXNwb25zZVtcImNvZGVcIl0gPT0gMiB8fCByZXNwb25zZVtcImNvZGVcIl0gPT0gMykge1xyXG4gICAgICAgICAgICAgICAgY2FwdGNoYSA9IHtcclxuICAgICAgICAgICAgICAgICAgICB0eXBlOiByZXNwb25zZVtcImNhcHRjaGFcIl1bXCJ0eXBlXCJdLFxyXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHJlc3BvbnNlW1wiY2FwdGNoYVwiXVtcInNpdGVLZXlcIl0gLy9UT0RPOiBXaHkgc28gc3RhdGljIGhlcmU/XHJcbiAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDIpXHJcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IHRyKFwiY2FwdGNoYSByZXF1aXJlZFwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgIHN0YXR1czogdHlwZW9mKGNhcHRjaGEpICE9PSBcInVuZGVmaW5lZFwiID8gXCJjYXB0Y2hhXCIgOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiBtZXNzYWdlLFxyXG4gICAgICAgICAgICAgICAgY2FwdGNoYTogY2FwdGNoYVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgIH1cclxuICAgICAgICAvL2RvY3VtZW50LmNvb2tpZSA9IFwidXNlcl9kYXRhPVwiICsgcmVzcG9uc2VbXCJkYXRhXCJdICsgXCI7cGF0aD0vXCI7XHJcbiAgICAgICAgLy9kb2N1bWVudC5jb29raWUgPSBcInVzZXJfc2lnbj1cIiArIHJlc3BvbnNlW1wic2lnblwiXSArIFwiO3BhdGg9L1wiO1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKHJlc3BvbnNlW1wiYXV0aC1rZXlcIl0sIHJlc3BvbnNlW1wiZGF0YVwiXSwgcmVzcG9uc2VbXCJzaWduXCJdKTtcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1kYXRhXCIsIHJlc3BvbnNlW1wiZGF0YVwiXSk7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tc2lnblwiLCByZXNwb25zZVtcInNpZ25cIl0pO1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLWF1dGhcIiwgcmVzcG9uc2VbXCJhdXRoLWtleVwiXSk7XHJcbiAgICAgICAgICAgIHVwZGF0ZV9mb3J1bSgpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkZhaWxlZCB0byBwYXJzZSBmb3J1bSBnaXZlbiBkYXRhOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiB0cihcIkZhaWxlZCB0byBwYXJzZSByZXNwb25zZSBkYXRhXCIpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIHN0YXR1czogXCJzdWNjZXNzXCJcclxuICAgICAgICB9O1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5ld19kYXRhKCkgOiBQcm9taXNlPFwic3VjY2Vzc1wiIHwgXCJsb2dpbi1yZXF1aXJlZFwiPiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlO1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAkLmFqYXgoe1xyXG4gICAgICAgICAgICAgICAgICAgIHVybDogYXBpX3VybCgpICsgXCI/d2ViLWFwaS92MS9yZW5ldy1kYXRhXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogXCJHRVRcIixcclxuICAgICAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGNyb3NzRG9tYWluOiB0cnVlLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiYXV0aC1rZXlcIjogX2RhdGEuYXV0aF9rZXlcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiByZXNvbHZlLFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBzdGF0dXMsIGVycm9yKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiUmVuZXcgcmVxdWVzdCBmYWlsZWQgJW86ICVvXCIpLCBzdGF0dXMsIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KHRyKFwicmVxdWVzdCBmYWlsZWRcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gc2VuZCByZW5ldyByZXF1ZXN0XCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYocmVzcG9uc2VbXCJzdGF0dXNcIl0gIT09IFwib2tcIikge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiUmVzcG9uc2Ugc3RhdHVzIG5vdCBva2V5LiBFcnJvciBoYXBwZW5kOiAlb1wiKSwgcmVzcG9uc2UpO1xyXG4gICAgICAgICAgICB0aHJvdyAocmVzcG9uc2VbXCJlcnJvcnNcIl0gfHwgW10pWzBdIHx8IHRyKFwiVW5rbm93biBlcnJvclwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcInN1Y2Nlc3NcIl0pIHtcclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDEpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBcImxvZ2luLXJlcXVpcmVkXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIGVycm9yIGNvZGUgKFwiICsgcmVzcG9uc2VbXCJjb2RlXCJdICsgXCIpXCI7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcImRhdGFcIl0gfHwgIXJlc3BvbnNlW1wic2lnblwiXSlcclxuICAgICAgICAgICAgdGhyb3cgdHIoXCJyZXNwb25zZSBtaXNzaW5nIGRhdGFcIik7XHJcblxyXG4gICAgICAgIGNvbnNvbGUuZGVidWcodHIoXCJSZW5ldyBzdWNjZWVkZWQuIFBhcnNpbmcgZGF0YS5cIikpO1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKF9kYXRhLmF1dGhfa2V5LCByZXNwb25zZVtcImRhdGFcIl0sIHJlc3BvbnNlW1wic2lnblwiXSk7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tZGF0YVwiLCByZXNwb25zZVtcImRhdGFcIl0pO1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIiwgcmVzcG9uc2VbXCJzaWduXCJdKTtcclxuICAgICAgICAgICAgdXBkYXRlX2ZvcnVtKCk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIHBhcnNlIGZvcnVtIGdpdmVuIGRhdGE6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHRocm93IHRyKFwiZmFpbGVkIHRvIHBhcnNlIGRhdGFcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gXCJzdWNjZXNzXCI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvZ291dCgpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgaWYoIWxvZ2dlZF9pbigpKVxyXG4gICAgICAgICAgICByZXR1cm47XHJcblxyXG4gICAgICAgIGxldCByZXNwb25zZTtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZSA9IGF3YWl0IG5ldyBQcm9taXNlPGFueT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgJC5hamF4KHtcclxuICAgICAgICAgICAgICAgICAgICB1cmw6IGFwaV91cmwoKSArIFwiP3dlYi1hcGkvdjEvbG9nb3V0XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogXCJHRVRcIixcclxuICAgICAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGNyb3NzRG9tYWluOiB0cnVlLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiYXV0aC1rZXlcIjogX2RhdGEuYXV0aF9rZXlcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiByZXNvbHZlLFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBzdGF0dXMsIGVycm9yKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiTG9nb3V0IHJlcXVlc3QgZmFpbGVkICVvOiAlb1wiKSwgc3RhdHVzLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdCh0cihcInJlcXVlc3QgZmFpbGVkXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KVxyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHRocm93IHRyKFwiZmFpbGVkIHRvIHNlbmQgbG9nb3V0IHJlcXVlc3RcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihyZXNwb25zZVtcInN0YXR1c1wiXSAhPT0gXCJva1wiKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJSZXNwb25zZSBzdGF0dXMgbm90IG9rZXkuIEVycm9yIGhhcHBlbmQ6ICVvXCIpLCByZXNwb25zZSk7XHJcbiAgICAgICAgICAgIHRocm93IChyZXNwb25zZVtcImVycm9yc1wiXSB8fCBbXSlbMF0gfHwgdHIoXCJVbmtub3duIGVycm9yXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoIXJlc3BvbnNlW1wic3VjY2Vzc1wiXSkge1xyXG4gICAgICAgICAgICAvKiBjb2RlIDEgbWVhbnMgbm90IGxvZ2dlZCBpbiwgaXRzIGFuIHN1Y2Nlc3MgKi9cclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdICE9IDEpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IFwiaW52YWxpZCBlcnJvciBjb2RlIChcIiArIHJlc3BvbnNlW1wiY29kZVwiXSArIFwiKVwiO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBfZGF0YSA9IHVuZGVmaW5lZDtcclxuICAgICAgICBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbShcInRlYXNwZWFrLWZvcnVtLWRhdGFcIik7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1zaWduXCIpO1xyXG4gICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFwidGVhc3BlYWstZm9ydW0tYXV0aFwiKTtcclxuICAgICAgICB1cGRhdGVfZm9ydW0oKTtcclxuICAgIH1cclxuXHJcbiAgICBsb2FkZXIucmVnaXN0ZXJfdGFzayhsb2FkZXIuU3RhZ2UuSkFWQVNDUklQVF9JTklUSUFMSVpJTkcsIHtcclxuICAgICAgICBuYW1lOiBcIlRlYUZvcm8gaW5pdGlhbGl6ZVwiLFxyXG4gICAgICAgIHByaW9yaXR5OiAxMCxcclxuICAgICAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCByYXdfZGF0YSA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tZGF0YVwiKTtcclxuICAgICAgICAgICAgY29uc3QgcmF3X3NpZ24gPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGZvcnVtX2F1dGggPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLWF1dGhcIik7XHJcbiAgICAgICAgICAgIGlmKCFyYXdfZGF0YSB8fCAhcmF3X3NpZ24gfHwgIWZvcnVtX2F1dGgpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiTm8gVGVhRm9ybyBhdXRoZW50aWZpY2F0aW9uIGZvdW5kLiBUZWFGb3JvIGNvbm5lY3Rpb24gc3RhdHVzOiB1bmNvbm5lY3RlZFwiKSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKGZvcnVtX2F1dGgsIHJhd19kYXRhLCByYXdfc2lnbik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBUZWFGb3JvIGNvbm5lY3Rpb24gZnJvbSBsb2NhbCBkYXRhLiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihfZGF0YS5zaG91bGRfcmVuZXcoKSkge1xyXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKHRyKFwiVGVhRm9ybyBkYXRhIHNob3VsZCBiZSByZW5ld2VkLiBFeGVjdXRpbmcgcmVuZXcuXCIpKTtcclxuICAgICAgICAgICAgICAgIHJlbmV3X2RhdGEoKS50aGVuKHN0YXR1cyA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoc3RhdHVzID09PSBcInN1Y2Nlc3NcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8odHIoXCJUZWFGb3JvIGRhdGEgaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHJlbmV3ZWQuXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJGYWlsZWQgdG8gcmVuZXcgVGVhRm9ybyBkYXRhLiBOZXcgbG9naW4gcmVxdWlyZWQuXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1kYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFwidGVhc3BlYWstZm9ydW0tYXV0aFwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiRmFpbGVkIHRvIHJlbmV3IFRlYUZvcm8gZGF0YS4gQW4gZXJyb3Igb2NjdXJyZWQ6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYoX2RhdGEgJiYgX2RhdGEuaXNfZXhwaXJlZCgpKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiVGVhRm9ybyBkYXRhIGlzIGV4cGlyZWQuIFRlYUZvcm8gY29ubmVjdGlvbiBpc24ndCBhdmFpbGFibGUhXCIpKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH0pXHJcbn0iLCIvLy8gPHJlZmVyZW5jZSBwYXRoPVwidWkvZWxlbWVudHMvbW9kYWwudHNcIiAvPlxyXG4vL1VzZWQgYnkgQ2VydEFjY2VwdCBwb3B1cFxyXG5cclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi9sb2dcIjtcclxuaW1wb3J0IHtjcmVhdGVFcnJvck1vZGFsfSBmcm9tIFwiLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5cclxuaWYodHlwZW9mKGN1c3RvbUVsZW1lbnRzKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgdHJ5IHtcclxuICAgICAgICBjbGFzcyBYX1Byb3BlcnRpZXMgZXh0ZW5kcyBIVE1MRWxlbWVudCB7fVxyXG4gICAgICAgIGNsYXNzIFhfUHJvcGVydHkgZXh0ZW5kcyBIVE1MRWxlbWVudCB7fVxyXG5cclxuICAgICAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3gtcHJvcGVydGllcycsIFhfUHJvcGVydGllcywgeyBleHRlbmRzOiAnZGl2JyB9KTtcclxuICAgICAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3gtcHJvcGVydHknLCBYX1Byb3BlcnR5LCB7IGV4dGVuZHM6ICdkaXYnIH0pO1xyXG4gICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgIGNvbnNvbGUud2FybihcImZhaWxlZCB0byBkZWZpbmUgY29zdHVtIGVsZW1lbnRzXCIpO1xyXG4gICAgfVxyXG59XHJcblxyXG4vKiBUID0gdmFsdWUgdHlwZSAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIFNldHRpbmdzS2V5PFQ+IHtcclxuICAgIGtleTogc3RyaW5nO1xyXG5cclxuICAgIGZhbGxiYWNrX2tleXM/OiBzdHJpbmcgfCBzdHJpbmdbXTtcclxuICAgIGZhbGxiYWNrX2ltcG9ydHM/OiB7W2tleTogc3RyaW5nXToodmFsdWU6IHN0cmluZykgPT4gVH07XHJcbiAgICBkZXNjcmlwdGlvbj86IHN0cmluZztcclxuICAgIGRlZmF1bHRfdmFsdWU/OiBUO1xyXG5cclxuICAgIHJlcXVpcmVfcmVzdGFydD86IGJvb2xlYW47XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTZXR0aW5nc0Jhc2Uge1xyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyByZWFkb25seSBVUERBVEVfRElSRUNUOiBib29sZWFuID0gdHJ1ZTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgc3RhdGljIHRyYW5zZm9ybVN0Tz88VD4oaW5wdXQ/OiBzdHJpbmcsIF9kZWZhdWx0PzogVCwgZGVmYXVsdF90eXBlPzogc3RyaW5nKSA6IFQge1xyXG4gICAgICAgIGRlZmF1bHRfdHlwZSA9IGRlZmF1bHRfdHlwZSB8fCB0eXBlb2YgX2RlZmF1bHQ7XHJcblxyXG4gICAgICAgIGlmICAgICAgKHR5cGVvZiBpbnB1dCA9PT0gXCJ1bmRlZmluZWRcIikgcmV0dXJuIF9kZWZhdWx0O1xyXG4gICAgICAgIGlmICAgICAgKGRlZmF1bHRfdHlwZSA9PT0gXCJzdHJpbmdcIikgICAgIHJldHVybiBpbnB1dCBhcyBhbnk7XHJcbiAgICAgICAgZWxzZSBpZiAoZGVmYXVsdF90eXBlID09PSBcIm51bWJlclwiKSAgICAgcmV0dXJuIHBhcnNlSW50KGlucHV0KSBhcyBhbnk7XHJcbiAgICAgICAgZWxzZSBpZiAoZGVmYXVsdF90eXBlID09PSBcImJvb2xlYW5cIikgICAgcmV0dXJuIChpbnB1dCA9PSBcIjFcIiB8fCBpbnB1dCA9PSBcInRydWVcIikgYXMgYW55O1xyXG4gICAgICAgIGVsc2UgaWYgKGRlZmF1bHRfdHlwZSA9PT0gXCJ1bmRlZmluZWRcIikgICByZXR1cm4gaW5wdXQgYXMgYW55O1xyXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKGlucHV0KSBhcyBhbnk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyB0cmFuc2Zvcm1PdFM/PFQ+KGlucHV0OiBUKSA6IHN0cmluZyB7XHJcbiAgICAgICAgaWYgICAgICAodHlwZW9mIGlucHV0ID09PSBcInN0cmluZ1wiKSAgICAgcmV0dXJuIGlucHV0IGFzIHN0cmluZztcclxuICAgICAgICBlbHNlIGlmICh0eXBlb2YgaW5wdXQgPT09IFwibnVtYmVyXCIpICAgICByZXR1cm4gaW5wdXQudG9TdHJpbmcoKTtcclxuICAgICAgICBlbHNlIGlmICh0eXBlb2YgaW5wdXQgPT09IFwiYm9vbGVhblwiKSAgICByZXR1cm4gaW5wdXQgPyBcIjFcIiA6IFwiMFwiO1xyXG4gICAgICAgIGVsc2UgaWYgKHR5cGVvZiBpbnB1dCA9PT0gXCJ1bmRlZmluZWRcIikgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGlucHV0KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm90ZWN0ZWQgc3RhdGljIHJlc29sdmVLZXk8VD4oa2V5OiBTZXR0aW5nc0tleTxUPiwgX2RlZmF1bHQ6IFQsIHJlc29sdmVyOiAoa2V5OiBzdHJpbmcpID0+IHN0cmluZyB8IGJvb2xlYW4sIGRlZmF1bHRfdHlwZT86IHN0cmluZykgOiBUIHtcclxuICAgICAgICBsZXQgdmFsdWUgPSByZXNvbHZlcihrZXkua2V5KTtcclxuICAgICAgICBpZighdmFsdWUpIHtcclxuICAgICAgICAgICAgLyogdHJ5aW5nIGZhbGxiYWNrcyAqL1xyXG4gICAgICAgICAgICBmb3IoY29uc3QgZmFsbGJhY2sgb2Yga2V5LmZhbGxiYWNrX2tleXMgfHwgW10pIHtcclxuICAgICAgICAgICAgICAgIHZhbHVlID0gcmVzb2x2ZXIoZmFsbGJhY2spO1xyXG4gICAgICAgICAgICAgICAgaWYodHlwZW9mKHZhbHVlKSA9PT0gXCJzdHJpbmdcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIC8qIGZhbGxiYWNrIGtleSBzdWNjZWVkZWQgKi9cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBpbXBvcnRlciA9IChrZXkuZmFsbGJhY2tfaW1wb3J0cyB8fCB7fSlbZmFsbGJhY2tdO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKGltcG9ydGVyKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaW1wb3J0ZXIodmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKHR5cGVvZih2YWx1ZSkgIT09ICdzdHJpbmcnKVxyXG4gICAgICAgICAgICByZXR1cm4gX2RlZmF1bHQ7XHJcblxyXG4gICAgICAgIHJldHVybiBTZXR0aW5nc0Jhc2UudHJhbnNmb3JtU3RPKHZhbHVlIGFzIHN0cmluZywgX2RlZmF1bHQsIGRlZmF1bHRfdHlwZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyBrZXlpZnk8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPikgOiBTZXR0aW5nc0tleTxUPiB7XHJcbiAgICAgICAgaWYodHlwZW9mKGtleSkgPT09IFwic3RyaW5nXCIpXHJcbiAgICAgICAgICAgIHJldHVybiB7a2V5OiBrZXl9O1xyXG4gICAgICAgIGlmKHR5cGVvZihrZXkpID09PSBcIm9iamVjdFwiICYmIGtleS5rZXkpXHJcbiAgICAgICAgICAgIHJldHVybiBrZXk7XHJcbiAgICAgICAgdGhyb3cgXCJrZXkgaXMgbm90IGEga2V5XCI7XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTdGF0aWNTZXR0aW5ncyBleHRlbmRzIFNldHRpbmdzQmFzZSB7XHJcbiAgICBwcml2YXRlIHN0YXRpYyBfaW5zdGFuY2U6IFN0YXRpY1NldHRpbmdzO1xyXG4gICAgc3RhdGljIGdldCBpbnN0YW5jZSgpIDogU3RhdGljU2V0dGluZ3Mge1xyXG4gICAgICAgIGlmKCF0aGlzLl9pbnN0YW5jZSlcclxuICAgICAgICAgICAgdGhpcy5faW5zdGFuY2UgPSBuZXcgU3RhdGljU2V0dGluZ3ModHJ1ZSk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2luc3RhbmNlO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBfaGFuZGxlOiBTdGF0aWNTZXR0aW5ncztcclxuICAgIHByb3RlY3RlZCBfc3RhdGljUHJvcHNUYWc6IEpRdWVyeTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoX3Jlc2VydmVkID0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICBpZihfcmVzZXJ2ZWQgJiYgIVN0YXRpY1NldHRpbmdzLl9pbnN0YW5jZSkge1xyXG4gICAgICAgICAgICB0aGlzLl9zdGF0aWNQcm9wc1RhZyA9ICQoXCIjcHJvcGVydGllc1wiKTtcclxuICAgICAgICAgICAgdGhpcy5pbml0aWFsaXplU3RhdGljKCk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5faGFuZGxlID0gU3RhdGljU2V0dGluZ3MuaW5zdGFuY2U7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgaW5pdGlhbGl6ZVN0YXRpYygpIHtcclxuICAgICAgICBsZXQgc2VhcmNoO1xyXG4gICAgICAgIGlmKHdpbmRvdy5vcGVuZXIgJiYgd2luZG93Lm9wZW5lciAhPT0gd2luZG93KSB7XHJcbiAgICAgICAgICAgIHNlYXJjaCA9IG5ldyBVUkwod2luZG93LmxvY2F0aW9uLmhyZWYpLnNlYXJjaDtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBzZWFyY2ggPSBsb2NhdGlvbi5zZWFyY2g7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzZWFyY2guc3Vic3RyKDEpLnNwbGl0KFwiJlwiKS5mb3JFYWNoKHBhcnQgPT4ge1xyXG4gICAgICAgICAgICBsZXQgaXRlbSA9IHBhcnQuc3BsaXQoXCI9XCIpO1xyXG4gICAgICAgICAgICAkKFwiPHgtcHJvcGVydHk+PC94LXByb3BlcnR5PlwiKVxyXG4gICAgICAgICAgICAgICAgLmF0dHIoXCJrZXlcIiwgaXRlbVswXSlcclxuICAgICAgICAgICAgICAgIC5hdHRyKFwidmFsdWVcIiwgaXRlbVsxXSlcclxuICAgICAgICAgICAgICAgIC5hcHBlbmRUbyh0aGlzLl9zdGF0aWNQcm9wc1RhZyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljPzxUPihrZXk6IHN0cmluZyB8IFNldHRpbmdzS2V5PFQ+LCBfZGVmYXVsdD86IFQsIGRlZmF1bHRfdHlwZT86IHN0cmluZykgOiBUIHtcclxuICAgICAgICBpZih0aGlzLl9oYW5kbGUpIHJldHVybiB0aGlzLl9oYW5kbGUuc3RhdGljPFQ+KGtleSwgX2RlZmF1bHQsIGRlZmF1bHRfdHlwZSk7XHJcblxyXG4gICAgICAgIGtleSA9IFN0YXRpY1NldHRpbmdzLmtleWlmeShrZXkpO1xyXG4gICAgICAgIHJldHVybiBTdGF0aWNTZXR0aW5ncy5yZXNvbHZlS2V5KGtleSwgX2RlZmF1bHQsIGtleSA9PiB7XHJcbiAgICAgICAgICAgIGxldCByZXN1bHQgPSB0aGlzLl9zdGF0aWNQcm9wc1RhZy5maW5kKFwiW2tleT0nXCIgKyBrZXkgKyBcIiddXCIpO1xyXG4gICAgICAgICAgICBpZihyZXN1bHQubGVuZ3RoID4gMClcclxuICAgICAgICAgICAgICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQocmVzdWx0Lmxhc3QoKS5hdHRyKCd2YWx1ZScpKTtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH0sIGRlZmF1bHRfdHlwZSk7XHJcbiAgICB9XHJcblxyXG4gICAgZGVsZXRlU3RhdGljPFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4pIHtcclxuICAgICAgICBpZih0aGlzLl9oYW5kbGUpIHtcclxuICAgICAgICAgICAgdGhpcy5faGFuZGxlLmRlbGV0ZVN0YXRpYzxUPihrZXkpO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBrZXkgPSBTdGF0aWNTZXR0aW5ncy5rZXlpZnkoa2V5KTtcclxuICAgICAgICBsZXQgcmVzdWx0ID0gdGhpcy5fc3RhdGljUHJvcHNUYWcuZmluZChcIltrZXk9J1wiICsga2V5LmtleSArIFwiJ11cIik7XHJcbiAgICAgICAgaWYocmVzdWx0Lmxlbmd0aCAhPSAwKSByZXN1bHQuZGV0YWNoKCk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTZXR0aW5ncyBleHRlbmRzIFN0YXRpY1NldHRpbmdzIHtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfVVNFUl9JU19ORVc6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ3VzZXJfaXNfbmV3X3VzZXInLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWVcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX0NPU01FVElDX1NMT1dET1dOOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlX2Nvc21ldGljX3Nsb3dkb3duJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0Rpc2FibGUgdGhlIGNvc21ldGljIHNsb3dkb3dzIGluIHNvbWUgcHJvY2Vzc2VzLCBsaWtlIGljb24gdXBsb2FkLidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX0NPTlRFWFRfTUVOVTogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnZGlzYWJsZUNvbnRleHRNZW51JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0Rpc2FibGUgdGhlIGNvbnRleHQgbWVudSBmb3IgdGhlIGNoYW5uZWwgdHJlZSB3aGljaCBhbGxvd3MgdG8gZGVidWcgdGhlIERPTSBlYXNpZXInXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfRElTQUJMRV9HTE9CQUxfQ09OVEVYVF9NRU5VOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlR2xvYmFsQ29udGV4dE1lbnUnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZSB0aGUgZ2VuZXJhbCBjb250ZXh0IG1lbnUgcHJldmVudGlvbicsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2VcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX1VOTE9BRF9ESUFMT0c6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2Rpc2FibGVVbmxvYWREaWFsb2cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZXMgdGhlIHVubG9hZCBwb3B1cCBvbiBzaWRlIGNsb3NpbmcnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX1ZPSUNFOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlVm9pY2UnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZXMgdGhlIHZvaWNlIGJyaWRnZS4gSWYgZGlzYWJsZWQsIHRoZSBhdWRpbyBhbmQgY29kZWMgd29ya2VycyBhcmVuXFwndCByZXF1aXJlZCBhbnltb3JlJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfRElTQUJMRV9NVUxUSV9TRVNTSU9OOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlTXVsdGlTZXNzaW9uJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZSxcclxuICAgICAgICByZXF1aXJlX3Jlc3RhcnQ6IHRydWVcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9MT0FEX0RVTU1ZX0VSUk9SOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkdW1teV9sb2FkX2Vycm9yJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RyaWdnZXJzIGEgbG9hZGluZyBlcnJvciBhdCB0aGUgZW5kIG9mIHRoZSBsb2FkaW5nIHByb2Nlc3MuJ1xyXG4gICAgfTtcclxuXHJcbiAgICAvKiBDb250cm9sIGJhciAqL1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05UUk9MX01VVEVfSU5QVVQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ211dGVfaW5wdXQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05UUk9MX01VVEVfT1VUUFVUOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdtdXRlX291dHB1dCdcclxuICAgIH07XHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NPTlRST0xfU0hPV19RVUVSSUVTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdzaG93X3NlcnZlcl9xdWVyaWVzJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OVFJPTF9DSEFOTkVMX1NVQlNDUklCRV9BTEw6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2NoYW5uZWxfc3Vic2NyaWJlX2FsbCdcclxuICAgIH07XHJcblxyXG4gICAgLyogQ29ubmVjdCBwYXJhbWV0ZXJzICovXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0ZMQUdfQ09OTkVDVF9ERUZBVUxUOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjb25uZWN0X2RlZmF1bHQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX0FERFJFU1M6IFNldHRpbmdzS2V5PHN0cmluZz4gPSB7XHJcbiAgICAgICAga2V5OiAnY29ubmVjdF9hZGRyZXNzJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OTkVDVF9QUk9GSUxFOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcHJvZmlsZScsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogJ2RlZmF1bHQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX1VTRVJOQU1FOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfdXNlcm5hbWUnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX1BBU1NXT1JEOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcGFzc3dvcmQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9GTEFHX0NPTk5FQ1RfUEFTU1dPUkQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcGFzc3dvcmRfaGFzaGVkJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OTkVDVF9ISVNUT1JZOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfaGlzdG9yeSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX05PX0ROU1BST1hZOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjb25uZWN0X25vX2Ruc3Byb3h5JyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZVxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NFUlRJRklDQVRFX0NBTExCQUNLOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2NlcnRpZmljYXRlX2NhbGxiYWNrJ1xyXG4gICAgfTtcclxuXHJcbiAgICAvKiBzb3VuZHMgKi9cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU09VTkRfTUFTVEVSOiBTZXR0aW5nc0tleTxudW1iZXI+ID0ge1xyXG4gICAgICAgIGtleTogJ2F1ZGlvX21hc3Rlcl92b2x1bWUnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IDEwMFxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX1NPVU5EX01BU1RFUl9TT1VORFM6IFNldHRpbmdzS2V5PG51bWJlcj4gPSB7XHJcbiAgICAgICAga2V5OiAnYXVkaW9fbWFzdGVyX3ZvbHVtZV9zb3VuZHMnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IDEwMFxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NIQVRfRklYRURfVElNRVNUQU1QUzogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9maXhlZF90aW1lc3RhbXBzJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgZml4ZWQgdGltZXN0YW1wcyBmb3IgY2hhdCBtZXNzYWdlcyBhbmQgZGlzYWJsZWQgdGhlIHVwZGF0aW5nIG9uY2UgKDIgc2Vjb25kcyBhZ28uLi4gZXRjKSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0NPTExPUVVJQUxfVElNRVNUQU1QUzogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9jb2xsb3F1aWFsX3RpbWVzdGFtcHMnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVkIGNvbGxvcXVpYWwgdGltZXN0YW1wIGZvcm1hdHRpbmcgbGlrZSBcIlllc3RlcmRheSBhdCAuLi5cIiBvciBcIlRvZGF5IGF0IC4uLlwiJ1xyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NIQVRfQ09MT1JFRF9FTU9KSUVTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2NvbG9yZWRfZW1vamllcycsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogdHJ1ZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgY29sb3JlZCBlbW9qaWVzIHBvd2VyZWQgYnkgVHdlbW9qaSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX1RBR19VUkxTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X3RhZ191cmxzJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiB0cnVlLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQXV0b21hdGljYWxseSBsaW5rIHVybHMgd2l0aCBbdXJsXSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0VOQUJMRV9NQVJLRE9XTjogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9lbmFibGVfbWFya2Rvd24nLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVkIG1hcmtkb3duIGNoYXQgc3VwcG9ydC4nXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ0hBVF9FTkFCTEVfQkJDT0RFOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2VuYWJsZV9iYmNvZGUnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlZCBiYmNvZGUgc3VwcG9ydCBpbiBjaGF0LidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0lNQUdFX1dISVRFTElTVF9SRUdFWDogU2V0dGluZ3NLZXk8c3RyaW5nPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2ltYWdlX3doaXRlbGlzdF9yZWdleCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogSlNPTi5zdHJpbmdpZnkoW10pXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU1dJVENIX0lOU1RBTlRfQ0hBVDogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnc3dpdGNoX2luc3RhbnRfY2hhdCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogdHJ1ZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdGx5IHN3aXRjaCB0byBjaGFubmVsIGNoYXQgb24gY2hhbm5lbCBzZWxlY3QnXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU1dJVENIX0lOU1RBTlRfQ0xJRU5UOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdzd2l0Y2hfaW5zdGFudF9jbGllbnQnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RseSBzd2l0Y2ggdG8gY2xpZW50IGluZm8gb24gY2xpZW50IHNlbGVjdCdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9IT1NUQkFOTkVSX0JBQ0tHUk9VTkQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2hvc3RiYW5uZXJfYmFja2dyb3VuZCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIGEgZGVmYXVsdCBiYWNrZ3JvdW5kIGJlZ2luZCB0aGUgaG9zdGJhbm5lcidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFOTkVMX0VESVRfQURWQU5DRUQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2NoYW5uZWxfZWRpdF9hZHZhbmNlZCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFZGl0IGNoYW5uZWxzIGluIGFkdmFuY2VkIG1vZGUgd2l0aCBhIGxvdCBtb3JlIHNldHRpbmdzJ1xyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX1BFUk1JU1NJT05TX1NIT1dfQUxMOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdwZXJtaXNzaW9uc19zaG93X2FsbCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTaG93IGFsbCBwZXJtaXNzaW9ucyBldmVuIHRob3UgdGhleSBkb250IG1ha2Ugc2Vuc2UgZm9yIHRoZSBzZXJ2ZXIvY2hhbm5lbCBncm91cCdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9URUFGT1JPX1VSTDogU2V0dGluZ3NLZXk8c3RyaW5nPiA9IHtcclxuICAgICAgICBrZXk6IFwidGVhZm9yb191cmxcIixcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBcImh0dHBzOi8vZm9ydW0udGVhc3BlYWsuZGUvXCJcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9GT05UX1NJWkU6IFNldHRpbmdzS2V5PG51bWJlcj4gPSB7XHJcbiAgICAgICAga2V5OiBcImZvbnRfc2l6ZVwiXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfSUNPTl9TSVpFOiBTZXR0aW5nc0tleTxudW1iZXI+ID0ge1xyXG4gICAgICAgIGtleTogXCJpY29uX3NpemVcIixcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiAxMDBcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9MQVNUX0lOVklURV9MSU5LX1RZUEU6IFNldHRpbmdzS2V5PHN0cmluZz4gPSB7XHJcbiAgICAgICAga2V5OiBcImxhc3RfaW52aXRlX2xpbmtfdHlwZVwiLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IFwidGVhLXdlYlwiXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBGTl9JTlZJVEVfTElOS19TRVRUSU5HOiAobmFtZTogc3RyaW5nKSA9PiBTZXR0aW5nc0tleTxzdHJpbmc+ID0gbmFtZSA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAnaW52aXRlX2xpbmtfc2V0dGluZ18nICsgbmFtZVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEZOX1NFUlZFUl9DSEFOTkVMX1NVQlNDUklCRV9NT0RFOiAoY2hhbm5lbF9pZDogbnVtYmVyKSA9PiBTZXR0aW5nc0tleTxudW1iZXI+ID0gY2hhbm5lbCA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAnY2hhbm5lbF9zdWJzY3JpYmVfbW9kZV8nICsgY2hhbm5lbFxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEZOX1BST0ZJTEVfUkVDT1JEOiAobmFtZTogc3RyaW5nKSA9PiBTZXR0aW5nc0tleTxhbnk+ID0gbmFtZSA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAncHJvZmlsZV9yZWNvcmQnICsgbmFtZVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWVMgPSAoKCkgPT4ge1xyXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IFtdO1xyXG5cclxuICAgICAgICBmb3IoY29uc3Qga2V5IGluIFNldHRpbmdzKSB7XHJcbiAgICAgICAgICAgIGlmKCFrZXkudG9VcHBlckNhc2UoKS5zdGFydHNXaXRoKFwiS0VZX1wiKSlcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICBpZihrZXkudG9VcHBlckNhc2UoKSA9PSBcIktFWVNcIilcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgcmVzdWx0LnB1c2goa2V5KTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIHN0YXRpYyBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIHNldHRpbmdzID0gbmV3IFNldHRpbmdzKCk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBjYWNoZUdsb2JhbCA9IHt9O1xyXG4gICAgcHJpdmF0ZSBzYXZlV29ya2VyOiBOb2RlSlMuVGltZXI7XHJcbiAgICBwcml2YXRlIHVwZGF0ZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICBzdXBlcigpO1xyXG5cclxuICAgICAgICBjb25zdCBqc29uID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJzZXR0aW5ncy5nbG9iYWxcIik7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgdGhpcy5jYWNoZUdsb2JhbCA9IEpTT04ucGFyc2UoanNvbik7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBnbG9iYWwgc2V0dGluZ3MhXFxuSnNvbjogJXNcXG5FcnJvcjogJW9cIiksIGpzb24sIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHNob3dfcG9wdXAgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBjcmVhdGVFcnJvck1vZGFsKHRyKFwiRmFpbGVkIHRvIGxvYWQgZ2xvYmFsIHNldHRpbmdzXCIpLCB0cihcIkZhaWxlZCB0byBsb2FkIGdsb2JhbCBjbGllbnQgc2V0dGluZ3MhXFxuTG9va3VwIGNvbnNvbGUgZm9yIG1vcmUgaW5mb3JtYXRpb24uXCIpKS5vcGVuKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIGlmKCFsb2FkZXIuZmluaXNoZWQoKSlcclxuICAgICAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHtcclxuICAgICAgICAgICAgICAgICAgICBwcmlvcml0eTogMCxcclxuICAgICAgICAgICAgICAgICAgICBuYW1lOiBcIlNldHRpbmdzIGVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHNob3dfcG9wdXAoKVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgIHNob3dfcG9wdXAoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYoIXRoaXMuY2FjaGVHbG9iYWwpIHRoaXMuY2FjaGVHbG9iYWwgPSB7fTtcclxuICAgICAgICB0aGlzLnNhdmVXb3JrZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICAgICAgICAgIGlmKHRoaXMudXBkYXRlZClcclxuICAgICAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgIH0sIDUgKiAxMDAwKTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWNfZ2xvYmFsPzxUPihrZXk6IHN0cmluZyB8IFNldHRpbmdzS2V5PFQ+LCBfZGVmYXVsdD86IFQpIDogVCB7XHJcbiAgICAgICAgY29uc3QgYWN0dWFsX2RlZmF1bHQgPSB0eXBlb2YoX2RlZmF1bHQpID09PSBcInVuZGVmaW5lZFwiICYmIHR5cGVvZihrZXkpID09PSBcIm9iamVjdFwiICYmICdkZWZhdWx0X3ZhbHVlJyBpbiBrZXkgPyBrZXkuZGVmYXVsdF92YWx1ZSA6IF9kZWZhdWx0O1xyXG5cclxuICAgICAgICBjb25zdCBkZWZhdWx0X29iamVjdCA9IHsgc2VlZDogTWF0aC5yYW5kb20oKSB9IGFzIGFueTtcclxuICAgICAgICBsZXQgX3N0YXRpYyA9IHRoaXMuc3RhdGljKGtleSwgZGVmYXVsdF9vYmplY3QsIHR5cGVvZiBfZGVmYXVsdCk7XHJcbiAgICAgICAgaWYoX3N0YXRpYyAhPT0gZGVmYXVsdF9vYmplY3QpIHJldHVybiBTdGF0aWNTZXR0aW5ncy50cmFuc2Zvcm1TdE8oX3N0YXRpYywgYWN0dWFsX2RlZmF1bHQpO1xyXG4gICAgICAgIHJldHVybiB0aGlzLmdsb2JhbDxUPihrZXksIGFjdHVhbF9kZWZhdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICBnbG9iYWw/PFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4sIF9kZWZhdWx0PzogVCkgOiBUIHtcclxuICAgICAgICBjb25zdCBhY3R1YWxfZGVmYXVsdCA9IHR5cGVvZihfZGVmYXVsdCkgPT09IFwidW5kZWZpbmVkXCIgJiYgdHlwZW9mKGtleSkgPT09IFwib2JqZWN0XCIgJiYgJ2RlZmF1bHRfdmFsdWUnIGluIGtleSA/IGtleS5kZWZhdWx0X3ZhbHVlIDogX2RlZmF1bHQ7XHJcbiAgICAgICAgcmV0dXJuIFN0YXRpY1NldHRpbmdzLnJlc29sdmVLZXkoU2V0dGluZ3Mua2V5aWZ5KGtleSksIGFjdHVhbF9kZWZhdWx0LCBrZXkgPT4gdGhpcy5jYWNoZUdsb2JhbFtrZXldKTtcclxuICAgIH1cclxuXHJcbiAgICBjaGFuZ2VHbG9iYWw8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPiwgdmFsdWU/OiBUKXtcclxuICAgICAgICBrZXkgPSBTZXR0aW5ncy5rZXlpZnkoa2V5KTtcclxuXHJcblxyXG4gICAgICAgIGlmKHRoaXMuY2FjaGVHbG9iYWxba2V5LmtleV0gPT0gdmFsdWUpIHJldHVybjtcclxuXHJcbiAgICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcclxuICAgICAgICB0aGlzLmNhY2hlR2xvYmFsW2tleS5rZXldID0gU3RhdGljU2V0dGluZ3MudHJhbnNmb3JtT3RTKHZhbHVlKTtcclxuXHJcbiAgICAgICAgaWYoU2V0dGluZ3MuVVBEQVRFX0RJUkVDVClcclxuICAgICAgICAgICAgdGhpcy5zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2F2ZSgpIHtcclxuICAgICAgICB0aGlzLnVwZGF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsZXQgZ2xvYmFsID0gSlNPTi5zdHJpbmdpZnkodGhpcy5jYWNoZUdsb2JhbCk7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJzZXR0aW5ncy5nbG9iYWxcIiwgZ2xvYmFsKTtcclxuICAgICAgICBpZihsb2NhbFN0b3JhZ2Uuc2F2ZSlcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNhdmUoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFNlcnZlclNldHRpbmdzIGV4dGVuZHMgU2V0dGluZ3NCYXNlIHtcclxuICAgIHByaXZhdGUgY2FjaGVTZXJ2ZXIgPSB7fTtcclxuICAgIHByaXZhdGUgX3NlcnZlcl91bmlxdWVfaWQ6IHN0cmluZztcclxuICAgIHByaXZhdGUgX3NlcnZlcl9zYXZlX3dvcmtlcjogTm9kZUpTLlRpbWVyO1xyXG4gICAgcHJpdmF0ZSBfc2VydmVyX3NldHRpbmdzX3VwZGF0ZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuICAgIHByaXZhdGUgX2Rlc3Ryb3llZCA9IGZhbHNlO1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHN1cGVyKCk7XHJcbiAgICAgICAgdGhpcy5fc2VydmVyX3NhdmVfd29ya2VyID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfc2V0dGluZ3NfdXBkYXRlZClcclxuICAgICAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgIH0sIDUgKiAxMDAwKTtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIHRoaXMuX2Rlc3Ryb3llZCA9IHRydWU7XHJcblxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgdGhpcy5jYWNoZVNlcnZlciA9IHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLl9zZXJ2ZXJfc2F2ZV93b3JrZXIpO1xyXG4gICAgICAgIHRoaXMuX3NlcnZlcl9zYXZlX3dvcmtlciA9IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZXJ2ZXI/PFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4sIF9kZWZhdWx0PzogVCkgOiBUIHtcclxuICAgICAgICBpZih0aGlzLl9kZXN0cm95ZWQpIHRocm93IFwiZGVzdHJveWVkXCI7XHJcbiAgICAgICAgcmV0dXJuIFN0YXRpY1NldHRpbmdzLnJlc29sdmVLZXkoU2V0dGluZ3Mua2V5aWZ5KGtleSksIF9kZWZhdWx0LCBrZXkgPT4gdGhpcy5jYWNoZVNlcnZlcltrZXldKTtcclxuICAgIH1cclxuXHJcbiAgICBjaGFuZ2VTZXJ2ZXI8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPiwgdmFsdWU/OiBUKSB7XHJcbiAgICAgICAgaWYodGhpcy5fZGVzdHJveWVkKSB0aHJvdyBcImRlc3Ryb3llZFwiO1xyXG4gICAgICAgIGtleSA9IFNldHRpbmdzLmtleWlmeShrZXkpO1xyXG5cclxuICAgICAgICBpZih0aGlzLmNhY2hlU2VydmVyW2tleS5rZXldID09IHZhbHVlKSByZXR1cm47XHJcblxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl9zZXR0aW5nc191cGRhdGVkID0gdHJ1ZTtcclxuICAgICAgICB0aGlzLmNhY2hlU2VydmVyW2tleS5rZXldID0gU3RhdGljU2V0dGluZ3MudHJhbnNmb3JtT3RTKHZhbHVlKTtcclxuXHJcbiAgICAgICAgaWYoU2V0dGluZ3MuVVBEQVRFX0RJUkVDVClcclxuICAgICAgICAgICAgdGhpcy5zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0U2VydmVyKHNlcnZlcl91bmlxdWVfaWQ6IHN0cmluZykge1xyXG4gICAgICAgIGlmKHRoaXMuX2Rlc3Ryb3llZCkgdGhyb3cgXCJkZXN0cm95ZWRcIjtcclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgICAgICB0aGlzLmNhY2hlU2VydmVyID0ge307XHJcbiAgICAgICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSBzZXJ2ZXJfdW5pcXVlX2lkO1xyXG5cclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBqc29uID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJzZXR0aW5ncy5zZXJ2ZXJfXCIgKyBzZXJ2ZXJfdW5pcXVlX2lkKTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuY2FjaGVTZXJ2ZXIgPSBKU09OLnBhcnNlKGpzb24pO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBzZXJ2ZXIgc2V0dGluZ3MgZm9yIHNlcnZlciAlcyFcXG5Kc29uOiAlc1xcbkVycm9yOiAlb1wiKSwgc2VydmVyX3VuaXF1ZV9pZCwganNvbiwgZXJyb3IpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmKCF0aGlzLmNhY2hlU2VydmVyKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5jYWNoZVNlcnZlciA9IHt9O1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzYXZlKCkge1xyXG4gICAgICAgIGlmKHRoaXMuX2Rlc3Ryb3llZCkgdGhyb3cgXCJkZXN0cm95ZWRcIjtcclxuICAgICAgICB0aGlzLl9zZXJ2ZXJfc2V0dGluZ3NfdXBkYXRlZCA9IGZhbHNlO1xyXG5cclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgIGxldCBzZXJ2ZXIgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmNhY2hlU2VydmVyKTtcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJzZXR0aW5ncy5zZXJ2ZXJfXCIgKyB0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkLCBzZXJ2ZXIpO1xyXG4gICAgICAgICAgICBpZihsb2NhbFN0b3JhZ2Uuc2F2ZSlcclxuICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zYXZlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgbGV0IHNldHRpbmdzOiBTZXR0aW5nczsiLCJpbXBvcnQge0tleUNvZGV9IGZyb20gXCIuLi8uLi9QUFRMaXN0ZW5lclwiO1xyXG5cclxuZXhwb3J0IGVudW0gRWxlbWVudFR5cGUge1xyXG4gICAgSEVBREVSLFxyXG4gICAgQk9EWSxcclxuICAgIEZPT1RFUlxyXG59XHJcblxyXG5leHBvcnQgdHlwZSBCb2R5Q3JlYXRvciA9ICgoKSA9PiBKUXVlcnkgfCBKUXVlcnlbXSB8IHN0cmluZykgfCBzdHJpbmcgfCBKUXVlcnkgfCBKUXVlcnlbXTtcclxuZXhwb3J0IGNvbnN0IE1vZGFsRnVuY3Rpb25zID0ge1xyXG4gICAgZGl2aWZ5OiBmdW5jdGlvbiAodmFsOiBKUXVlcnkpIHtcclxuICAgICAgICBpZih2YWwubGVuZ3RoID4gMSlcclxuICAgICAgICAgICAgcmV0dXJuICQuc3Bhd24oXCJkaXZcIikuYXBwZW5kKHZhbCk7XHJcbiAgICAgICAgcmV0dXJuIHZhbDtcclxuICAgIH0sXHJcblxyXG4gICAganF1ZXJpZWZ5OiBmdW5jdGlvbih2YWw6IEJvZHlDcmVhdG9yLCB0eXBlPzogRWxlbWVudFR5cGUpIDogSlF1ZXJ5W10gfCBKUXVlcnkgfCB1bmRlZmluZWQge1xyXG4gICAgICAgIGlmKHR5cGVvZih2YWwpID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgICAgIHZhbCA9IHZhbCgpO1xyXG5cclxuICAgICAgICBpZih2YWwgaW5zdGFuY2VvZiBqUXVlcnkpXHJcbiAgICAgICAgICAgIHJldHVybiB2YWwgYXMgSlF1ZXJ5O1xyXG5cclxuICAgICAgICBpZihBcnJheS5pc0FycmF5KHZhbCkpIHtcclxuICAgICAgICAgICAgaWYodmFsLmxlbmd0aCA9PSAwKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgICAgIHJldHVybiB2YWwubWFwKGUgPT4gdGhpcy5qcXVlcmllZnkoZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgc3dpdGNoICh0eXBlb2YgdmFsKXtcclxuICAgICAgICAgICAgY2FzZSBcInN0cmluZ1wiOlxyXG4gICAgICAgICAgICAgICAgaWYodHlwZSA9PSBFbGVtZW50VHlwZS5IRUFERVIpXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJtb2RhbC10aXRsZVwiKS50ZXh0KHZhbCk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gJChcIjxkaXY+XCIgKyB2YWwgKyBcIjwvZGl2PlwiKTtcclxuICAgICAgICAgICAgY2FzZSBcIm9iamVjdFwiOiByZXR1cm4gdmFsIGFzIEpRdWVyeTtcclxuICAgICAgICAgICAgY2FzZSBcInVuZGVmaW5lZFwiOlxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoKFwiSW52YWxpZCB0eXBlICVvXCIpLCB0eXBlb2YgdmFsKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiAkKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICB3YXJwUHJvcGVydGllcyhkYXRhOiBNb2RhbFByb3BlcnRpZXMgfCBhbnkpIDogTW9kYWxQcm9wZXJ0aWVzIHtcclxuICAgICAgICBpZihkYXRhIGluc3RhbmNlb2YgTW9kYWxQcm9wZXJ0aWVzKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBkYXRhO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHByb3BzID0gbmV3IE1vZGFsUHJvcGVydGllcygpO1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGRhdGEpKVxyXG4gICAgICAgICAgICAgICAgcHJvcHNba2V5XSA9IGRhdGFba2V5XTtcclxuICAgICAgICAgICAgcmV0dXJuIHByb3BzO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufTtcclxuXHJcbmV4cG9ydCBjbGFzcyBNb2RhbFByb3BlcnRpZXMge1xyXG4gICAgdGVtcGxhdGU/OiBzdHJpbmc7XHJcbiAgICBoZWFkZXI6IEJvZHlDcmVhdG9yID0gKCkgPT4gXCJIRUFERVJcIjtcclxuICAgIGJvZHk6IEJvZHlDcmVhdG9yID0gKCkgICAgPT4gXCJCT0RZXCI7XHJcbiAgICBmb290ZXI6IEJvZHlDcmVhdG9yID0gKCkgID0+IFwiRk9PVEVSXCI7XHJcblxyXG4gICAgY2xvc2VMaXN0ZW5lcjogKCgpID0+IHZvaWQpIHwgKCgpID0+IHZvaWQpW10gPSAoKSA9PiB7fTtcclxuICAgIHJlZ2lzdGVyQ2xvc2VMaXN0ZW5lcihsaXN0ZW5lcjogKCkgPT4gdm9pZCkgOiB0aGlzIHtcclxuICAgICAgICBpZih0aGlzLmNsb3NlTGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgaWYoJC5pc0FycmF5KHRoaXMuY2xvc2VMaXN0ZW5lcikpXHJcbiAgICAgICAgICAgICAgICB0aGlzLmNsb3NlTGlzdGVuZXIucHVzaChsaXN0ZW5lcik7XHJcbiAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgIHRoaXMuY2xvc2VMaXN0ZW5lciA9IFt0aGlzLmNsb3NlTGlzdGVuZXIsIGxpc3RlbmVyXTtcclxuICAgICAgICB9IGVsc2UgdGhpcy5jbG9zZUxpc3RlbmVyID0gbGlzdGVuZXI7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICB3aWR0aDogbnVtYmVyIHwgc3RyaW5nO1xyXG4gICAgbWluX3dpZHRoPzogbnVtYmVyIHwgc3RyaW5nO1xyXG4gICAgaGVpZ2h0OiBudW1iZXIgfCBzdHJpbmcgPSBcImF1dG9cIjtcclxuXHJcbiAgICBjbG9zZWFibGU6IGJvb2xlYW4gPSB0cnVlO1xyXG5cclxuICAgIHRyaWdnZXJDbG9zZSgpe1xyXG4gICAgICAgIGlmKCQuaXNBcnJheSh0aGlzLmNsb3NlTGlzdGVuZXIpKVxyXG4gICAgICAgICAgICBmb3IobGV0IGxpc3RlbmVyIG9mIHRoaXMuY2xvc2VMaXN0ZW5lcilcclxuICAgICAgICAgICAgICAgIGxpc3RlbmVyKCk7XHJcbiAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICB0aGlzLmNsb3NlTGlzdGVuZXIoKTtcclxuICAgIH1cclxuXHJcbiAgICB0ZW1wbGF0ZV9wcm9wZXJ0aWVzPzogYW55ID0ge307XHJcbiAgICB0cmlnZ2VyX3RhYjogYm9vbGVhbiA9IHRydWU7XHJcbiAgICBmdWxsX3NpemU/OiBib29sZWFuID0gZmFsc2U7XHJcbn1cclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgbW9kYWwge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGluaXRpYWxpemVfbW9kYWxzKCkge1xyXG4gICAgICAgIHJlZ2lzdGVyX2dsb2JhbF9ldmVudHMoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBzY3JvbGxTaXplID0gMTg7XHJcbiAgICBmdW5jdGlvbiBzY3JvbGxfYmFyX2NsaWNrZWQoZXZlbnQpe1xyXG4gICAgICAgIGNvbnN0IHggPSBldmVudC5wYWdlWCxcclxuICAgICAgICAgICAgeSA9IGV2ZW50LnBhZ2VZLFxyXG4gICAgICAgICAgICBlID0gJChldmVudC50YXJnZXQpO1xyXG5cclxuICAgICAgICBpZihlLmhhc1Njcm9sbEJhcihcImhlaWdodFwiKSl7XHJcbiAgICAgICAgICAgIGNvbnN0IHRvcCA9IGUub2Zmc2V0KCkudG9wO1xyXG4gICAgICAgICAgICBjb25zdCByaWdodCA9IGUub2Zmc2V0KCkubGVmdCArIGUud2lkdGgoKTtcclxuICAgICAgICAgICAgY29uc3QgYm90dG9tID0gdG9wICtlLmhlaWdodCgpO1xyXG4gICAgICAgICAgICBjb25zdCBsZWZ0ID0gcmlnaHQgLSBzY3JvbGxTaXplO1xyXG5cclxuICAgICAgICAgICAgaWYoKHkgPj0gdG9wICYmIHkgPD0gYm90dG9tKSAmJiAoeCA+PSBsZWZ0ICYmIHggPD0gcmlnaHQpKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihlLmhhc1Njcm9sbEJhcihcIndpZHRoXCIpKXtcclxuICAgICAgICAgICAgY29uc3QgYm90dG9tID0gZS5vZmZzZXQoKS50b3AgKyBlLmhlaWdodCgpO1xyXG4gICAgICAgICAgICBjb25zdCB0b3AgPSBib3R0b20gLSBzY3JvbGxTaXplO1xyXG4gICAgICAgICAgICBjb25zdCBsZWZ0ID0gZS5vZmZzZXQoKS5sZWZ0O1xyXG4gICAgICAgICAgICBjb25zdCByaWdodCA9IGxlZnQgKyBlLndpZHRoKCk7XHJcblxyXG4gICAgICAgICAgICBpZigoeSA+PSB0b3AgJiYgeSA8PSBib3R0b20pICYmICh4ID49IGxlZnQgJiYgeCA8PSByaWdodCkpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICBmdW5jdGlvbiByZWdpc3Rlcl9nbG9iYWxfZXZlbnRzKCkge1xyXG4gICAgICAgICQoZG9jdW1lbnQpLm9uKCdtb3VzZWRvd24nLCAoZXZlbnQ6IEpRdWVyeS5Nb3VzZURvd25FdmVudCkgPT4ge1xyXG4gICAgICAgICAgICAvKiBwYWdlWCBvciBwYWdlWSBhcmUgdW5kZWZpbmVkIGlmIHRoaXMgaXMgYW4gZXZlbnQgZXhlY3V0ZWQgdmlhIC50cmlnZ2VyKCdjbGljaycpOyAqL1xyXG4gICAgICAgICAgICBpZihfZ2xvYmFsX21vZGFsX2NvdW50ID09IDAgfHwgdHlwZW9mKGV2ZW50LnBhZ2VYKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB0eXBlb2YoZXZlbnQucGFnZVkpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuXHJcbiAgICAgICAgICAgIGxldCBlbGVtZW50ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xyXG4gICAgICAgICAgICBjb25zdCBvcmlnaW5hbCA9IGVsZW1lbnQ7XHJcbiAgICAgICAgICAgIGRvIHtcclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbC1jb250ZW50JykpXHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIWVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbCcpKVxyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQgPT0gX2dsb2JhbF9tb2RhbF9sYXN0ICYmIF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lICsgMTAwID4gRGF0ZS5ub3coKSlcclxuICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuXHJcbiAgICAgICAgICAgICAgICBpZihlbGVtZW50ID09PSBvcmlnaW5hbCAmJiBzY3JvbGxfYmFyX2NsaWNrZWQoZXZlbnQpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0X3RpbWUgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgJChlbGVtZW50KS5maW5kKFwiPiAubW9kYWwtZGlhbG9nID4gLm1vZGFsLWNvbnRlbnQgPiAubW9kYWwtaGVhZGVyIC5idXR0b24tbW9kYWwtY2xvc2VcIikudHJpZ2dlcignY2xpY2snKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9IHdoaWxlKChlbGVtZW50ID0gZWxlbWVudC5wYXJlbnRFbGVtZW50KSk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICQoZG9jdW1lbnQpLm9uKCdrZXl1cCcsIChldmVudDogSlF1ZXJ5LktleVVwRXZlbnQpID0+IHtcclxuICAgICAgICAgICAgaWYoX2dsb2JhbF9tb2RhbF9jb3VudCA9PSAwIHx8IHR5cGVvZihldmVudC50YXJnZXQpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgaWYoZXZlbnQua2V5ICE9PSBcIkVzY2FwZVwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgbGV0IGVsZW1lbnQgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XHJcbiAgICAgICAgICAgIGlmKGVsZW1lbnQubm9kZU5hbWUgPT0gXCJIVE1MSW5wdXRFbGVtZW50XCIgfHwgZWxlbWVudC5ub2RlTmFtZSA9PSBcIkhUTUxTZWxlY3RFbGVtZW50XCIgfHwgZWxlbWVudC5ub2RlTmFtZSA9PSBcIkhUTUxUZXh0QXJlYUVsZW1lbnRcIilcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgIGRvIHtcclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbC1jb250ZW50JykpXHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIWVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbCcpKVxyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQgPT0gX2dsb2JhbF9tb2RhbF9sYXN0ICYmIF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lICsgMTAwID4gRGF0ZS5ub3coKSlcclxuICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuXHJcbiAgICAgICAgICAgICAgICAkKGVsZW1lbnQpLmZpbmQoXCI+IC5tb2RhbC1kaWFsb2cgPiAubW9kYWwtY29udGVudCA+IC5tb2RhbC1oZWFkZXIgLmJ1dHRvbi1tb2RhbC1jbG9zZVwiKS50cmlnZ2VyKCdjbGljaycpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIH0gd2hpbGUoKGVsZW1lbnQgPSBlbGVtZW50LnBhcmVudEVsZW1lbnQpKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxufVxyXG5tb2RhbC5pbml0aWFsaXplX21vZGFscygpO1xyXG5cclxubGV0IF9nbG9iYWxfbW9kYWxfY291bnQgPSAwO1xyXG5sZXQgX2dsb2JhbF9tb2RhbF9sYXN0OiBIVE1MRWxlbWVudDtcclxubGV0IF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lOiBudW1iZXI7XHJcblxyXG5leHBvcnQgY2xhc3MgTW9kYWwge1xyXG4gICAgcHJpdmF0ZSBfaHRtbFRhZzogSlF1ZXJ5O1xyXG4gICAgcHJvcGVydGllczogTW9kYWxQcm9wZXJ0aWVzO1xyXG4gICAgc2hvd246IGJvb2xlYW47XHJcblxyXG4gICAgb3Blbl9saXN0ZW5lcjogKCgpID0+IGFueSlbXSA9IFtdO1xyXG4gICAgY2xvc2VfbGlzdGVuZXI6ICgoKSA9PiBhbnkpW10gPSBbXTtcclxuICAgIGNsb3NlX2VsZW1lbnRzOiBKUXVlcnk7XHJcblxyXG4gICAgY29uc3RydWN0b3IocHJvcHM6IE1vZGFsUHJvcGVydGllcykge1xyXG4gICAgICAgIHRoaXMucHJvcGVydGllcyA9IHByb3BzO1xyXG4gICAgICAgIHRoaXMuc2hvd24gPSBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgaHRtbFRhZygpIDogSlF1ZXJ5IHtcclxuICAgICAgICBpZighdGhpcy5faHRtbFRhZykgdGhpcy5fY3JlYXRlKCk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2h0bWxUYWc7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBfY3JlYXRlKCkge1xyXG4gICAgICAgIGNvbnN0IGhlYWRlciA9IE1vZGFsRnVuY3Rpb25zLmpxdWVyaWVmeSh0aGlzLnByb3BlcnRpZXMuaGVhZGVyLCBFbGVtZW50VHlwZS5IRUFERVIpO1xyXG4gICAgICAgIGNvbnN0IGJvZHkgPSBNb2RhbEZ1bmN0aW9ucy5qcXVlcmllZnkodGhpcy5wcm9wZXJ0aWVzLmJvZHksIEVsZW1lbnRUeXBlLkJPRFkpO1xyXG4gICAgICAgIGNvbnN0IGZvb3RlciA9IE1vZGFsRnVuY3Rpb25zLmpxdWVyaWVmeSh0aGlzLnByb3BlcnRpZXMuZm9vdGVyLCBFbGVtZW50VHlwZS5GT09URVIpO1xyXG5cclxuICAgICAgICAvL0ZJWE1FOiBjYWNoZSB0ZW1wbGF0ZVxyXG4gICAgICAgIGNvbnN0IHRlbXBsYXRlID0gJCh0aGlzLnByb3BlcnRpZXMudGVtcGxhdGUgfHwgXCIjdG1wbF9tb2RhbFwiKTtcclxuXHJcbiAgICAgICAgY29uc3QgcHJvcGVydGllcyA9IHtcclxuICAgICAgICAgICAgbW9kYWxfaGVhZGVyOiBoZWFkZXIsXHJcbiAgICAgICAgICAgIG1vZGFsX2JvZHk6IGJvZHksXHJcbiAgICAgICAgICAgIG1vZGFsX2Zvb3RlcjogZm9vdGVyLFxyXG5cclxuICAgICAgICAgICAgY2xvc2VhYmxlOiB0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlLFxyXG4gICAgICAgICAgICBmdWxsX3NpemU6IHRoaXMucHJvcGVydGllcy5mdWxsX3NpemVcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBpZih0aGlzLnByb3BlcnRpZXMudGVtcGxhdGVfcHJvcGVydGllcylcclxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihwcm9wZXJ0aWVzLCB0aGlzLnByb3BlcnRpZXMudGVtcGxhdGVfcHJvcGVydGllcyk7XHJcblxyXG4gICAgICAgIGNvbnN0IHRhZyA9IHRlbXBsYXRlLnJlbmRlclRhZyhwcm9wZXJ0aWVzKTtcclxuICAgICAgICBpZih0eXBlb2YodGhpcy5wcm9wZXJ0aWVzLndpZHRoKSAhPT0gXCJ1bmRlZmluZWRcIiAmJiB0eXBlb2YodGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aCkgIT09IFwidW5kZWZpbmVkXCIpXHJcbiAgICAgICAgICAgIHRhZy5maW5kKFwiLm1vZGFsLWNvbnRlbnRcIilcclxuICAgICAgICAgICAgICAgIC5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aClcclxuICAgICAgICAgICAgICAgIC5jc3MoXCJ3aWR0aFwiLCB0aGlzLnByb3BlcnRpZXMud2lkdGgpO1xyXG4gICAgICAgIGVsc2UgaWYodHlwZW9mKHRoaXMucHJvcGVydGllcy53aWR0aCkgIT09IFwidW5kZWZpbmVkXCIpIC8vTGVnYWN5IHN1cHBvcnRcclxuICAgICAgICAgICAgdGFnLmZpbmQoXCIubW9kYWwtY29udGVudFwiKS5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLndpZHRoKTtcclxuICAgICAgICBlbHNlIGlmKHR5cGVvZih0aGlzLnByb3BlcnRpZXMubWluX3dpZHRoKSAhPT0gXCJ1bmRlZmluZWRcIilcclxuICAgICAgICAgICAgdGFnLmZpbmQoXCIubW9kYWwtY29udGVudFwiKS5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aCk7XHJcblxyXG4gICAgICAgIHRoaXMuY2xvc2VfZWxlbWVudHMgPSB0YWcuZmluZChcIi5idXR0b24tbW9kYWwtY2xvc2VcIik7XHJcbiAgICAgICAgdGhpcy5jbG9zZV9lbGVtZW50cy50b2dnbGUodGhpcy5wcm9wZXJ0aWVzLmNsb3NlYWJsZSkub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICBpZih0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5jbG9zZSgpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIHRoaXMuX2h0bWxUYWcgPSB0YWc7XHJcblxyXG4gICAgICAgIHRoaXMuX2h0bWxUYWcuZmluZChcImlucHV0XCIpLm9uKCdjaGFuZ2UnLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICQoZXZlbnQudGFyZ2V0KS5wYXJlbnRzKFwiLmZvcm0tZ3JvdXBcIikudG9nZ2xlQ2xhc3MoJ2lzLWZpbGxlZCcsICEhKGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50KS52YWx1ZSk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vVE9ETzogQWZ0ZXIgdGhlIGFuaW1hdGlvbiFcclxuICAgICAgICB0aGlzLl9odG1sVGFnLm9uKCdoaWRlLmJzLm1vZGFsJywgZXZlbnQgPT4gIXRoaXMucHJvcGVydGllcy5jbG9zZWFibGUgfHwgdGhpcy5jbG9zZSgpKTtcclxuICAgICAgICB0aGlzLl9odG1sVGFnLm9uKCdoaWRkZW4uYnMubW9kYWwnLCBldmVudCA9PiB0aGlzLl9odG1sVGFnLnJlbW92ZSgpKTtcclxuICAgIH1cclxuXHJcbiAgICBvcGVuKCkge1xyXG4gICAgICAgIGlmKHRoaXMuc2hvd24pXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0X3RpbWUgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgIF9nbG9iYWxfbW9kYWxfbGFzdCA9IHRoaXMuaHRtbFRhZ1swXTtcclxuXHJcbiAgICAgICAgdGhpcy5zaG93biA9IHRydWU7XHJcbiAgICAgICAgdGhpcy5odG1sVGFnLmFwcGVuZFRvKCQoXCJib2R5XCIpKTtcclxuXHJcbiAgICAgICAgX2dsb2JhbF9tb2RhbF9jb3VudCsrO1xyXG4gICAgICAgIHRoaXMuaHRtbFRhZy5zaG93KCk7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmh0bWxUYWcuYWRkQ2xhc3MoJ3Nob3duJyksIDApO1xyXG5cclxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IGxpc3RlbmVyIG9mIHRoaXMub3Blbl9saXN0ZW5lcikgbGlzdGVuZXIoKTtcclxuICAgICAgICAgICAgdGhpcy5odG1sVGFnLmZpbmQoXCIudGFiXCIpLnRyaWdnZXIoJ3RhYi5yZXNpemUnKTtcclxuICAgICAgICB9LCAzMDApO1xyXG4gICAgfVxyXG5cclxuICAgIGNsb3NlKCkge1xyXG4gICAgICAgIGlmKCF0aGlzLnNob3duKSByZXR1cm47XHJcblxyXG4gICAgICAgIF9nbG9iYWxfbW9kYWxfY291bnQtLTtcclxuICAgICAgICBpZihfZ2xvYmFsX21vZGFsX2xhc3QgPT09IHRoaXMuaHRtbFRhZ1swXSlcclxuICAgICAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0ID0gdW5kZWZpbmVkO1xyXG5cclxuICAgICAgICB0aGlzLnNob3duID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5odG1sVGFnLnJlbW92ZUNsYXNzKCdzaG93bicpO1xyXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLmh0bWxUYWcucmVtb3ZlKCk7XHJcbiAgICAgICAgICAgIHRoaXMuX2h0bWxUYWcgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfSwgMzAwKTtcclxuICAgICAgICB0aGlzLnByb3BlcnRpZXMudHJpZ2dlckNsb3NlKCk7XHJcbiAgICAgICAgZm9yKGNvbnN0IGxpc3RlbmVyIG9mIHRoaXMuY2xvc2VfbGlzdGVuZXIpXHJcbiAgICAgICAgICAgIGxpc3RlbmVyKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0X2Nsb3NlYWJsZShmbGFnOiBib29sZWFuKSB7XHJcbiAgICAgICAgaWYoZmxhZyA9PT0gdGhpcy5wcm9wZXJ0aWVzLmNsb3NlYWJsZSlcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICB0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlID0gZmxhZztcclxuICAgICAgICB0aGlzLmNsb3NlX2VsZW1lbnRzLnRvZ2dsZShmbGFnKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1vZGFsKGRhdGE6IE1vZGFsUHJvcGVydGllcyB8IGFueSkgOiBNb2RhbCB7XHJcbiAgICByZXR1cm4gbmV3IE1vZGFsKE1vZGFsRnVuY3Rpb25zLndhcnBQcm9wZXJ0aWVzKGRhdGEpKTtcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIElucHV0TW9kYWxQcm9wZXJ0aWVzIGV4dGVuZHMgTW9kYWxQcm9wZXJ0aWVzIHtcclxuICAgIG1heExlbmd0aD86IG51bWJlcjtcclxuXHJcbiAgICBmaWVsZF90aXRsZT86IHN0cmluZztcclxuICAgIGZpZWxkX2xhYmVsPzogc3RyaW5nO1xyXG4gICAgZmllbGRfcGxhY2Vob2xkZXI/OiBzdHJpbmc7XHJcblxyXG4gICAgZXJyb3JfbWVzc2FnZT86IHN0cmluZztcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlucHV0TW9kYWwoaGVhZE1lc3NhZ2U6IEJvZHlDcmVhdG9yLCBxdWVzdGlvbjogQm9keUNyZWF0b3IsIHZhbGlkYXRvcjogKGlucHV0OiBzdHJpbmcpID0+IGJvb2xlYW4sIGNhbGxiYWNrOiAoZmxhZzogYm9vbGVhbiB8IHN0cmluZykgPT4gdm9pZCwgcHJvcHM6IElucHV0TW9kYWxQcm9wZXJ0aWVzIHwgYW55ID0ge30pIDogTW9kYWwge1xyXG4gICAgcHJvcHMgPSBNb2RhbEZ1bmN0aW9ucy53YXJwUHJvcGVydGllcyhwcm9wcyk7XHJcbiAgICBwcm9wcy50ZW1wbGF0ZV9wcm9wZXJ0aWVzIHx8IChwcm9wcy50ZW1wbGF0ZV9wcm9wZXJ0aWVzID0ge30pO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF90aXRsZSA9IHByb3BzLmZpZWxkX3RpdGxlO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF9sYWJlbCA9IHByb3BzLmZpZWxkX2xhYmVsO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF9wbGFjZWhvbGRlciA9IHByb3BzLmZpZWxkX3BsYWNlaG9sZGVyO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5lcnJvcl9tZXNzYWdlID0gcHJvcHMuZXJyb3JfbWVzc2FnZTtcclxuXHJcbiAgICBwcm9wcy50ZW1wbGF0ZSA9IFwiI3RtcGxfbW9kYWxfaW5wdXRcIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkTWVzc2FnZTtcclxuICAgIHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMucXVlc3Rpb24gPSBNb2RhbEZ1bmN0aW9ucy5qcXVlcmllZnkocXVlc3Rpb24pO1xyXG5cclxuICAgIGNvbnN0IG1vZGFsID0gY3JlYXRlTW9kYWwocHJvcHMpO1xyXG5cclxuICAgIGNvbnN0IGlucHV0ID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci12YWx1ZSBpbnB1dFwiKTtcclxuICAgIGNvbnN0IGJ1dHRvbl9jYW5jZWwgPSBtb2RhbC5odG1sVGFnLmZpbmQoXCIuYnV0dG9uLWNhbmNlbFwiKTtcclxuICAgIGNvbnN0IGJ1dHRvbl9zdWJtaXQgPSBtb2RhbC5odG1sVGFnLmZpbmQoXCIuYnV0dG9uLXN1Ym1pdFwiKTtcclxuXHJcbiAgICBsZXQgc3VibWl0ZWQgPSBmYWxzZTtcclxuICAgIGlucHV0Lm9uKCdrZXl1cCBjaGFuZ2UnLCBldmVudCA9PiB7XHJcbiAgICAgICAgY29uc3Qgc3RyID0gaW5wdXQudmFsKCkgYXMgc3RyaW5nO1xyXG4gICAgICAgIGNvbnN0IHZhbGlkID0gc3RyICE9PSB1bmRlZmluZWQgJiYgdmFsaWRhdG9yKHN0cik7XHJcblxyXG4gICAgICAgIGlucHV0LmF0dHIoXCJwYXR0ZXJuXCIsIHZhbGlkID8gbnVsbCA6IFwiXlthXXsxMDAwfSRcIikudG9nZ2xlQ2xhc3MoXCJpcy1pbnZhbGlkXCIsICF2YWxpZCk7XHJcbiAgICAgICAgYnV0dG9uX3N1Ym1pdC5wcm9wKFwiZGlzYWJsZWRcIiwgIXZhbGlkKTtcclxuICAgIH0pO1xyXG4gICAgaW5wdXQub24oJ2tleWRvd24nLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQua2V5Q29kZSAhPT0gS2V5Q29kZS5LRVlfUkVUVVJOIHx8IGV2ZW50LnNoaWZ0S2V5KVxyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgaWYoYnV0dG9uX3N1Ym1pdC5wcm9wKFwiZGlzYWJsZWRcIikpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICBidXR0b25fc3VibWl0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBidXR0b25fc3VibWl0Lm9uKCdjbGljaycsIGV2ZW50ID0+IHtcclxuICAgICAgICBpZighc3VibWl0ZWQpIHtcclxuICAgICAgICAgICAgc3VibWl0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICBjb25zdCBzdHIgPSBpbnB1dC52YWwoKSBhcyBzdHJpbmc7XHJcbiAgICAgICAgICAgIGlmKHN0ciAhPT0gdW5kZWZpbmVkICYmIHZhbGlkYXRvcihzdHIpKVxyXG4gICAgICAgICAgICAgICAgY2FsbGJhY2soc3RyKTtcclxuICAgICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG4gICAgfSkucHJvcChcImRpc2FibGVkXCIsICF2YWxpZGF0b3IoXCJcIikpOyAvKiBkaXNhYmxlZCBpZiBlbXB0eSBpbnB1dCBpc24ndCBhbGxvd2VkICovXHJcblxyXG4gICAgYnV0dG9uX2NhbmNlbC5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoIXN1Ym1pdGVkKSB7XHJcbiAgICAgICAgICAgIHN1Ym1pdGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbW9kYWwub3Blbl9saXN0ZW5lci5wdXNoKCgpID0+IGlucHV0LmZvY3VzKCkpO1xyXG4gICAgbW9kYWwuY2xvc2VfbGlzdGVuZXIucHVzaCgoKSA9PiBidXR0b25fY2FuY2VsLnRyaWdnZXIoJ2NsaWNrJykpO1xyXG4gICAgcmV0dXJuIG1vZGFsO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRXJyb3JNb2RhbChoZWFkZXI6IEJvZHlDcmVhdG9yLCBtZXNzYWdlOiBCb2R5Q3JlYXRvciwgcHJvcHM6IE1vZGFsUHJvcGVydGllcyB8IGFueSA9IHsgZm9vdGVyOiB1bmRlZmluZWQgfSkge1xyXG4gICAgcHJvcHMgPSBNb2RhbEZ1bmN0aW9ucy53YXJwUHJvcGVydGllcyhwcm9wcyk7XHJcbiAgICAocHJvcHMudGVtcGxhdGVfcHJvcGVydGllcyB8fCAocHJvcHMudGVtcGxhdGVfcHJvcGVydGllcyA9IHt9KSkuaGVhZGVyX2NsYXNzID0gXCJtb2RhbC1oZWFkZXItZXJyb3JcIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkZXI7XHJcbiAgICBwcm9wcy5ib2R5ID0gbWVzc2FnZTtcclxuXHJcbiAgICBjb25zdCBtb2RhbCA9IGNyZWF0ZU1vZGFsKHByb3BzKTtcclxuICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtZXJyb3JcIik7XHJcbiAgICByZXR1cm4gbW9kYWw7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJbmZvTW9kYWwoaGVhZGVyOiBCb2R5Q3JlYXRvciwgbWVzc2FnZTogQm9keUNyZWF0b3IsIHByb3BzOiBNb2RhbFByb3BlcnRpZXMgfCBhbnkgPSB7IGZvb3RlcjogdW5kZWZpbmVkIH0pIHtcclxuICAgIHByb3BzID0gTW9kYWxGdW5jdGlvbnMud2FycFByb3BlcnRpZXMocHJvcHMpO1xyXG4gICAgKHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMgfHwgKHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMgPSB7fSkpLmhlYWRlcl9jbGFzcyA9IFwibW9kYWwtaGVhZGVyLWluZm9cIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkZXI7XHJcbiAgICBwcm9wcy5ib2R5ID0gbWVzc2FnZTtcclxuXHJcbiAgICBjb25zdCBtb2RhbCA9IGNyZWF0ZU1vZGFsKHByb3BzKTtcclxuICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtaW5mb1wiKTtcclxuICAgIHJldHVybiBtb2RhbDtcclxufVxyXG4iLCJpbXBvcnQge2xvZywgTG9nQ2F0ZWdvcnl9IGZyb20gXCIuLi8uLi9sb2dcIjtcclxuXHJcbmV4cG9ydCBlbnVtIENoYXRUeXBlIHtcclxuICAgIEdFTkVSQUwsXHJcbiAgICBTRVJWRVIsXHJcbiAgICBDSEFOTkVMLFxyXG4gICAgQ0xJRU5UXHJcbn1cclxuXHJcbmRlY2xhcmUgY29uc3QgeGJiY29kZTogYW55O1xyXG5leHBvcnQgbmFtZXNwYWNlIE1lc3NhZ2VIZWxwZXIge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGh0bWxFc2NhcGUobWVzc2FnZTogc3RyaW5nKSA6IHN0cmluZ1tdIHtcclxuICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgICAgICBkaXYuaW5uZXJUZXh0ID0gbWVzc2FnZTtcclxuICAgICAgICBtZXNzYWdlID0gIGRpdi5pbm5lckhUTUw7XHJcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2UucmVwbGFjZSgvIC9nLCAnJm5ic3A7Jykuc3BsaXQoLzxicj4vKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gZm9ybWF0RWxlbWVudChvYmplY3Q6IGFueSwgZXNjYXBlX2h0bWw6IGJvb2xlYW4gPSB0cnVlKSA6IEpRdWVyeVtdIHtcclxuICAgICAgICBpZigkLmlzQXJyYXkob2JqZWN0KSkge1xyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gW107XHJcbiAgICAgICAgICAgIGZvcihsZXQgZWxlbWVudCBvZiBvYmplY3QpXHJcbiAgICAgICAgICAgICAgICByZXN1bHQucHVzaCguLi5mb3JtYXRFbGVtZW50KGVsZW1lbnQsIGVzY2FwZV9odG1sKSk7XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09IFwic3RyaW5nXCIpIHtcclxuICAgICAgICAgICAgaWYob2JqZWN0Lmxlbmd0aCA9PSAwKSByZXR1cm4gW107XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gZXNjYXBlX2h0bWwgP1xyXG4gICAgICAgICAgICAgICAgaHRtbEVzY2FwZShvYmplY3QpLm1hcCgoZW50cnksIGlkeCwgYXJyYXkpID0+ICQuc3Bhd24oXCJhXCIpLmNzcyhcImRpc3BsYXlcIiwgKGlkeCA9PSAwIHx8IGlkeCArIDEgPT0gYXJyYXkubGVuZ3RoID8gXCJpbmxpbmVcIiA6IFwiXCIpICsgXCJibG9ja1wiKS5odG1sKGVudHJ5ID09IFwiXCIgJiYgaWR4ICE9IDAgPyBcIiZuYnNwO1wiIDogZW50cnkpKSA6XHJcbiAgICAgICAgICAgICAgICBbJC5zcGF3bihcImRpdlwiKS5jc3MoXCJkaXNwbGF5XCIsIFwiaW5saW5lLWJsb2NrXCIpLmh0bWwob2JqZWN0KV07XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcIm9iamVjdFwiKSB7XHJcbiAgICAgICAgICAgIGlmKG9iamVjdCBpbnN0YW5jZW9mICQpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gW29iamVjdCBhcyBhbnldO1xyXG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0RWxlbWVudChcIjx1bmtud29uIG9iamVjdD5cIik7XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcImZ1bmN0aW9uXCIpIHJldHVybiBmb3JtYXRFbGVtZW50KG9iamVjdCgpLCBlc2NhcGVfaHRtbCk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlb2Yob2JqZWN0KSA9PT0gXCJ1bmRlZmluZWRcIikgcmV0dXJuIGZvcm1hdEVsZW1lbnQoXCI8dW5kZWZpbmVkPlwiKTtcclxuICAgICAgICBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcIm51bWJlclwiKSByZXR1cm4gWyQuc3Bhd24oXCJhXCIpLnRleHQob2JqZWN0KV07XHJcbiAgICAgICAgcmV0dXJuIGZvcm1hdEVsZW1lbnQoXCI8dW5rbm93biBvYmplY3QgdHlwZSBcIiArIHR5cGVvZiBvYmplY3QgKyBcIj5cIik7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdE1lc3NhZ2UocGF0dGVybjogc3RyaW5nLCAuLi5vYmplY3RzOiBhbnlbXSkgOiBKUXVlcnlbXSB7XHJcbiAgICAgICAgbGV0IGJlZ2luID0gMCwgZm91bmQgPSAwO1xyXG5cclxuICAgICAgICBsZXQgcmVzdWx0OiBKUXVlcnlbXSA9IFtdO1xyXG4gICAgICAgIGRvIHtcclxuICAgICAgICAgICAgZm91bmQgPSBwYXR0ZXJuLmluZGV4T2YoJ3snLCBmb3VuZCk7XHJcbiAgICAgICAgICAgIGlmKGZvdW5kID09IC0xIHx8IHBhdHRlcm4ubGVuZ3RoIDw9IGZvdW5kICsgMSkge1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goLi4uZm9ybWF0RWxlbWVudChwYXR0ZXJuLnN1YnN0cihiZWdpbikpKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZihmb3VuZCA+IDAgJiYgcGF0dGVybltmb3VuZCAtIDFdID09ICdcXFxcJykge1xyXG4gICAgICAgICAgICAgICAgLy9UT0RPIHJlbW92ZSB0aGUgZXNjYXBlIVxyXG4gICAgICAgICAgICAgICAgZm91bmQrKztcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXN1bHQucHVzaCguLi5mb3JtYXRFbGVtZW50KHBhdHRlcm4uc3Vic3RyKGJlZ2luLCBmb3VuZCAtIGJlZ2luKSkpOyAvL0FwcGVuZCB0aGUgdGV4dFxyXG5cclxuICAgICAgICAgICAgbGV0IG9mZnNldCA9IDA7XHJcbiAgICAgICAgICAgIGlmKHBhdHRlcm5bZm91bmQgKyAxXSA9PSAnOicpIHtcclxuICAgICAgICAgICAgICAgIG9mZnNldCsrOyAvKiB0aGUgYmVnaW5uaW5nIDogKi9cclxuICAgICAgICAgICAgICAgIHdoaWxlIChwYXR0ZXJuW2ZvdW5kICsgMSArIG9mZnNldF0gIT0gJzonICYmIGZvdW5kICsgMSArIG9mZnNldCA8IHBhdHRlcm4ubGVuZ3RoKSBvZmZzZXQrKztcclxuICAgICAgICAgICAgICAgIGNvbnN0IHRhZyA9IHBhdHRlcm4uc3Vic3RyKGZvdW5kICsgMiwgb2Zmc2V0IC0gMSk7XHJcblxyXG4gICAgICAgICAgICAgICAgb2Zmc2V0Kys7IC8qIHRoZSBlbmRpbmcgOiAqL1xyXG4gICAgICAgICAgICAgICAgaWYocGF0dGVybltmb3VuZCArIG9mZnNldCArIDFdICE9ICd9JyAmJiBmb3VuZCArIDEgKyBvZmZzZXQgPCBwYXR0ZXJuLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGZvdW5kKys7XHJcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goJC5zcGF3bih0YWcgYXMgYW55KSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgbnVtYmVyO1xyXG4gICAgICAgICAgICAgICAgd2hpbGUgKFwiMDEyMzQ1Njc4OVwiLmluY2x1ZGVzKHBhdHRlcm5bZm91bmQgKyAxICsgb2Zmc2V0XSkpIG9mZnNldCsrO1xyXG4gICAgICAgICAgICAgICAgbnVtYmVyID0gcGFyc2VJbnQob2Zmc2V0ID4gMCA/IHBhdHRlcm4uc3Vic3RyKGZvdW5kICsgMSwgb2Zmc2V0KSA6IFwiMFwiKTtcclxuICAgICAgICAgICAgICAgIGlmKHBhdHRlcm5bZm91bmQgKyBvZmZzZXQgKyAxXSAhPSAnfScpIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3VuZCsrO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmKG9iamVjdHMubGVuZ3RoIDwgbnVtYmVyKVxyXG4gICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkdFTkVSQUwsIHRyKFwiTWVzc2FnZSB0byBmb3JtYXQgY29udGFpbnMgaW52YWxpZCBpbmRleCAoJW8pXCIpLCBudW1iZXIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLmZvcm1hdEVsZW1lbnQob2JqZWN0c1tudW1iZXJdKSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvdW5kID0gZm91bmQgKyAxICsgb2Zmc2V0O1xyXG4gICAgICAgICAgICBiZWdpbiA9IGZvdW5kICsgMTtcclxuICAgICAgICB9IHdoaWxlKGZvdW5kKyspO1xyXG5cclxuICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfVxyXG5cclxuICAgIC8vVE9ETzogUmVtb3ZlIHRoaXMgKG9ubHkgbGVnYWN5KVxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGJiY29kZV9jaGF0KG1lc3NhZ2U6IHN0cmluZykgOiBKUXVlcnlbXSB7XHJcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VzLmZvcm1hdHRlci5iYmNvZGUuZm9ybWF0KG1lc3NhZ2UsIHtcclxuICAgICAgICAgICAgaXNfY2hhdF9tZXNzYWdlOiB0cnVlXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IG5hbWVzcGFjZSBuZXR3b3JrIHtcclxuICAgICAgICBleHBvcnQgY29uc3QgS0IgPSAxMDI0O1xyXG4gICAgICAgIGV4cG9ydCBjb25zdCBNQiA9IDEwMjQgKiBLQjtcclxuICAgICAgICBleHBvcnQgY29uc3QgR0IgPSAxMDI0ICogTUI7XHJcbiAgICAgICAgZXhwb3J0IGNvbnN0IFRCID0gMTAyNCAqIEdCO1xyXG5cclxuICAgICAgICBleHBvcnQgZnVuY3Rpb24gZm9ybWF0X2J5dGVzKHZhbHVlOiBudW1iZXIsIG9wdGlvbnM/OiB7XHJcbiAgICAgICAgICAgIHRpbWU/OiBzdHJpbmcsXHJcbiAgICAgICAgICAgIHVuaXQ/OiBzdHJpbmcsXHJcbiAgICAgICAgICAgIGV4YWN0PzogYm9vbGVhblxyXG4gICAgICAgIH0pIDogc3RyaW5nIHtcclxuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcbiAgICAgICAgICAgIGlmKHR5cGVvZiBvcHRpb25zLmV4YWN0ICE9PSBcImJvb2xlYW5cIilcclxuICAgICAgICAgICAgICAgIG9wdGlvbnMuZXhhY3QgPSB0cnVlO1xyXG4gICAgICAgICAgICBpZih0eXBlb2Ygb3B0aW9ucy51bml0ICE9PSBcInN0cmluZ1wiKVxyXG4gICAgICAgICAgICAgICAgb3B0aW9ucy51bml0ID0gXCJCeXRlc1wiO1xyXG5cclxuICAgICAgICAgICAgbGV0IHBvaW50cyA9IHZhbHVlLnRvRml4ZWQoMCkucmVwbGFjZSgvKFxcZCkoPz0oXFxkezN9KSsoPyFcXGQpKS9nLCAnJDEsJyk7XHJcblxyXG4gICAgICAgICAgICBsZXQgdiwgdW5pdDtcclxuICAgICAgICAgICAgaWYodmFsdWUgPiAyICogVEIpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIlRCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBUQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gR0IpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIkdCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBHQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gTUIpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIk1CXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBNQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gS0IpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIktCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBLQjtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIlwiO1xyXG4gICAgICAgICAgICAgICAgdiA9IHZhbHVlO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gXCJcIjtcclxuICAgICAgICAgICAgaWYob3B0aW9ucy5leGFjdCB8fCAhdW5pdCkge1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0ICs9IHBvaW50cztcclxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMudW5pdCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIG9wdGlvbnMudW5pdDtcclxuICAgICAgICAgICAgICAgICAgICBpZihvcHRpb25zLnRpbWUpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBcIi9cIiArIG9wdGlvbnMudGltZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZih1bml0KSB7XHJcbiAgICAgICAgICAgICAgICByZXN1bHQgKz0gKHJlc3VsdCA/IFwiIC8gXCIgOiBcIlwiKSArIHYudG9GaXhlZCgyKSArIFwiIFwiICsgdW5pdDtcclxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMudGltZSlcclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gXCIvXCIgKyBvcHRpb25zLnRpbWU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGNvbnN0IEsgPSAxMDAwO1xyXG4gICAgZXhwb3J0IGNvbnN0IE0gPSAxMDAwICogSztcclxuICAgIGV4cG9ydCBjb25zdCBHID0gMTAwMCAqIE07XHJcbiAgICBleHBvcnQgY29uc3QgVCA9IDEwMDAgKiBHO1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdF9udW1iZXIodmFsdWU6IG51bWJlciwgb3B0aW9ucz86IHtcclxuICAgICAgICB0aW1lPzogc3RyaW5nLFxyXG4gICAgICAgIHVuaXQ/OiBzdHJpbmdcclxuICAgIH0pIHtcclxuICAgICAgICBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihvcHRpb25zIHx8IHt9LCB7fSk7XHJcblxyXG4gICAgICAgIGxldCBwb2ludHMgPSB2YWx1ZS50b0ZpeGVkKDApLnJlcGxhY2UoLyhcXGQpKD89KFxcZHszfSkrKD8hXFxkKSkvZywgJyQxLCcpO1xyXG5cclxuICAgICAgICBsZXQgdiwgdW5pdDtcclxuICAgICAgICBpZih2YWx1ZSA+IDIgKiBUKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIlRcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gVDtcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBHKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIkdcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gRztcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBNKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIk1cIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gTTtcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBLKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIktcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gSztcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICB1bml0ID0gXCJcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZih1bml0ICYmIG9wdGlvbnMudGltZSlcclxuICAgICAgICAgICAgdW5pdCA9IHVuaXQgKyBcIi9cIiArIG9wdGlvbnMudGltZTtcclxuICAgICAgICByZXR1cm4gcG9pbnRzICsgXCIgXCIgKyAob3B0aW9ucy51bml0IHx8IFwiXCIpICsgKHVuaXQgPyAoXCIgLyBcIiArIHYudG9GaXhlZCgyKSArIFwiIFwiICsgdW5pdCkgOiBcIlwiKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY29uc3QgVElNRV9TRUNPTkQgPSAxMDAwO1xyXG4gICAgZXhwb3J0IGNvbnN0IFRJTUVfTUlOVVRFID0gNjAgKiBUSU1FX1NFQ09ORDtcclxuICAgIGV4cG9ydCBjb25zdCBUSU1FX0hPVVIgPSA2MCAqIFRJTUVfTUlOVVRFO1xyXG4gICAgZXhwb3J0IGNvbnN0IFRJTUVfREFZID0gMjQgKiBUSU1FX0hPVVI7XHJcbiAgICBleHBvcnQgY29uc3QgVElNRV9XRUVLID0gNyAqIFRJTUVfREFZO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBmb3JtYXRfdGltZSh0aW1lOiBudW1iZXIsIGRlZmF1bHRfdmFsdWU6IHN0cmluZykge1xyXG4gICAgICAgIGxldCByZXN1bHQgPSBcIlwiO1xyXG4gICAgICAgIGlmKHRpbWUgPiBUSU1FX1dFRUspIHtcclxuICAgICAgICAgICAgY29uc3QgYW1vdW50ID0gTWF0aC5mbG9vcih0aW1lIC8gVElNRV9XRUVLKTtcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiIFwiICsgYW1vdW50ICsgXCIgXCIgKyAoYW1vdW50ID4gMSA/IHRyKFwiV2Vla3NcIikgOiB0cihcIldlZWtcIikpO1xyXG4gICAgICAgICAgICB0aW1lIC09IGFtb3VudCAqIFRJTUVfV0VFSztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKHRpbWUgPiBUSU1FX0RBWSkge1xyXG4gICAgICAgICAgICBjb25zdCBhbW91bnQgPSBNYXRoLmZsb29yKHRpbWUgLyBUSU1FX0RBWSk7XHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIGFtb3VudCArIFwiIFwiICsgKGFtb3VudCA+IDEgPyB0cihcIkRheXNcIikgOiB0cihcIkRheVwiKSk7XHJcbiAgICAgICAgICAgIHRpbWUgLT0gYW1vdW50ICogVElNRV9EQVk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZih0aW1lID4gVElNRV9IT1VSKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGFtb3VudCA9IE1hdGguZmxvb3IodGltZSAvIFRJTUVfSE9VUik7XHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIGFtb3VudCArIFwiIFwiICsgKGFtb3VudCA+IDEgPyB0cihcIkhvdXJzXCIpIDogdHIoXCJIb3VyXCIpKTtcclxuICAgICAgICAgICAgdGltZSAtPSBhbW91bnQgKiBUSU1FX0hPVVI7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZih0aW1lID4gVElNRV9NSU5VVEUpIHtcclxuICAgICAgICAgICAgY29uc3QgYW1vdW50ID0gTWF0aC5mbG9vcih0aW1lIC8gVElNRV9NSU5VVEUpO1xyXG4gICAgICAgICAgICByZXN1bHQgKz0gXCIgXCIgKyBhbW91bnQgKyBcIiBcIiArIChhbW91bnQgPiAxID8gdHIoXCJNaW51dGVzXCIpIDogdHIoXCJNaW51dGVcIikpO1xyXG4gICAgICAgICAgICB0aW1lIC09IGFtb3VudCAqIFRJTUVfTUlOVVRFO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYodGltZSA+IFRJTUVfU0VDT05EKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGFtb3VudCA9IE1hdGguZmxvb3IodGltZSAvIFRJTUVfU0VDT05EKTtcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiIFwiICsgYW1vdW50ICsgXCIgXCIgKyAoYW1vdW50ID4gMSA/IHRyKFwiU2Vjb25kc1wiKSA6IHRyKFwiU2Vjb25kXCIpKTtcclxuICAgICAgICAgICAgdGltZSAtPSBhbW91bnQgKiBUSU1FX1NFQ09ORDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQubGVuZ3RoID4gMCA/IHJlc3VsdC5zdWJzdHJpbmcoMSkgOiBkZWZhdWx0X3ZhbHVlO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBfaWNvbl9zaXplX3N0eWxlOiBKUXVlcnk8SFRNTFN0eWxlRWxlbWVudD47XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc2V0X2ljb25fc2l6ZShzaXplOiBzdHJpbmcpIHtcclxuICAgICAgICBpZighX2ljb25fc2l6ZV9zdHlsZSlcclxuICAgICAgICAgICAgX2ljb25fc2l6ZV9zdHlsZSA9ICQuc3Bhd24oXCJzdHlsZVwiKS5hcHBlbmRUbygkKFwiI3N0eWxlXCIpKTtcclxuXHJcbiAgICAgICAgX2ljb25fc2l6ZV9zdHlsZS50ZXh0KFwiXFxuXCIgK1xyXG4gICAgICAgICAgICBcIi5tZXNzYWdlID4gLmVtb2ppIHtcXG5cIiArXHJcbiAgICAgICAgICAgIFwiICBoZWlnaHQ6IFwiICsgc2l6ZSArIFwiIWltcG9ydGFudDtcXG5cIiArXHJcbiAgICAgICAgICAgIFwiICB3aWR0aDogXCIgKyBzaXplICsgXCIhaW1wb3J0YW50O1xcblwiICtcclxuICAgICAgICAgICAgXCJ9XFxuXCJcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgICAgIG5hbWU6IFwiaWNvbiBzaXplIGluaXRcIixcclxuICAgICAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgICAgICBNZXNzYWdlSGVscGVyLnNldF9pY29uX3NpemUoKHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0lDT05fU0laRSkgLyAxMDApLnRvRml4ZWQoMikgKyBcImVtXCIpO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgcHJpb3JpdHk6IDEwXHJcbiAgICB9KTtcclxufSIsIi8vRklYTUU6IE1vdmUgdGhpcyBzaGl0IG91dCBvZiB0aGlzIGZpbGUhXHJcbmV4cG9ydCBuYW1lc3BhY2UgY29ubmVjdGlvbl9sb2cge1xyXG4gICAgLy9UT0RPOiBTYXZlIHBhc3N3b3JkIGRhdGFcclxuICAgIGV4cG9ydCB0eXBlIENvbm5lY3Rpb25EYXRhID0ge1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBpY29uX2lkOiBudW1iZXI7XHJcbiAgICAgICAgY291bnRyeTogc3RyaW5nO1xyXG4gICAgICAgIGNsaWVudHNfb25saW5lOiBudW1iZXI7XHJcbiAgICAgICAgY2xpZW50c190b3RhbDogbnVtYmVyO1xyXG5cclxuICAgICAgICBmbGFnX3Bhc3N3b3JkOiBib29sZWFuO1xyXG4gICAgICAgIHBhc3N3b3JkX2hhc2g6IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgdHlwZSBDb25uZWN0aW9uRW50cnkgPSBDb25uZWN0aW9uRGF0YSAmIHtcclxuICAgICAgICBhZGRyZXNzOiB7IGhvc3RuYW1lOiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9LFxyXG4gICAgICAgIHRvdGFsX2Nvbm5lY3Rpb246IG51bWJlcjtcclxuXHJcbiAgICAgICAgZmlyc3RfdGltZXN0YW1wOiBudW1iZXI7XHJcbiAgICAgICAgbGFzdF90aW1lc3RhbXA6IG51bWJlcjtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgX2hpc3Rvcnk6IENvbm5lY3Rpb25FbnRyeVtdID0gW107XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9nX2Nvbm5lY3QoYWRkcmVzczogeyBob3N0bmFtZTogc3RyaW5nOyBwb3J0OiBudW1iZXIgfSkge1xyXG4gICAgICAgIGxldCBlbnRyeSA9IF9oaXN0b3J5LmZpbmQoZSA9PiBlLmFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSA9PSBhZGRyZXNzLmhvc3RuYW1lLnRvTG93ZXJDYXNlKCkgJiYgZS5hZGRyZXNzLnBvcnQgPT0gYWRkcmVzcy5wb3J0KTtcclxuICAgICAgICBpZighZW50cnkpIHtcclxuICAgICAgICAgICAgX2hpc3RvcnkucHVzaChlbnRyeSA9IHtcclxuICAgICAgICAgICAgICAgIGxhc3RfdGltZXN0YW1wOiBEYXRlLm5vdygpLFxyXG4gICAgICAgICAgICAgICAgZmlyc3RfdGltZXN0YW1wOiBEYXRlLm5vdygpLFxyXG4gICAgICAgICAgICAgICAgYWRkcmVzczogYWRkcmVzcyxcclxuICAgICAgICAgICAgICAgIGNsaWVudHNfb25saW5lOiAwLFxyXG4gICAgICAgICAgICAgICAgY2xpZW50c190b3RhbDogMCxcclxuICAgICAgICAgICAgICAgIGNvdW50cnk6ICd1bmtub3duJyxcclxuICAgICAgICAgICAgICAgIG5hbWU6ICdVbmtub3duJyxcclxuICAgICAgICAgICAgICAgIGljb25faWQ6IDAsXHJcbiAgICAgICAgICAgICAgICB0b3RhbF9jb25uZWN0aW9uOiAwLFxyXG5cclxuICAgICAgICAgICAgICAgIGZsYWdfcGFzc3dvcmQ6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgcGFzc3dvcmRfaGFzaDogdW5kZWZpbmVkXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbnRyeS5sYXN0X3RpbWVzdGFtcCA9IERhdGUubm93KCk7XHJcbiAgICAgICAgZW50cnkudG90YWxfY29ubmVjdGlvbisrO1xyXG4gICAgICAgIF9zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZV9hZGRyZXNzX2luZm8oYWRkcmVzczogeyBob3N0bmFtZTogc3RyaW5nOyBwb3J0OiBudW1iZXIgfSwgZGF0YTogQ29ubmVjdGlvbkRhdGEpIHtcclxuICAgICAgICBfaGlzdG9yeS5maWx0ZXIoZSA9PiBlLmFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSA9PSBhZGRyZXNzLmhvc3RuYW1lLnRvTG93ZXJDYXNlKCkgJiYgZS5hZGRyZXNzLnBvcnQgPT0gYWRkcmVzcy5wb3J0KS5mb3JFYWNoKGUgPT4ge1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGRhdGEpKSB7XHJcbiAgICAgICAgICAgICAgICBpZih0eXBlb2YoZGF0YVtrZXldKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGVba2V5XSA9IGRhdGFba2V5XTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIF9zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZV9hZGRyZXNzX3Bhc3N3b3JkKGFkZHJlc3M6IHsgaG9zdG5hbWU6IHN0cmluZzsgcG9ydDogbnVtYmVyIH0sIHBhc3N3b3JkX2hhc2g6IHN0cmluZykge1xyXG4gICAgICAgIF9oaXN0b3J5LmZpbHRlcihlID0+IGUuYWRkcmVzcy5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpID09IGFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSAmJiBlLmFkZHJlc3MucG9ydCA9PSBhZGRyZXNzLnBvcnQpLmZvckVhY2goZSA9PiB7XHJcbiAgICAgICAgICAgIGUucGFzc3dvcmRfaGFzaCA9IHBhc3N3b3JkX2hhc2g7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgX3NhdmUoKTtcclxuICAgIH1cclxuXHJcbiAgICBmdW5jdGlvbiBfc2F2ZSgpIHtcclxuICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfSElTVE9SWSwgSlNPTi5zdHJpbmdpZnkoX2hpc3RvcnkpKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gaGlzdG9yeSgpIDogQ29ubmVjdGlvbkVudHJ5W10ge1xyXG4gICAgICAgIHJldHVybiBfaGlzdG9yeS5zb3J0KChhLCBiKSA9PiBiLmxhc3RfdGltZXN0YW1wIC0gYS5sYXN0X3RpbWVzdGFtcCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlbGV0ZV9lbnRyeShhZGRyZXNzOiB7IGhvc3RuYW1lOiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9KSB7XHJcbiAgICAgICAgX2hpc3RvcnkgPSBfaGlzdG9yeS5maWx0ZXIoZSA9PiAhKGUuYWRkcmVzcy5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpID09IGFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSAmJiBlLmFkZHJlc3MucG9ydCA9PSBhZGRyZXNzLnBvcnQpKTtcclxuICAgICAgICBfc2F2ZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgICAgIG5hbWU6ICdjb25uZWN0aW9uIGhpc3RvcnkgbG9hZCcsXHJcbiAgICAgICAgcHJpb3JpdHk6IDEsXHJcbiAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICAgICAgX2hpc3RvcnkgPSBbXTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIF9oaXN0b3J5ID0gSlNPTi5wYXJzZShzZXR0aW5ncy5nbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfSElTVE9SWSwgXCJbXVwiKSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkNMSUVOVCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBjb25uZWN0aW9uIGhpc3Rvcnk6IHt9XCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9KTtcclxufVxyXG5cclxuZXhwb3J0IG5hbWVzcGFjZSBNb2RhbHMge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHNwYXduQ29ubmVjdE1vZGFsKG9wdGlvbnM6IHtcclxuICAgICAgICBkZWZhdWx0X2Nvbm5lY3RfbmV3X3RhYj86IGJvb2xlYW4gLyogZGVmYXVsdCBmYWxzZSAqL1xyXG4gICAgfSwgZGVmYXVsdEhvc3Q6IHsgdXJsOiBzdHJpbmcsIGVuZm9yY2U6IGJvb2xlYW59ID0geyB1cmw6IFwidHMuVGVhU3BlYWsuZGVcIiwgZW5mb3JjZTogZmFsc2V9LCBjb25uZWN0X3Byb2ZpbGU/OiB7IHByb2ZpbGU6IHByb2ZpbGVzLkNvbm5lY3Rpb25Qcm9maWxlLCBlbmZvcmNlOiBib29sZWFufSkge1xyXG4gICAgICAgIGxldCBzZWxlY3RlZF9wcm9maWxlOiBwcm9maWxlcy5Db25uZWN0aW9uUHJvZmlsZTtcclxuXHJcbiAgICAgICAgY29uc3QgcmFuZG9tX2lkID0gKCgpID0+IHtcclxuICAgICAgICAgICAgY29uc3QgYXJyYXkgPSBuZXcgVWludDMyQXJyYXkoMTApO1xyXG4gICAgICAgICAgICB3aW5kb3cuY3J5cHRvLmdldFJhbmRvbVZhbHVlcyhhcnJheSk7XHJcbiAgICAgICAgICAgIHJldHVybiBhcnJheS5qb2luKFwiXCIpO1xyXG4gICAgICAgIH0pKCk7XHJcblxyXG4gICAgICAgIGNvbnN0IG1vZGFsID0gY3JlYXRlTW9kYWwoe1xyXG4gICAgICAgICAgICBoZWFkZXI6IHRyKFwiQ29ubmVjdCB0byBhIHNlcnZlclwiKSxcclxuICAgICAgICAgICAgYm9keTogJChcIiN0bXBsX2Nvbm5lY3RcIikucmVuZGVyVGFnKHtcclxuICAgICAgICAgICAgICAgIGNsaWVudDogbmF0aXZlX2NsaWVudCxcclxuICAgICAgICAgICAgICAgIGZvcnVtX3BhdGg6IHNldHRpbmdzLnN0YXRpYyhcImZvcnVtX3BhdGhcIiksXHJcbiAgICAgICAgICAgICAgICBwYXNzd29yZF9pZDogcmFuZG9tX2lkLFxyXG4gICAgICAgICAgICAgICAgbXVsdGlfdGFiOiAhc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfRElTQUJMRV9NVUxUSV9TRVNTSU9OKSxcclxuICAgICAgICAgICAgICAgIGRlZmF1bHRfY29ubmVjdF9uZXdfdGFiOiB0eXBlb2Yob3B0aW9ucy5kZWZhdWx0X2Nvbm5lY3RfbmV3X3RhYikgPT09IFwiYm9vbGVhblwiICYmIG9wdGlvbnMuZGVmYXVsdF9jb25uZWN0X25ld190YWJcclxuICAgICAgICAgICAgfSksXHJcbiAgICAgICAgICAgIGZvb3RlcjogKCkgPT4gdW5kZWZpbmVkLFxyXG4gICAgICAgICAgICBtaW5fd2lkdGg6IFwiMjhlbVwiXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtY29ubmVjdFwiKTtcclxuXHJcbiAgICAgICAgLyogc2VydmVyIGxpc3QgdG9nZ2xlICovXHJcbiAgICAgICAge1xyXG4gICAgICAgICAgICBjb25zdCBjb250YWluZXJfbGFzdF9zZXJ2ZXJzID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci1sYXN0LXNlcnZlcnNcIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGJ1dHRvbiA9IG1vZGFsLmh0bWxUYWcuZmluZChcIi5idXR0b24tdG9nZ2xlLWxhc3Qtc2VydmVyc1wiKTtcclxuICAgICAgICAgICAgY29uc3Qgc2V0X3Nob3cgPSBzaG93biA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb250YWluZXJfbGFzdF9zZXJ2ZXJzLnRvZ2dsZUNsYXNzKCdzaG93bicsIHNob3duKTtcclxuICAgICAgICAgICAgICAgIGJ1dHRvbi5maW5kKFwiLmFycm93XCIpLnRvZ2dsZUNsYXNzKCdkb3duJywgc2hvd24pLnRvZ2dsZUNsYXNzKCd1cCcsICFzaG93bik7XHJcbiAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoXCJjb25uZWN0X3Nob3dfbGFzdF9zZXJ2ZXJzXCIsIHNob3duKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgYnV0dG9uLm9uKCdjbGljaycsIGV2ZW50ID0+IHtcclxuICAgICAgICAgICAgICAgIHNldF9zaG93KCFjb250YWluZXJfbGFzdF9zZXJ2ZXJzLmhhc0NsYXNzKFwic2hvd25cIikpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgc2V0X3Nob3coc2V0dGluZ3Muc3RhdGljX2dsb2JhbChcImNvbm5lY3Rfc2hvd19sYXN0X3NlcnZlcnNcIiwgZmFsc2UpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IGFwcGx5ID0gKGhlYWRlciwgYm9keSwgZm9vdGVyKSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IGNvbnRhaW5lcl9sYXN0X3NlcnZlcl9ib2R5ID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci1sYXN0LXNlcnZlcnMgLnRhYmxlIC5ib2R5XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBjb250YWluZXJfZW1wdHkgPSBjb250YWluZXJfbGFzdF9zZXJ2ZXJfYm9keS5maW5kKFwiLmJvZHktZW1wdHlcIik7XHJcbiAgICAgICAgICAgIGxldCBjdXJyZW50X2Nvbm5lY3RfZGF0YTogY29ubmVjdGlvbl9sb2cuQ29ubmVjdGlvbkVudHJ5O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX2Nvbm5lY3QgPSBmb290ZXIuZmluZChcIi5idXR0b24tY29ubmVjdFwiKTtcclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX2Nvbm5lY3RfdGFiID0gZm9vdGVyLmZpbmQoXCIuYnV0dG9uLWNvbm5lY3QtbmV3LXRhYlwiKTtcclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX21hbmFnZSA9IGJvZHkuZmluZChcIi5idXR0b24tbWFuYWdlLXByb2ZpbGVzXCIpO1xyXG5cclxuICAgICAgICAgICAgY29uc3QgaW5wdXRfcHJvZmlsZSA9IGJvZHkuZmluZChcIi5jb250YWluZXItc2VsZWN0LXByb2ZpbGUgc2VsZWN0XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBpbnB1dF9hZGRyZXNzID0gYm9keS5maW5kKFwiLmNvbnRhaW5lci1hZGRyZXNzIGlucHV0XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBpbnB1dF9uaWNrbmFtZSA9IGJvZHkuZmluZChcIi5jb250YWluZXItbmlja25hbWUgaW5wdXRcIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGlucHV0X3Bhc3N3b3JkID0gYm9keS5maW5kKFwiLmNvbnRhaW5lci1wYXNzd29yZCBpbnB1dFwiKTtcclxuXHJcbiAgICAgICAgICAgIGxldCB1cGRhdGVGaWVsZHMgPSAocmVzZXRfY3VycmVudF9kYXRhOiBib29sZWFuKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZihyZXNldF9jdXJyZW50X2RhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICBjdXJyZW50X2Nvbm5lY3RfZGF0YSA9IHVuZGVmaW5lZDtcclxuICAgICAgICAgICAgICAgICAgICBjb250YWluZXJfbGFzdF9zZXJ2ZXJfYm9keS5maW5kKFwiLnNlbGVjdGVkXCIpLnJlbW92ZUNsYXNzKFwic2VsZWN0ZWRcIik7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgbGV0IGFkZHJlc3MgPSBpbnB1dF9hZGRyZXNzLnZhbCgpLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfQUREUkVTUywgYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICBsZXQgZmxhZ19hZGRyZXNzID0gISFhZGRyZXNzLm1hdGNoKFJlZ2V4LklQX1Y0KSB8fCAhIWFkZHJlc3MubWF0Y2goUmVnZXguSVBfVjYpIHx8ICEhYWRkcmVzcy5tYXRjaChSZWdleC5ET01BSU4pO1xyXG5cclxuICAgICAgICAgICAgICAgIGxldCBuaWNrbmFtZSA9IGlucHV0X25pY2tuYW1lLnZhbCgpLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICBpZihuaWNrbmFtZSlcclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfVVNFUk5BTUUsIG5pY2tuYW1lKTtcclxuICAgICAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgICAgICBuaWNrbmFtZSA9IGlucHV0X25pY2tuYW1lLmF0dHIoXCJwbGFjZWhvbGRlclwiKSB8fCBcIlwiO1xyXG4gICAgICAgICAgICAgICAgbGV0IGZsYWdfbmlja25hbWUgPSBuaWNrbmFtZS5sZW5ndGggPj0gMyAmJiBuaWNrbmFtZS5sZW5ndGggPD0gMzI7XHJcblxyXG4gICAgICAgICAgICAgICAgaW5wdXRfYWRkcmVzcy5hdHRyKCdwYXR0ZXJuJywgZmxhZ19hZGRyZXNzID8gbnVsbCA6ICdeW2FdezEwMDB9JCcpLnRvZ2dsZUNsYXNzKCdpcy1pbnZhbGlkJywgIWZsYWdfYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICBpbnB1dF9uaWNrbmFtZS5hdHRyKCdwYXR0ZXJuJywgZmxhZ19uaWNrbmFtZSA/IG51bGwgOiAnXlthXXsxMDAwfSQnKS50b2dnbGVDbGFzcygnaXMtaW52YWxpZCcsICFmbGFnX25pY2tuYW1lKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBmbGFnX2Rpc2FibGVkID0gIWZsYWdfbmlja25hbWUgfHwgIWZsYWdfYWRkcmVzcyB8fCAhc2VsZWN0ZWRfcHJvZmlsZSB8fCAhc2VsZWN0ZWRfcHJvZmlsZS52YWxpZCgpO1xyXG4gICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3QucHJvcChcImRpc2FibGVkXCIsIGZsYWdfZGlzYWJsZWQpO1xyXG4gICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3RfdGFiLnByb3AoXCJkaXNhYmxlZFwiLCBmbGFnX2Rpc2FibGVkKTtcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIGlucHV0X2FkZHJlc3MudmFsKGRlZmF1bHRIb3N0LmVuZm9yY2UgPyBkZWZhdWx0SG9zdC51cmwgOiBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX0FERFJFU1MsIGRlZmF1bHRIb3N0LnVybCkpO1xyXG4gICAgICAgICAgICBpbnB1dF9hZGRyZXNzXHJcbiAgICAgICAgICAgICAgICAub24oXCJrZXl1cFwiLCAoKSA9PiB1cGRhdGVGaWVsZHModHJ1ZSkpXHJcbiAgICAgICAgICAgICAgICAub24oJ2tleWRvd24nLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoZXZlbnQua2V5Q29kZSA9PSBLZXlDb2RlLktFWV9FTlRFUiAmJiAhZXZlbnQuc2hpZnRLZXkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbl9jb25uZWN0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgYnV0dG9uX21hbmFnZS5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBtb2RhbCA9IE1vZGFscy5zcGF3blNldHRpbmdzTW9kYWwoXCJpZGVudGl0eS1wcm9maWxlc1wiKTtcclxuICAgICAgICAgICAgICAgIG1vZGFsLmNsb3NlX2xpc3RlbmVyLnB1c2goKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGlucHV0X3Byb2ZpbGUudHJpZ2dlcignY2hhbmdlJyk7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgIC8qIENvbm5lY3QgUHJvZmlsZXMgKi9cclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHByb2ZpbGUgb2YgcHJvZmlsZXMucHJvZmlsZXMoKSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGlucHV0X3Byb2ZpbGUuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwib3B0aW9uXCIpLnRleHQocHJvZmlsZS5wcm9maWxlX25hbWUpLnZhbChwcm9maWxlLmlkKVxyXG4gICAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgaW5wdXRfcHJvZmlsZS5vbignY2hhbmdlJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkX3Byb2ZpbGUgPSBwcm9maWxlcy5maW5kX3Byb2ZpbGUoaW5wdXRfcHJvZmlsZS52YWwoKSBhcyBzdHJpbmcpIHx8IHByb2ZpbGVzLmRlZmF1bHRfcHJvZmlsZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgc2V0dGluZ3MuY2hhbmdlR2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX1VTRVJOQU1FLCB1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9uaWNrbmFtZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLmF0dHIoJ3BsYWNlaG9sZGVyJywgc2VsZWN0ZWRfcHJvZmlsZS5jb25uZWN0X3VzZXJuYW1lKCkgfHwgXCJBbm90aGVyIFRlYVNwZWFrIHVzZXJcIilcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC52YWwoXCJcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfUFJPRklMRSwgc2VsZWN0ZWRfcHJvZmlsZS5pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgaW5wdXRfcHJvZmlsZS50b2dnbGVDbGFzcyhcImlzLWludmFsaWRcIiwgIXNlbGVjdGVkX3Byb2ZpbGUgfHwgIXNlbGVjdGVkX3Byb2ZpbGUudmFsaWQoKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgdXBkYXRlRmllbGRzKHRydWUpO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICBpbnB1dF9wcm9maWxlLnZhbChjb25uZWN0X3Byb2ZpbGUgJiYgY29ubmVjdF9wcm9maWxlLnByb2ZpbGUgP1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3RfcHJvZmlsZS5wcm9maWxlLmlkIDpcclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX1BST0ZJTEUsIFwiZGVmYXVsdFwiKVxyXG4gICAgICAgICAgICAgICAgKS50cmlnZ2VyKCdjaGFuZ2UnKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgY29uc3QgbGFzdF9uaWNrbmFtZSA9IHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfVVNFUk5BTUUsIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgIGlmKGxhc3Rfbmlja25hbWUpIC8qIHJlc3RvcmUgKi9cclxuICAgICAgICAgICAgICAgIHNldHRpbmdzLmNoYW5nZUdsb2JhbChTZXR0aW5ncy5LRVlfQ09OTkVDVF9VU0VSTkFNRSwgbGFzdF9uaWNrbmFtZSk7XHJcblxyXG4gICAgICAgICAgICBpbnB1dF9uaWNrbmFtZS52YWwobGFzdF9uaWNrbmFtZSk7XHJcbiAgICAgICAgICAgIGlucHV0X25pY2tuYW1lLm9uKFwia2V5dXBcIiwgKCkgPT4gdXBkYXRlRmllbGRzKHRydWUpKTtcclxuICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB1cGRhdGVGaWVsZHMoZmFsc2UpLCAxMDApO1xyXG5cclxuICAgICAgICAgICAgY29uc3Qgc2VydmVyX2FkZHJlc3MgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBsZXQgYWRkcmVzcyA9IGlucHV0X2FkZHJlc3MudmFsKCkudG9TdHJpbmcoKTtcclxuICAgICAgICAgICAgICAgIGlmKGFkZHJlc3MubWF0Y2goUmVnZXguSVBfVjYpICYmICFhZGRyZXNzLnN0YXJ0c1dpdGgoXCJbXCIpKVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIltcIiArIGFkZHJlc3MgKyBcIl1cIjtcclxuICAgICAgICAgICAgICAgIHJldHVybiBhZGRyZXNzO1xyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICBidXR0b25fY29ubmVjdC5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG5cclxuICAgICAgICAgICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgICAgICAgICAgaWYoY29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uc3RhcnRDb25uZWN0aW9uKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50X2Nvbm5lY3RfZGF0YSA/IGN1cnJlbnRfY29ubmVjdF9kYXRhLmFkZHJlc3MuaG9zdG5hbWUgKyBcIjpcIiArIGN1cnJlbnRfY29ubmVjdF9kYXRhLmFkZHJlc3MucG9ydCA6IHNlcnZlcl9hZGRyZXNzKCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkX3Byb2ZpbGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5pY2tuYW1lOiBpbnB1dF9uaWNrbmFtZS52YWwoKS50b1N0cmluZygpIHx8ICBpbnB1dF9uaWNrbmFtZS5hdHRyKFwicGxhY2Vob2xkZXJcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzd29yZDogKGN1cnJlbnRfY29ubmVjdF9kYXRhICYmIGN1cnJlbnRfY29ubmVjdF9kYXRhLnBhc3N3b3JkX2hhc2gpID8ge3Bhc3N3b3JkOiBjdXJyZW50X2Nvbm5lY3RfZGF0YS5wYXNzd29yZF9oYXNoLCBoYXNoZWQ6IHRydWV9IDoge3Bhc3N3b3JkOiBpbnB1dF9wYXNzd29yZC52YWwoKS50b1N0cmluZygpLCBoYXNoZWQ6IGZhbHNlfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3RfdGFiLnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICBidXR0b25fY29ubmVjdF90YWIub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgbW9kYWwuY2xvc2UoKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBjb25uZWN0aW9uID0gc2VydmVyX2Nvbm5lY3Rpb25zLnNwYXduX3NlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXIoKTtcclxuICAgICAgICAgICAgICAgIHNlcnZlcl9jb25uZWN0aW9ucy5zZXRfYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcihjb25uZWN0aW9uKTtcclxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uc3RhcnRDb25uZWN0aW9uKFxyXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfY29ubmVjdF9kYXRhID8gY3VycmVudF9jb25uZWN0X2RhdGEuYWRkcmVzcy5ob3N0bmFtZSArIFwiOlwiICsgY3VycmVudF9jb25uZWN0X2RhdGEuYWRkcmVzcy5wb3J0IDogIHNlcnZlcl9hZGRyZXNzKCksXHJcbiAgICAgICAgICAgICAgICAgICAgc2VsZWN0ZWRfcHJvZmlsZSxcclxuICAgICAgICAgICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbmlja25hbWU6IGlucHV0X25pY2tuYW1lLnZhbCgpLnRvU3RyaW5nKCkgfHwgIGlucHV0X25pY2tuYW1lLmF0dHIoXCJwbGFjZWhvbGRlclwiKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6IChjdXJyZW50X2Nvbm5lY3RfZGF0YSAmJiBjdXJyZW50X2Nvbm5lY3RfZGF0YS5wYXNzd29yZF9oYXNoKSA/IHtwYXNzd29yZDogY3VycmVudF9jb25uZWN0X2RhdGEucGFzc3dvcmRfaGFzaCwgaGFzaGVkOiB0cnVlfSA6IHtwYXNzd29yZDogaW5wdXRfcGFzc3dvcmQudmFsKCkudG9TdHJpbmcoKSwgaGFzaGVkOiBmYWxzZX1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9KTtcclxuXHJcblxyXG4gICAgICAgICAgICAvKiBjb25uZWN0IGhpc3Rvcnkgc2hvdyAqL1xyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YgY29ubmVjdGlvbl9sb2cuaGlzdG9yeSgpLnNsaWNlKDAsIDEwKSkge1xyXG4gICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJyb3dcIikuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGRlbGV0ZVwiKS5hcHBlbmQoJC5zcGF3bihcImRpdlwiKS5hZGRDbGFzcyhcImljb25fZW0gY2xpZW50LWRlbGV0ZVwiKSkub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByb3cgPSAkKGV2ZW50LnRhcmdldCkucGFyZW50cygnLnJvdycpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93LmhpZGUoMjUwLCAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93LmRldGFjaCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25uZWN0aW9uX2xvZy5kZWxldGVfZW50cnkoZW50cnkuYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250YWluZXJfZW1wdHkudG9nZ2xlKGNvbnRhaW5lcl9sYXN0X3NlcnZlcl9ib2R5LmNoaWxkcmVuKCkubGVuZ3RoID4gMSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICAgICAgKS5hcHBlbmQoXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb2x1bW4gbmFtZVwiKS5hcHBlbmQoW1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgSWNvbk1hbmFnZXIuZ2VuZXJhdGVfdGFnKEljb25NYW5hZ2VyLmxvYWRfY2FjaGVkX2ljb24oZW50cnkuaWNvbl9pZCkpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImFcIikudGV4dChlbnRyeS5uYW1lKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBdKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGFkZHJlc3NcIikudGV4dChlbnRyeS5hZGRyZXNzLmhvc3RuYW1lICsgKGVudHJ5LmFkZHJlc3MucG9ydCAhPSA5OTg3ID8gKFwiOlwiICsgZW50cnkuYWRkcmVzcy5wb3J0KSA6IFwiXCIpKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIHBhc3N3b3JkXCIpLnRleHQoZW50cnkuZmxhZ19wYXNzd29yZCA/IHRyKFwiWWVzXCIpIDogdHIoXCJOb1wiKSlcclxuICAgICAgICAgICAgICAgICAgICApLmFwcGVuZChcclxuICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImRpdlwiKS5hZGRDbGFzcyhcImNvbHVtbiBjb3VudHJ5LW5hbWVcIikuYXBwZW5kKFtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb3VudHJ5IGZsYWctXCIgKyBlbnRyeS5jb3VudHJ5LnRvTG93ZXJDYXNlKCkpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImFcIikudGV4dChpMThuLmNvdW50cnlfbmFtZShlbnRyeS5jb3VudHJ5LCB0cihcIkdsb2JhbFwiKSkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIF0pXHJcbiAgICAgICAgICAgICAgICAgICAgKS5hcHBlbmQoXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb2x1bW4gY2xpZW50c1wiKS50ZXh0KGVudHJ5LmNsaWVudHNfb25saW5lICsgXCIvXCIgKyBlbnRyeS5jbGllbnRzX3RvdGFsKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGNvbm5lY3Rpb25zXCIpLnRleHQoZW50cnkudG90YWxfY29ubmVjdGlvbiArIFwiXCIpXHJcbiAgICAgICAgICAgICAgICAgICAgKS5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGV2ZW50LmlzRGVmYXVsdFByZXZlbnRlZCgpKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudF9jb25uZWN0X2RhdGEgPSBlbnRyeTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGFpbmVyX2xhc3Rfc2VydmVyX2JvZHkuZmluZChcIi5zZWxlY3RlZFwiKS5yZW1vdmVDbGFzcyhcInNlbGVjdGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAkKGV2ZW50LnRhcmdldCkucGFyZW50KCcucm93JykuYWRkQ2xhc3MoJ3NlbGVjdGVkJyk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9hZGRyZXNzLnZhbChlbnRyeS5hZGRyZXNzLmhvc3RuYW1lICsgKGVudHJ5LmFkZHJlc3MucG9ydCAhPSA5OTg3ID8gKFwiOlwiICsgZW50cnkuYWRkcmVzcy5wb3J0KSA6IFwiXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXRfcGFzc3dvcmQudmFsKGVudHJ5LmZsYWdfcGFzc3dvcmQgJiYgZW50cnkucGFzc3dvcmRfaGFzaCA/IFwiV29sdmVyaW5ERVYgWWVhaHIhXCIgOiBcIlwiKS50cmlnZ2VyKCdjaGFuZ2UnKTtcclxuICAgICAgICAgICAgICAgICAgICB9KS5vbignZGJsY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfY29ubmVjdF9kYXRhID0gZW50cnk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbl9jb25uZWN0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkuYXBwZW5kVG8oY29udGFpbmVyX2xhc3Rfc2VydmVyX2JvZHkpO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRhaW5lcl9lbXB0eS50b2dnbGUoZmFsc2UpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfTtcclxuICAgICAgICBhcHBseShtb2RhbC5odG1sVGFnLCBtb2RhbC5odG1sVGFnLCBtb2RhbC5odG1sVGFnKTtcclxuXHJcbiAgICAgICAgbW9kYWwub3BlbigpO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY29uc3QgUmVnZXggPSB7XHJcbiAgICAgICAgLy9ET01BSU48OnBvcnQ+XHJcbiAgICAgICAgRE9NQUlOOiAvXihsb2NhbGhvc3R8KCgoW2EtekEtWjAtOV8tXXswLDYzfVxcLil7MCwyNTN9KT9bYS16QS1aMC05Xy1dezAsNjN9XFwuW2EtekEtWl17Miw2NH0pKSh8Oig2NTUzWzAtNV18NjU1WzAtMl1bMC05XXw2NVswLTRdWzAtOV17Mn18NlswLTRdWzAtOV17M318WzAtNV0/WzAtOV17MSw0Nn0pKSQvLFxyXG4gICAgICAgIC8vSVA8OnBvcnQ+XHJcbiAgICAgICAgSVBfVjQ6IC8oXigoMjVbMC01XXwyWzAtNF1bMC05XXxbMDFdP1swLTldWzAtOV0/KVxcLil7M30oMjVbMC01XXwyWzAtNF1bMC05XXxbMDFdP1swLTldWzAtOV0/KSkofDooNjU1M1swLTVdfDY1NVswLTJdWzAtOV18NjVbMC00XVswLTldezJ9fDZbMC00XVswLTldezN9fFswLTVdP1swLTldezEsNH0pKSQvLFxyXG4gICAgICAgIElQX1Y2OiAvKChbMC05YS1mQS1GXXsxLDR9Oil7Nyw3fVswLTlhLWZBLUZdezEsNH18KFswLTlhLWZBLUZdezEsNH06KXsxLDd9OnwoWzAtOWEtZkEtRl17MSw0fTopezEsNn06WzAtOWEtZkEtRl17MSw0fXwoWzAtOWEtZkEtRl17MSw0fTopezEsNX0oOlswLTlhLWZBLUZdezEsNH0pezEsMn18KFswLTlhLWZBLUZdezEsNH06KXsxLDR9KDpbMC05YS1mQS1GXXsxLDR9KXsxLDN9fChbMC05YS1mQS1GXXsxLDR9Oil7MSwzfSg6WzAtOWEtZkEtRl17MSw0fSl7MSw0fXwoWzAtOWEtZkEtRl17MSw0fTopezEsMn0oOlswLTlhLWZBLUZdezEsNH0pezEsNX18WzAtOWEtZkEtRl17MSw0fTooKDpbMC05YS1mQS1GXXsxLDR9KXsxLDZ9KXw6KCg6WzAtOWEtZkEtRl17MSw0fSl7MSw3fXw6KXxmZTgwOig6WzAtOWEtZkEtRl17MCw0fSl7MCw0fSVbMC05YS16QS1aXXsxLH18OjooZmZmZig6MHsxLDR9KXswLDF9Oil7MCwxfSgoMjVbMC01XXwoMlswLTRdfDF7MCwxfVswLTldKXswLDF9WzAtOV0pXFwuKXszLDN9KDI1WzAtNV18KDJbMC00XXwxezAsMX1bMC05XSl7MCwxfVswLTldKXwoWzAtOWEtZkEtRl17MSw0fTopezEsNH06KCgyNVswLTVdfCgyWzAtNF18MXswLDF9WzAtOV0pezAsMX1bMC05XSlcXC4pezMsM30oMjVbMC01XXwoMlswLTRdfDF7MCwxfVswLTldKXswLDF9WzAtOV0pKS8sXHJcbiAgICAgICAgSVA6IC9eKChbMC05XXxbMS05XVswLTldfDFbMC05XXsyfXwyWzAtNF1bMC05XXwyNVswLTVdKVxcLil7M30oWzAtOV18WzEtOV1bMC05XXwxWzAtOV17Mn18MlswLTRdWzAtOV18MjVbMC01XSkkfF4oKFthLXpBLVpdfFthLXpBLVpdW2EtekEtWjAtOVxcLV0qW2EtekEtWjAtOV0pXFwuKSooW0EtWmEtel18W0EtWmEtel1bQS1aYS16MC05XFwtXSpbQS1aYS16MC05XSkkfF5cXHMqKCgoWzAtOUEtRmEtZl17MSw0fTopezd9KFswLTlBLUZhLWZdezEsNH18OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezZ9KDpbMC05QS1GYS1mXXsxLDR9fCgoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NX0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwyfSl8OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NH0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwzfSl8KCg6WzAtOUEtRmEtZl17MSw0fSk/OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KSl8OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezN9KCgoOlswLTlBLUZhLWZdezEsNH0pezEsNH0pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsMn06KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7Mn0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSw1fSl8KCg6WzAtOUEtRmEtZl17MSw0fSl7MCwzfTooKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKShcXC4oMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKXszfSkpfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXsxfSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDZ9KXwoKDpbMC05QS1GYS1mXXsxLDR9KXswLDR9OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KSl8OikpfCg6KCgoOlswLTlBLUZhLWZdezEsNH0pezEsN30pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsNX06KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSkpKCUuKyk/XFxzKiQvLFxyXG4gICAgfTtcclxufSJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 71232377..e1cc94f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,14 +32,20 @@ } }, "@types/jquery": { - "version": "3.3.32", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.32.tgz", - "integrity": "sha512-UKoof2mnV/X1/Ix2g+V2Ny5sgHjV8nK/UJbiYxuo4zPwzGyFlZ/mp4KaePb2VqQrqJctmcDQNA57buU84/2uIw==", + "version": "3.3.34", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.34.tgz", + "integrity": "sha512-lW9vsVL53Xu/Nj4gi2hNmHGc4u3KKghjqTkAlO0kF5GIOPxbqqnQpgqJBzmn3yXLrPqHb6cmNJ6URnS23Vtvbg==", "dev": true, "requires": { "@types/sizzle": "*" } }, + "@types/lodash": { + "version": "4.14.149", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", + "dev": true + }, "@types/moment": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@types/moment/-/moment-2.13.0.tgz", @@ -55,6 +61,31 @@ "integrity": "sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A==", "dev": true }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.9.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.26.tgz", + "integrity": "sha512-dGuSM+B0Pq1MKXYUMlUQWeS6Jj9IhSAUf9v8Ikaimj+YhkBcQrihWBkmyEhK/1fzkJTwZQkhZp5YhmWa2CH+Rw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.9.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.5.tgz", + "integrity": "sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/sha256": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@types/sha256/-/sha256-0.2.0.tgz", @@ -80,6 +111,235 @@ "@types/node": "*" } }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -135,12 +395,28 @@ "buffer-equal": "^1.0.0" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -186,6 +462,12 @@ "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -252,6 +534,59 @@ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -276,6 +611,12 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -285,12 +626,30 @@ "async-done": "^1.2.2" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true + }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -381,6 +740,27 @@ } } }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -397,6 +777,27 @@ "file-uri-to-path": "1.0.0" } }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -418,6 +819,94 @@ "repeat-element": "^1.1.2" } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -430,6 +919,64 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -461,6 +1008,30 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -491,6 +1062,31 @@ "readdirp": "^2.0.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -561,6 +1157,25 @@ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, "clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", @@ -616,18 +1231,48 @@ "object-visit": "^1.0.0" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -652,6 +1297,24 @@ "typedarray": "^0.0.6" } }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, "convert-hex": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/convert-hex/-/convert-hex-0.1.0.tgz", @@ -673,6 +1336,20 @@ "integrity": "sha1-ec5BqbsNA7z3LNxqjzxW+7xkQQo=", "dev": true }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -695,6 +1372,106 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", + "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.23", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.1.1", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.2", + "schema-utils": "^2.6.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, "css-tree": { "version": "1.0.0-alpha.29", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", @@ -713,6 +1490,12 @@ } } }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, "csso": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", @@ -742,6 +1525,27 @@ } } }, + "csstype": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", + "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -752,6 +1556,15 @@ "type": "^1.0.1" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -858,12 +1671,51 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -886,6 +1738,43 @@ "object.defaults": "^1.1.0" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -895,6 +1784,26 @@ "once": "^1.4.0" } }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -954,6 +1863,77 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -1034,6 +2014,12 @@ "is-extglob": "^1.0.0" } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -1046,6 +2032,24 @@ "time-stamp": "^1.0.0" } }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1072,6 +2076,17 @@ "repeat-string": "^1.5.2" } }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", @@ -1429,6 +2444,23 @@ "for-in": "^1.0.1" } }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -1438,6 +2470,16 @@ "map-cache": "^0.2.2" } }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -1459,6 +2501,18 @@ "through2": "^2.0.3" } }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2023,24 +3077,97 @@ } } }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -2507,6 +3634,17 @@ "which": "^1.2.14" } }, + "globule": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", + "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.12", + "minimatch": "~3.0.2" + } + }, "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", @@ -2577,6 +3715,22 @@ "glogg": "^1.0.0" } }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -2586,12 +3740,24 @@ "ansi-regex": "^2.0.0" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2652,6 +3818,37 @@ } } }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -2667,6 +3864,87 @@ "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", "dev": true }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2796,6 +4074,12 @@ "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -2829,6 +4113,12 @@ "kind-of": "^3.0.2" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -2867,6 +4157,18 @@ "is-unc-path": "^1.0.0" } }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -2894,6 +4196,12 @@ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2915,12 +4223,76 @@ "isarray": "1.0.0" } }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-base64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", + "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2930,6 +4302,18 @@ "graceful-fs": "^4.1.6" } }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "just-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", @@ -3011,6 +4395,93 @@ "strip-bom": "^2.0.0" } }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -3028,12 +4499,27 @@ } } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -3364,12 +4850,70 @@ "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", "dev": true }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "mdn-data": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", "dev": true }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -3391,6 +4935,16 @@ "regex-cache": "^0.4.2" } }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, "mime-db": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", @@ -3406,6 +4960,49 @@ "mime-db": "1.43.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3421,6 +5018,36 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -3457,6 +5084,20 @@ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", "dev": true }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3473,8 +5114,7 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true + "dev": true }, "nanomatch": { "version": "1.2.13", @@ -3515,12 +5155,125 @@ } } }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-sass": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz", + "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3542,6 +5295,18 @@ "remove-trailing-separator": "^1.0.1" } }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, "now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", @@ -3551,12 +5316,44 @@ "once": "^1.3.2" } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -3730,6 +5527,18 @@ "readable-stream": "^2.0.1" } }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, "os-locale": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", @@ -3739,6 +5548,95 @@ "lcid": "^1.0.0" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, "parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -3789,6 +5687,12 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -3810,6 +5714,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", @@ -3842,6 +5752,25 @@ "pinkie-promise": "^2.0.0" } }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, "picomatch": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", @@ -3869,12 +5798,149 @@ "pinkie": "^2.0.0" } }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + } + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "postcss": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", + "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-value-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", + "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", @@ -3887,12 +5953,66 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -3914,6 +6034,40 @@ "pump": "^2.0.0" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", @@ -3939,6 +6093,51 @@ } } }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", + "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -4271,6 +6470,16 @@ "resolve": "^1.1.6" } }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", @@ -4329,6 +6538,15 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, "replace-ext": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", @@ -4346,6 +6564,34 @@ "remove-trailing-separator": "^1.1.0" } }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4367,6 +6613,15 @@ "path-parse": "^1.0.6" } }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -4377,6 +6632,12 @@ "global-modules": "^1.0.0" } }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, "resolve-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", @@ -4398,6 +6659,34 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -4413,6 +6702,12 @@ "ret": "~0.1.10" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "sass": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/sass/-/sass-1.22.10.tgz", @@ -4544,6 +6839,79 @@ } } }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -4559,6 +6927,12 @@ "sver-compat": "^1.5.0" } }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -4588,6 +6962,22 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "sha256": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/sha256/-/sha256-0.2.0.tgz", @@ -4598,6 +6988,44 @@ "convert-string": "~0.1.0" } }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4712,6 +7140,21 @@ "kind-of": "^3.2.0" } }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4794,6 +7237,32 @@ "extend-shallow": "^3.0.0" } }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -4821,18 +7290,66 @@ } } }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -4871,6 +7388,31 @@ "is-utf8": "^0.2.0" } }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz", + "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.6.4" + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -4887,6 +7429,23 @@ "es6-symbol": "^3.1.1" } }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, "terser": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", @@ -4898,6 +7457,36 @@ "source-map-support": "~0.5.12" } }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -4924,6 +7513,15 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -4934,6 +7532,12 @@ "is-negated-glob": "^1.0.0" } }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -4985,6 +7589,136 @@ "through2": "^2.0.3" } }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, + "ts-loader": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.2.tgz", + "integrity": "sha512-HDo5kXZCBml3EUPcc7RlZOV/JGlLHwppTLEHb3SHnr5V7NXD4klMEkrhJe5wgRbaWsSXi+Y1SIBN/K9B6zWGWQ==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, "ttypescript": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.10.tgz", @@ -4994,6 +7728,21 @@ "resolve": "^1.9.0" } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -5053,6 +7802,30 @@ "set-value": "^2.0.1" } }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, "unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", @@ -5121,24 +7894,80 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "dev": true + }, "v8flags": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", @@ -5164,6 +7993,17 @@ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", "dev": true }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "vinyl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", @@ -5218,6 +8058,12 @@ "vinyl": "^2.0.0" } }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, "wat2wasm": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wat2wasm/-/wat2wasm-1.0.2.tgz", @@ -5236,6 +8082,1015 @@ } } }, + "watchpack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", + "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, + "webpack": { + "version": "4.42.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", + "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-cli": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", + "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -5251,6 +9106,24 @@ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -5279,6 +9152,12 @@ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yargs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", diff --git a/package.json b/package.json index 7339e367..6e2f47f6 100644 --- a/package.json +++ b/package.json @@ -17,29 +17,41 @@ "csso": "csso", "rebuild-structure-web-dev": "php files.php generate web dev", "minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output", - "start": "npm run compile-file-helper && node file.js ndevelop" + "start": "npm run compile-file-helper && node file.js ndevelop", + "build": "webpack --config webpack.config.js", + "watch": "webpack --watch" }, "author": "TeaSpeak (WolverinDEV)", "license": "ISC", "devDependencies": { "@types/emscripten": "^1.38.0", - "@types/jquery": "^3.3.31", + "@types/jquery": "^3.3.34", + "@types/lodash": "^4.14.149", "@types/moment": "^2.13.0", "@types/node": "^12.7.2", + "@types/react-dom": "^16.9.5", "@types/sha256": "^0.2.0", "@types/websocket": "0.0.40", "clean-css": "^4.2.1", + "css-loader": "^3.4.2", "csso-cli": "^2.0.2", + "fs-extra": "latest", "gulp": "^4.0.2", "mime-types": "^2.1.24", + "mini-css-extract-plugin": "^0.9.0", "mkdirp": "^0.5.1", + "node-sass": "^4.13.1", "sass": "1.22.10", + "sass-loader": "^8.0.2", "sha256": "^0.2.0", + "style-loader": "^1.1.3", "terser": "^4.2.1", + "ts-loader": "^6.2.2", "ttypescript": "^1.5.10", "typescript": "3.6.5", "wat2wasm": "^1.0.2", - "fs-extra": "latest" + "webpack": "^4.42.1", + "webpack-cli": "^3.3.11" }, "repository": { "type": "git", @@ -50,6 +62,8 @@ }, "homepage": "https://www.teaspeak.de", "dependencies": { - "@types/fs-extra": "^8.0.1" + "@types/fs-extra": "^8.0.1", + "react": "^16.13.1", + "react-dom": "^16.13.1" } } diff --git a/shared/css/static/channel-tree.scss b/shared/css/static/channel-tree.scss index 14b62dcb..64433d19 100644 --- a/shared/css/static/channel-tree.scss +++ b/shared/css/static/channel-tree.scss @@ -94,96 +94,7 @@ } &.channel { - display: flex; - flex-direction: column; - .container-channel { - position: relative; - - display: flex; - flex-direction: row; - justify-content: stretch; - - width: 100%; - min-height: 16px; - - align-items: center; - cursor: pointer; - - .channel-type { - flex-grow: 0; - flex-shrink: 0; - - margin-right: 2px; - } - - .container-channel-name { - display: flex; - flex-direction: row; - - flex-grow: 1; - flex-shrink: 1; - - justify-content: left; - - max-width: 100%; /* important for the repetitive channel name! */ - overflow-x: hidden; - height: 16px; - - &.align-right { - justify-content: right; - } - - &.align-center, &.align-repetitive { - justify-content: center; - } - - .channel-name { - align-self: center; - color: $channel_tree_entry_text_color; - - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &.align-repetitive { - .channel-name { - text-overflow: clip; - } - } - } - - .icons { - display: flex; - flex-direction: row; - - flex-grow: 0; - flex-shrink: 0; - } - - &.move-selected { - border-bottom: 1px solid black; - } - - .show-channel-normal-only { - display: none; - - &.channel-normal { - display: block; - } - } - - .icon_no_sound { - display: flex; - } - } - - .container-clients { - display: flex; - flex-direction: column; - } } &.client { @@ -272,40 +183,6 @@ } } - &.channel .container-channel, &.client, &.server { - .marker-text-unread { - position: absolute; - left: 0; - top: 0; - bottom: 0; - - width: 1px; - background-color: #a814147F; - - opacity: 1; - - &:before { - content: ''; - position: absolute; - - left: 0; - top: 0; - bottom: 0; - - width: 24px; - - background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */ - background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ - } - - &.hidden { - opacity: 0; - } - - @include transition(opacity $button_hover_animation_time); - } - } } } diff --git a/shared/js/BrowserIPC.ts b/shared/js/BrowserIPC.ts index b7271f6e..a371cba7 100644 --- a/shared/js/BrowserIPC.ts +++ b/shared/js/BrowserIPC.ts @@ -1,8 +1,8 @@ -interface Window { +export interface Window { BroadcastChannel: BroadcastChannel; } -namespace bipc { +export namespace bipc { export interface BroadcastMessage { timestamp: number; receiver: string; diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts index 85c3b8af..d1256c5f 100644 --- a/shared/js/ConnectionHandler.ts +++ b/shared/js/ConnectionHandler.ts @@ -1,6 +1,6 @@ /// /// -/// +/// /// /// /// @@ -8,7 +8,17 @@ /// /// -enum DisconnectReason { +import {ChannelTree} from "./channel-tree/view"; +import {LocalClientEntry} from "./channel-tree/client"; +import {ServerAddress} from "./channel-tree/server"; +import {ChannelEntry} from "./channel-tree/channel"; +import {AbstractServerConnection} from "./connection/ConnectionBase"; +import {PermissionManager} from "./permission/PermissionManager"; +import {GroupManager} from "./permission/GroupManager"; +import {ServerSettings} from "./settings"; +import {Hostbanner} from "./ui/frames/hostbanner"; + +export enum DisconnectReason { HANDLER_DESTROYED, REQUESTED, DNS_FAILED, @@ -28,7 +38,7 @@ enum DisconnectReason { UNKNOWN } -enum ConnectionState { +export enum ConnectionState { UNCONNECTED, CONNECTING, INITIALISING, @@ -36,7 +46,7 @@ enum ConnectionState { DISCONNECTING } -enum ViewReasonId { +export enum ViewReasonId { VREASON_USER_ACTION = 0, VREASON_MOVED = 1, VREASON_SYSTEM = 2, @@ -51,7 +61,7 @@ enum ViewReasonId { VREASON_SERVER_SHUTDOWN = 11 } -interface VoiceStatus { +export interface VoiceStatus { input_hardware: boolean; input_muted: boolean; output_muted: boolean; @@ -68,7 +78,7 @@ interface VoiceStatus { queries_visible: boolean; } -interface ConnectParameters { +export interface ConnectParameters { nickname?: string; channel?: { target: string | number; @@ -79,10 +89,10 @@ interface ConnectParameters { auto_reconnect_attempt?: boolean; } -class ConnectionHandler { +export class ConnectionHandler { channelTree: ChannelTree; - serverConnection: connection.AbstractServerConnection; + serverConnection: AbstractServerConnection; fileManager: FileManager; diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index e20cc336..b20e369a 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -1,21 +1,26 @@ -/// -/// +import {ChannelEntry} from "./channel-tree/channel"; +import {AbstractCommandHandler, ServerCommand} from "./connection/ConnectionBase"; +import {ConnectionHandler} from "./ConnectionHandler"; +import {CommandResult} from "./connection/ServerConnectionDeclaration"; +import {log, LogCategory} from "./log"; +import {ClientEntry} from "./channel-tree/client"; +import {hex} from "./crypto/hex"; -class FileEntry { +export class FileEntry { name: string; datetime: number; type: number; size: number; } -class FileListRequest { +export class FileListRequest { path: string; entries: FileEntry[]; callback: (entries: FileEntry[]) => void; } -namespace transfer { +export namespace transfer { export interface TransferKey { client_transfer_id: number; server_transfer_id: number; @@ -152,7 +157,7 @@ class RequestFileUpload implements transfer.UploadTransfer { } } -class FileManager extends connection.AbstractCommandHandler { +class FileManager extends AbstractCommandHandler { handle: ConnectionHandler; icons: IconManager; avatars: AvatarManager; @@ -191,7 +196,7 @@ class FileManager extends connection.AbstractCommandHandler { this.avatars = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyfilelist": this.notifyFileList(command.arguments); diff --git a/shared/js/MessageFormatter.ts b/shared/js/MessageFormatter.ts index 66bb2d7f..3ef9906c 100644 --- a/shared/js/MessageFormatter.ts +++ b/shared/js/MessageFormatter.ts @@ -1,4 +1,10 @@ -namespace messages.formatter { +import {Settings, settings} from "./settings"; +import {contextmenu} from "./ui/elements/context_menu"; +import {image_preview} from "./ui/frames/image_preview"; +import {guid} from "./crypto/uid"; + +declare const xbbcode; +export namespace messages.formatter { export namespace bbcode { const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]"; const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/; diff --git a/shared/js/PPTListener.ts b/shared/js/PPTListener.ts index b0d32f5d..939963a8 100644 --- a/shared/js/PPTListener.ts +++ b/shared/js/PPTListener.ts @@ -1,4 +1,4 @@ -enum KeyCode { +export enum KeyCode { KEY_CANCEL = 3, KEY_HELP = 6, KEY_BACK_SPACE = 8, @@ -118,7 +118,7 @@ enum KeyCode { KEY_META = 224 } -namespace ppt { +export namespace ppt { export enum EventType { KEY_PRESS, KEY_RELEASE, diff --git a/shared/js/bookmarks.ts b/shared/js/bookmarks.ts index cb12ca5e..60a1268a 100644 --- a/shared/js/bookmarks.ts +++ b/shared/js/bookmarks.ts @@ -1,14 +1,6 @@ -namespace bookmarks { - function guid() { - function s4() { - return Math - .floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); - } +import {profiles} from "./profiles/ConnectionProfile"; +export namespace bookmarks { export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => { const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile(); if(profile.valid()) { diff --git a/shared/js/channel-tree/channel.ts b/shared/js/channel-tree/channel.ts index 1a510f05..52da4cee 100644 --- a/shared/js/channel-tree/channel.ts +++ b/shared/js/channel-tree/channel.ts @@ -1,12 +1,15 @@ /// /// -enum ChannelType { +import {ChannelTree} from "./view"; +import {ClientEntry} from "./client"; + +export enum ChannelType { PERMANENT, SEMI_PERMANENT, TEMPORARY } -namespace ChannelType { +export namespace ChannelType { export function normalize(mode: ChannelType) { let value: string = ChannelType[mode]; value = value.toLowerCase(); @@ -14,13 +17,13 @@ namespace ChannelType { } } -enum ChannelSubscribeMode { +export enum ChannelSubscribeMode { SUBSCRIBED, UNSUBSCRIBED, INHERITED } -class ChannelProperties { +export class ChannelProperties { channel_order: number = 0; channel_name: string = ""; channel_name_phonetic: string = ""; @@ -55,7 +58,7 @@ class ChannelProperties { channel_conversation_history_length: number = -1; } -class ChannelEntry { +export class ChannelEntry { channelTree: ChannelTree; channelId: number; parent?: ChannelEntry; diff --git a/shared/js/channel-tree/client.ts b/shared/js/channel-tree/client.ts index d898f8f7..7010a7a5 100644 --- a/shared/js/channel-tree/client.ts +++ b/shared/js/channel-tree/client.ts @@ -1,8 +1,11 @@ /// -/// -/// +/// +/// -enum ClientType { +import {ChannelEntry} from "./channel"; +import {ChannelTree} from "./view"; + +export enum ClientType { CLIENT_VOICE, CLIENT_QUERY, CLIENT_INTERNAL, @@ -11,7 +14,7 @@ enum ClientType { CLIENT_UNDEFINED } -class ClientProperties { +export class ClientProperties { client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type client_type_exact: ClientType = ClientType.CLIENT_VOICE; @@ -57,7 +60,7 @@ class ClientProperties { client_is_priority_speaker: boolean = false; } -class ClientConnectionInfo { +export class ClientConnectionInfo { connection_bandwidth_received_last_minute_control: number = -1; connection_bandwidth_received_last_minute_keepalive: number = -1; connection_bandwidth_received_last_minute_speech: number = -1; @@ -109,7 +112,7 @@ class ClientConnectionInfo { connection_client_port: number = -1; } -class ClientEntry { +export class ClientEntry { readonly events: events.Registry; protected _clientId: number; @@ -1123,7 +1126,7 @@ class ClientEntry { } } -class LocalClientEntry extends ClientEntry { +export class LocalClientEntry extends ClientEntry { handle: ConnectionHandler; private renaming: boolean; @@ -1232,7 +1235,7 @@ class LocalClientEntry extends ClientEntry { } } -class MusicClientProperties extends ClientProperties { +export class MusicClientProperties extends ClientProperties { player_state: number = 0; player_volume: number = 0; @@ -1264,7 +1267,7 @@ class MusicClientProperties extends ClientProperties { } */ -class SongInfo { +export class SongInfo { song_id: number = 0; song_url: string = ""; song_invoker: number = 0; @@ -1277,7 +1280,7 @@ class SongInfo { song_length: number = 0; } -class MusicClientPlayerInfo extends SongInfo { +export class MusicClientPlayerInfo extends SongInfo { bot_id: number = 0; player_state: number = 0; @@ -1290,7 +1293,7 @@ class MusicClientPlayerInfo extends SongInfo { player_description: string = ""; } -class MusicClientEntry extends ClientEntry { +export class MusicClientEntry extends ClientEntry { private _info_promise: Promise; private _info_promise_age: number = 0; private _info_promise_resolve: any; diff --git a/shared/js/channel-tree/server.ts b/shared/js/channel-tree/server.ts index 598f9d22..2c02ae8a 100644 --- a/shared/js/channel-tree/server.ts +++ b/shared/js/channel-tree/server.ts @@ -1,7 +1,9 @@ /// -/// +/// -class ServerProperties { +import {ChannelTree} from "./view"; + +export class ServerProperties { virtualserver_host: string = ""; virtualserver_port: number = 0; @@ -78,7 +80,7 @@ class ServerProperties { virtualserver_total_bytes_uploaded: number = 0; } -interface ServerConnectionInfo { +export interface ServerConnectionInfo { connection_filetransfer_bandwidth_sent: number; connection_filetransfer_bandwidth_received: number; @@ -103,12 +105,12 @@ interface ServerConnectionInfo { connection_ping: number; } -interface ServerAddress { +export interface ServerAddress { host: string; port: number; } -class ServerEntry { +export class ServerEntry { remote_address: ServerAddress; channelTree: ChannelTree; properties: ServerProperties; diff --git a/shared/js/channel-tree/view.ts b/shared/js/channel-tree/view.ts index 4530e28e..5f1e8108 100644 --- a/shared/js/channel-tree/view.ts +++ b/shared/js/channel-tree/view.ts @@ -4,12 +4,12 @@ /// /// /// -/// -/// +/// +/// /// -class ChannelTree { +export class ChannelTree { client: ConnectionHandler; server: ServerEntry; diff --git a/shared/js/connection/CommandHandler.ts b/shared/js/connection/CommandHandler.ts index f5e4bfed..e2a72bb1 100644 --- a/shared/js/connection/CommandHandler.ts +++ b/shared/js/connection/CommandHandler.ts @@ -1,309 +1,466 @@ -/// +import {ConnectionHandler, DisconnectReason, ViewReasonId} from "../ConnectionHandler"; +import { + AbstractCommandHandler, + AbstractCommandHandlerBoss, + AbstractServerConnection, + CommandOptions, ServerCommand +} from "./ConnectionBase"; +import {CommandResult, ErrorID} from "./ServerConnectionDeclaration"; +import {Sound} from "../sound/Sounds"; +import {log, LogCategory} from "../log"; +import {MessageHelper} from "../ui/frames/chat"; +import { + ClientConnectionInfo, + ClientEntry, + ClientType, + LocalClientEntry, + MusicClientEntry, SongInfo +} from "../channel-tree/client"; +import {ChannelEntry} from "../channel-tree/channel"; +import {chat as pchat} from "../ui/frames/side/private_conversations"; +import {Modals} from "../ui/modal/ModalPoke"; +import {chat} from "../ui/frames/side/conversations"; +import Conversation = chat.channel.Conversation; +import {createErrorModal, createInfoModal, createInputModal, createModal} from "../ui/elements/modal"; +import {server_connections} from "../ui/frames/connection_handlers"; +import {server} from "../ui/frames/server_log"; -namespace connection { - import Conversation = chat.channel.Conversation; - import MusicInfo = chat.MusicInfo; +export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { + constructor(connection: AbstractServerConnection) { + super(connection); + } +} - export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { - constructor(connection: AbstractServerConnection) { - super(connection); +export class ConnectionCommandHandler extends AbstractCommandHandler { + readonly connection: AbstractServerConnection; + readonly connection_handler: ConnectionHandler; + + constructor(connection: AbstractServerConnection) { + super(connection); + this.connection_handler = connection.client; + + this["error"] = this.handleCommandResult; + this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; + this["notifychannelcreated"] = this.handleCommandChannelCreate; + this["notifychanneldeleted"] = this.handleCommandChannelDelete; + this["notifychannelhide"] = this.handleCommandChannelHide; + this["notifychannelshow"] = this.handleCommandChannelShow; + + this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; + this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; + + this["notifycliententerview"] = this.handleCommandClientEnterView; + this["notifyclientleftview"] = this.handleCommandClientLeftView; + this["notifyclientmoved"] = this.handleNotifyClientMoved; + this["initserver"] = this.handleCommandServerInit; + this["notifychannelmoved"] = this.handleNotifyChannelMoved; + this["notifychanneledited"] = this.handleNotifyChannelEdited; + this["notifytextmessage"] = this.handleNotifyTextMessage; + this["notifyclientchatcomposing"] = this.notifyClientChatComposing; + this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; + this["notifyclientupdated"] = this.handleNotifyClientUpdated; + this["notifyserveredited"] = this.handleNotifyServerEdited; + this["notifyserverupdated"] = this.handleNotifyServerUpdated; + + this["notifyclientpoke"] = this.handleNotifyClientPoke; + + this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; + + this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; + this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; + this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; + + this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; + this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; + + this["notifyconversationhistory"] = this.handleNotifyConversationHistory; + this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; + + this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; + this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; + + this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; + this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; + this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; + this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + } + + private loggable_invoker(unique_id, client_id, name) : server.base.Client | undefined { + const id = parseInt(client_id); + if(typeof(client_id) === "undefined" || Number.isNaN(id)) + return undefined; + + if(id == 0) + return { + client_id: 0, + client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, + client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, + }; + + return { + client_unique_id: unique_id, + client_name: name, + client_id: client_id + }; + } + + proxy_command_promise(promise: Promise, options: CommandOptions) { + if(!options.process_result) + return promise; + + return promise.catch(ex => { + if(options.process_result) { + if(ex instanceof CommandResult) { + let res = ex; + if(!res.success) { + if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error + const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); + res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); + this.connection_handler.log.log(server.Type.ERROR_PERMISSION, { + permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) + }); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } else if(res.id != ErrorID.EMPTY_RESULT) { + this.connection_handler.log.log(server.Type.ERROR_CUSTOM, { + message: res.extra_message.length == 0 ? res.message : res.extra_message + }); + } + } + } else if(typeof(ex) === "string") { + this.connection_handler.log.log(server.Type.CONNECTION_COMMAND_ERROR, {error: ex}); + } else { + log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); + } + } + + return Promise.reject(ex); + }); + } + + handle_command(command: ServerCommand) : boolean { + if(this[command.command]) { + this[command.command](command.arguments); + return true; + } + + return false; + } + + set_handler(command: string, handler: any) { + this[command] = handler; + } + + unset_handler(command: string, handler?: any) { + if(handler && this[command] != handler) return; + this[command] = undefined; + } + + handleCommandResult(json) { + json = json[0]; //Only one bulk + + let code : string = json["return_code"]; + if(!code || code.length == 0) { + log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); + return; + } + let retListeners = this.connection["_retListener"]; + + for(let e of retListeners) { + if(e.code != code) continue; + retListeners.remove(e); + let result = new CommandResult(json); + if(result.success) + e.resolve(result); + else + e.reject(result); + break; } } - export class ConnectionCommandHandler extends AbstractCommandHandler { - readonly connection: AbstractServerConnection; - readonly connection_handler: ConnectionHandler; - - constructor(connection: AbstractServerConnection) { - super(connection); - this.connection_handler = connection.client; - - this["error"] = this.handleCommandResult; - this["channellist"] = this.handleCommandChannelList; - this["channellistfinished"] = this.handleCommandChannelListFinished; - this["notifychannelcreated"] = this.handleCommandChannelCreate; - this["notifychanneldeleted"] = this.handleCommandChannelDelete; - this["notifychannelhide"] = this.handleCommandChannelHide; - this["notifychannelshow"] = this.handleCommandChannelShow; - - this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; - this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; - - this["notifycliententerview"] = this.handleCommandClientEnterView; - this["notifyclientleftview"] = this.handleCommandClientLeftView; - this["notifyclientmoved"] = this.handleNotifyClientMoved; - this["initserver"] = this.handleCommandServerInit; - this["notifychannelmoved"] = this.handleNotifyChannelMoved; - this["notifychanneledited"] = this.handleNotifyChannelEdited; - this["notifytextmessage"] = this.handleNotifyTextMessage; - this["notifyclientchatcomposing"] = this.notifyClientChatComposing; - this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; - this["notifyclientupdated"] = this.handleNotifyClientUpdated; - this["notifyserveredited"] = this.handleNotifyServerEdited; - this["notifyserverupdated"] = this.handleNotifyServerUpdated; - - this["notifyclientpoke"] = this.handleNotifyClientPoke; - - this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; - - this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; - this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; - this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; - - this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; - this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; - - this["notifyconversationhistory"] = this.handleNotifyConversationHistory; - this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; - - this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; - this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; - - this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; - this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; - this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; - this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + handleCommandServerInit(json){ + //We could setup the voice channel + if(this.connection.support_voice()) { + log.debug(LogCategory.NETWORKING, tr("Setting up voice")); + } else { + log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); } - private loggable_invoker(unique_id, client_id, name) : log.server.base.Client | undefined { - const id = parseInt(client_id); - if(typeof(client_id) === "undefined" || Number.isNaN(id)) - return undefined; - if(id == 0) - return { - client_id: 0, - client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, - client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, - }; + json = json[0]; //Only one bulk - return { - client_unique_id: unique_id, - client_name: name, - client_id: client_id - }; + this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); + this.connection.client.side_bar.channel_conversations().reset(); + this.connection.client.clientId = parseInt(json["aclid"]); + this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "aclid") continue; + if(key === "acn") continue; + + updates.push({key: key, value: json[key]}); } + this.connection.client.channelTree.server.updateVariables(false, ...updates); - proxy_command_promise(promise: Promise, options: connection.CommandOptions) { - if(!options.process_result) - return promise; - - return promise.catch(ex => { - if(options.process_result) { - if(ex instanceof CommandResult) { - let res = ex; - if(!res.success) { - if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error - const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); - res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); - this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, { - permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) - }); - this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } else if(res.id != ErrorID.EMPTY_RESULT) { - this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, { - message: res.extra_message.length == 0 ? res.message : res.extra_message - }); - } - } - } else if(typeof(ex) === "string") { - this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, {error: ex}); - } else { - log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); - } - } - - return Promise.reject(ex); - }); - } - - handle_command(command: ServerCommand) : boolean { - if(this[command.command]) { - this[command.command](command.arguments); - return true; - } - - return false; - } - - set_handler(command: string, handler: any) { - this[command] = handler; - } - - unset_handler(command: string, handler?: any) { - if(handler && this[command] != handler) return; - this[command] = undefined; - } - - handleCommandResult(json) { - json = json[0]; //Only one bulk - - let code : string = json["return_code"]; - if(!code || code.length == 0) { - log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); - return; - } - let retListeners = this.connection["_retListener"]; - - for(let e of retListeners) { - if(e.code != code) continue; - retListeners.remove(e); - let result = new CommandResult(json); - if(result.success) - e.resolve(result); - else - e.reject(result); - break; - } - } - - handleCommandServerInit(json){ - //We could setup the voice channel - if(this.connection.support_voice()) { - log.debug(LogCategory.NETWORKING, tr("Setting up voice")); - } else { - log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); - } - - - json = json[0]; //Only one bulk - - this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); - this.connection.client.side_bar.channel_conversations().reset(); - this.connection.client.clientId = parseInt(json["aclid"]); - this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "aclid") continue; - if(key === "acn") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - - const properties = this.connection.client.channelTree.server.properties; - /* host message */ - if(properties.virtualserver_hostmessage_mode > 0) { - if(properties.virtualserver_hostmessage_mode == 1) { - /* show in log */ - this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, { - message: properties.virtualserver_hostmessage - }); - } else { - /* create modal/create modal and quit */ - createModal({ - header: tr("Host message"), - body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), - footer: undefined - }).open(); - - if(properties.virtualserver_hostmessage_mode == 3) { - /* first let the client initialize his stuff */ - setTimeout(() => { - this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { - message: properties.virtualserver_welcomemessage - }); - - this.connection.disconnect("host message disconnect"); - this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); - this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); - }, 100); - } - } - } - - /* welcome message */ - if(properties.virtualserver_welcomemessage) { - this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, { - message: properties.virtualserver_welcomemessage + const properties = this.connection.client.channelTree.server.properties; + /* host message */ + if(properties.virtualserver_hostmessage_mode > 0) { + if(properties.virtualserver_hostmessage_mode == 1) { + /* show in log */ + this.connection_handler.log.log(server.Type.SERVER_HOST_MESSAGE, { + message: properties.virtualserver_hostmessage }); - } + } else { + /* create modal/create modal and quit */ + createModal({ + header: tr("Host message"), + body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), + footer: undefined + }).open(); - /* priviledge key */ - if(properties.virtualserver_ask_for_privilegekey) { - createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { - if(!result) return; - const scon = server_connections.active_connection_handler(); - - if(scon.serverConnection.connected) - scon.serverConnection.send_command("tokenuse", { - token: result - }).then(() => { - createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); - }).catch(error => { - createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); + if(properties.virtualserver_hostmessage_mode == 3) { + /* first let the client initialize his stuff */ + setTimeout(() => { + this.connection_handler.log.log(server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { + message: properties.virtualserver_welcomemessage }); - }, { field_placeholder: 'Enter Privilege Key' }).open(); - } - this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, { - own_client: this.connection_handler.getClient().log_data() - }); - this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); - this.connection.client.onConnected(); - } - - handleNotifyServerConnectionInfo(json) { - json = json[0]; - - /* everything is a number, so lets parse it */ - for(const key of Object.keys(json)) - json[key] = parseFloat(json[key]); - - this.connection_handler.channelTree.server.set_connection_info(json); - } - - handleNotifyConnectionInfo(json) { - json = json[0]; - - const object = new ClientConnectionInfo(); - /* everything is a number (except ip), so lets parse it */ - for(const key of Object.keys(json)) { - if(key === "connection_client_ip") - object[key] = json[key]; - else - object[key] = parseFloat(json[key]); - } - - const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); - if(!client) { - log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); - return; - } - - client.set_connection_info(object); - } - - private createChannelFromJson(json, ignoreOrder: boolean = false) { - let tree = this.connection.client.channelTree; - - let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); - tree.insertChannel(channel); - if(json["channel_order"] !== "0") { - let prev = tree.findChannel(json["channel_order"]); - if(!prev && json["channel_order"] != 0) { - if(!ignoreOrder) { - log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); - return; - } + this.connection.disconnect("host message disconnect"); + this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); + this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); + }, 100); } + } + } - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + /* welcome message */ + if(properties.virtualserver_welcomemessage) { + this.connection_handler.log.log(server.Type.SERVER_WELCOME_MESSAGE, { + message: properties.virtualserver_welcomemessage + }); + } + + /* priviledge key */ + if(properties.virtualserver_ask_for_privilegekey) { + createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { + if(!result) return; + const scon = server_connections.active_connection_handler(); + + if(scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); + }).catch(error => { + createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); + }); + }, { field_placeholder: 'Enter Privilege Key' }).open(); + } + + this.connection_handler.log.log(server.Type.CONNECTION_CONNECTED, { + own_client: this.connection_handler.getClient().log_data() + }); + this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); + this.connection.client.onConnected(); + } + + handleNotifyServerConnectionInfo(json) { + json = json[0]; + + /* everything is a number, so lets parse it */ + for(const key of Object.keys(json)) + json[key] = parseFloat(json[key]); + + this.connection_handler.channelTree.server.set_connection_info(json); + } + + handleNotifyConnectionInfo(json) { + json = json[0]; + + const object = new ClientConnectionInfo(); + /* everything is a number (except ip), so lets parse it */ + for(const key of Object.keys(json)) { + if(key === "connection_client_ip") + object[key] = json[key]; + else + object[key] = parseFloat(json[key]); + } + + const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); + if(!client) { + log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); + return; + } + + client.set_connection_info(object); + } + + private createChannelFromJson(json, ignoreOrder: boolean = false) { + let tree = this.connection.client.channelTree; + + let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); + tree.insertChannel(channel); + if(json["channel_order"] !== "0") { + let prev = tree.findChannel(json["channel_order"]); + if(!prev && json["channel_order"] != 0) { + if(!ignoreOrder) { + log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); return; } - tree.moveChannel(channel, prev, parent); //TODO test if channel exists! } - if(ignoreOrder) { - for(let ch of tree.channels) { - if(ch.properties.channel_order == channel.channelId) { - tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) - } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + return; + } + tree.moveChannel(channel, prev, parent); //TODO test if channel exists! + } + if(ignoreOrder) { + for(let ch of tree.channels) { + if(ch.properties.channel_order == channel.channelId) { + tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) + } + } + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "cpid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + } + + handleCommandChannelList(json) { + this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ + log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); + for(let index = 0; index < json.length; index++) + this.createChannelFromJson(json[index], true); + } + + + handleCommandChannelListFinished(json) { + this.connection.client.channelTree.show_channel_tree(); + } + + handleCommandChannelCreate(json) { + this.createChannelFromJson(json[0]); + } + + handleCommandChannelShow(json) { + this.createChannelFromJson(json[0]); //TODO may chat? + } + + handleCommandChannelDelete(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandChannelHide(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandClientEnterView(json) { + let tree = this.connection.client.channelTree; + + let client: ClientEntry; + let channel = undefined; + let old_channel = undefined; + let reason_id, reason_msg; + + let invokerid, invokername, invokeruid; + + for(const entry of json) { + /* attempt to update properties if given */ + channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; + old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; + reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; + reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; + + invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; + invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; + invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; + + client = tree.findClient(parseInt(entry["clid"])); + + if(!client) { + if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { + client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } else { + client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } + + client.properties.client_type = parseInt(entry["client_type"]); + client = tree.insertClient(client, channel); + } else { + tree.moveClient(client, channel); + } + + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(server.Type.CLIENT_VIEW_ENTER, { + channel_from: old_channel ? old_channel.log_data() : undefined, + channel_to: channel ? channel.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(invokeruid, invokerid, invokername), + message:reason_msg, + reason: parseInt(reason_id), + own_channel: channel == own_channel + }); + + if(reason_id == ViewReasonId.VREASON_USER_ACTION) { + if(own_channel == channel) + if(old_channel) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else + this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { + + } else { + console.warn(tr("Unknown reasonid for %o"), reason_id); } } @@ -311,159 +468,113 @@ namespace connection { key: string, value: string }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "cpid") continue; + + for(let key in entry) { + if(key == "cfid") continue; + if(key == "ctid") continue; if(key === "invokerid") continue; if(key === "invokername") continue; if(key === "invokeruid") continue; if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); + updates.push({key: key, value: entry[key]}); } - channel.updateVariables(...updates); - } - handleCommandChannelList(json) { - this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ - log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); - for(let index = 0; index < json.length; index++) - this.createChannelFromJson(json[index], true); - } + client.updateVariables(...updates); + /* if its a new client join, or a system reason (like we joined) */ + if(!old_channel || reason_id == 2) { + /* client new join */ + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + unique_id: client.properties.client_unique_identifier, + client_id: client.clientId(), + name: client.clientNickName() + }, { + create: false, + attach: true + }); + if(conversation) + client.flag_text_unread = conversation.is_unread(); + } - handleCommandChannelListFinished(json) { - this.connection.client.channelTree.show_channel_tree(); - } - - handleCommandChannelCreate(json) { - this.createChannelFromJson(json[0]); - } - - handleCommandChannelShow(json) { - this.createChannelFromJson(json[0]); //TODO may chat? - } - - handleCommandChannelDelete(json) { - let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); + if(client instanceof LocalClientEntry) { + client.initializeListener(); + this.connection_handler.update_voice_status(); + this.connection_handler.side_bar.info_frame().update_channel_talk(); + const conversations = this.connection.client.side_bar.channel_conversations(); + conversations.set_current_channel(client.currentChannel().channelId); } } + } - handleCommandChannelHide(json) { + handleCommandClientLeftView(json) { + let reason_id = -1; + + for(const entry of json) { + reason_id = entry["reasonid"] || reason_id; let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); + let client = tree.findClient(entry["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client left!")); + return 0; } - } - - handleCommandClientEnterView(json) { - let tree = this.connection.client.channelTree; - - let client: ClientEntry; - let channel = undefined; - let old_channel = undefined; - let reason_id, reason_msg; - - let invokerid, invokername, invokeruid; - - for(const entry of json) { - /* attempt to update properties if given */ - channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; - old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; - reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; - reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; - - invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; - invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; - invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; - - client = tree.findClient(parseInt(entry["clid"])); - - if(!client) { - if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { - client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } else { - client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } - - client.properties.client_type = parseInt(entry["client_type"]); - client = tree.insertClient(client, channel); + if(client == this.connection.client.getClient()) { + if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); } else { - tree.moveClient(client, channel); + this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); } + this.connection_handler.side_bar.info_frame().update_channel_talk(); + return; + } - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, { - channel_from: old_channel ? old_channel.log_data() : undefined, - channel_to: channel ? channel.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(invokeruid, invokerid, invokername), - message:reason_msg, - reason: parseInt(reason_id), - own_channel: channel == own_channel - }); + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + let channel_from = tree.findChannel(entry["cfid"]); + let channel_to = tree.findChannel(entry["ctid"]); + + const is_own_channel = channel_from == own_channel; + this.connection_handler.log.log(server.Type.CLIENT_VIEW_LEAVE, { + channel_from: channel_from ? channel_from.log_data() : undefined, + channel_to: channel_to ? channel_to.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), + message: entry["reasonmsg"], + reason: parseInt(entry["reasonid"]), + ban_time: parseInt(entry["bantime"]), + own_channel: is_own_channel + }); + + if(is_own_channel) { if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - if(own_channel == channel) - if(old_channel) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else - this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { + this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { - + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); + } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { + this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); } else { - console.warn(tr("Unknown reasonid for %o"), reason_id); + log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); } } - let updates: { - key: string, - value: string - }[] = []; - - for(let key in entry) { - if(key == "cfid") continue; - if(key == "ctid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: entry[key]}); - } - - client.updateVariables(...updates); - - /* if its a new client join, or a system reason (like we joined) */ - if(!old_channel || reason_id == 2) { - /* client new join */ + if(!channel_to) { + /* client left the server */ const conversation_manager = this.connection_handler.side_bar.private_conversations(); const conversation = conversation_manager.find_conversation({ unique_id: client.properties.client_unique_identifier, @@ -471,690 +582,599 @@ namespace connection { name: client.clientNickName() }, { create: false, - attach: true + attach: false }); - if(conversation) - client.flag_text_unread = conversation.is_unread(); - } - - if(client instanceof LocalClientEntry) { - client.initializeListener(); - this.connection_handler.update_voice_status(); - this.connection_handler.side_bar.info_frame().update_channel_talk(); - const conversations = this.connection.client.side_bar.channel_conversations(); - conversations.set_current_channel(client.currentChannel().channelId); - } - } - } - - handleCommandClientLeftView(json) { - let reason_id = -1; - - for(const entry of json) { - reason_id = entry["reasonid"] || reason_id; - let tree = this.connection.client.channelTree; - let client = tree.findClient(entry["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client left!")); - return 0; - } - if(client == this.connection.client.getClient()) { - if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else { - this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); - } - this.connection_handler.side_bar.info_frame().update_channel_talk(); - return; - } - - - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - let channel_from = tree.findChannel(entry["cfid"]); - let channel_to = tree.findChannel(entry["ctid"]); - - const is_own_channel = channel_from == own_channel; - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, { - channel_from: channel_from ? channel_from.log_data() : undefined, - channel_to: channel_to ? channel_to.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), - message: entry["reasonmsg"], - reason: parseInt(entry["reasonid"]), - ban_time: parseInt(entry["bantime"]), - own_channel: is_own_channel - }); - - if(is_own_channel) { - if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { - this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); - } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); - } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { - this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); - } else { - log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); - } - } - - if(!channel_to) { - /* client left the server */ - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - unique_id: client.properties.client_unique_identifier, - client_id: client.clientId(), - name: client.clientNickName() - }, { - create: false, - attach: false - }); - if(conversation) { - conversation.set_state(chat.PrivateConversationState.DISCONNECTED); - } + if(conversation) { + conversation.set_state(pchat.PrivateConversationState.DISCONNECTED); } } - - tree.deleteClient(client); - } - } - - handleNotifyClientMoved(json) { - json = json[0]; //Only one bulk - let tree = this.connection.client.channelTree; - let client = tree.findClient(json["clid"]); - let self = client instanceof LocalClientEntry; - - let channel_to = tree.findChannel(parseInt(json["ctid"])); - let channel_from = tree.findChannel(parseInt(json["cfid"])); - - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); - return 0; } - if(!channel_to) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); - return 0; - } - - if(!self) { - if(!channel_from) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); - channel_from = client.currentChannel(); - } else if(channel_from != client.currentChannel()) { - log.error(LogCategory.NETWORKING, - tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), - client.currentChannel().channelId, channel_from.channelId - ); - } - } else { - channel_from = client.currentChannel(); - } - - tree.moveClient(client, channel_to); - - if(self) { - this.connection_handler.update_voice_status(channel_to); - - for(const entry of client.channelTree.clientsByChannel(channel_from)) { - if(entry !== client && entry.get_audio_handle()) { - entry.get_audio_handle().abort_replay(); - entry.speaking = false; - } - } - - const side_bar = this.connection_handler.side_bar; - side_bar.info_frame().update_channel_talk(); - - const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); - if(conversation_to) - conversation_to.update_private_state(); - - if(channel_from) { - const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); - if(conversation_from) - conversation_from.update_private_state(); - } - - side_bar.channel_conversations().update_chat_box(); - } - - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, { - channel_from: channel_from ? { - channel_id: channel_from.channelId, - channel_name: channel_from.channelName() - } : undefined, - channel_from_own: channel_from == own_channel, - - channel_to: channel_to ? { - channel_id: channel_to.channelId, - channel_name: channel_to.channelName() - } : undefined, - channel_to_own: channel_to == own_channel, - - client: { - client_id: client.clientId(), - client_name: client.clientNickName(), - client_unique_id: client.properties.client_unique_identifier - }, - client_own: self, - - invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), - - message: json["reasonmsg"], - reason: parseInt(json["reasonid"]), - }); - if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { - if(self) - this.connection_handler.sound.play(Sound.USER_MOVED_SELF); - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); - } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - if(self) {} //If we do an action we wait for the error response - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { - if(self) { - this.connection_handler.sound.play(Sound.CHANNEL_KICKED); - } else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else { - console.warn(tr("Unknown reason id %o"), json["reasonid"]); - } - } - - handleNotifyChannelMoved(json) { - json = json[0]; //Only one bulk - - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); - return 0; - } - - let prev = tree.findChannel(json["order"]); - if(!prev && json["order"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); - return 0; - } - - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); - return 0; - } - - tree.moveChannel(channel, prev, parent); - } - - handleNotifyChannelEdited(json) { - json = json[0]; //Only one bulk - - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); - return 0; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - - if(this.connection_handler.getClient().currentChannel() === channel) { - //TODO: Playback sound that your channel has been edited - this.connection_handler.update_voice_status(); - } - } - - handleNotifyTextMessage(json) { - json = json[0]; //Only one bulk - - let mode = json["targetmode"]; - if(mode == 1){ - //json["invokerid"], json["invokername"], json["invokeruid"] - const target_client_id = parseInt(json["target"]); - const target_own = target_client_id === this.connection.client.getClientId(); - - if(target_own && target_client_id === json["invokerid"]) { - log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json)); - return; - } - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, - unique_id: target_own ? json["invokeruid"] : undefined, - name: target_own ? json["invokername"] : undefined - }, { - create: target_own, - attach: target_own - }); - if(!conversation) { - log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); - return; - } - - conversation.append_message(json["msg"], { - type: target_own ? "partner" : "self", - name: json["invokername"], - unique_id: json["invokeruid"], - client_id: parseInt(json["invokerid"]) - }); - - if(target_own) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - if(client) /* the client itself might be invisible */ - client.flag_text_unread = conversation.is_unread(); - } else { - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - } - } else if(mode == 2) { - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const own_channel_id = this.connection.client.getClient().currentChannel().channelId; - const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; - const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); - - if(json["invokerid"] == this.connection.client.clientId) - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - else if(channel_id == own_channel_id) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - } - - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(channel_id); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], - - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] - }); - if(conversation.is_unread() && channel) - channel.flag_text_unread = true; - } else if(mode == 3) { - this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, { - message: json["msg"], - sender: { - client_unique_id: json["invokeruid"], - client_name: json["invokername"], - client_id: parseInt(json["invokerid"]) - } - }); - - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(0); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], - - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] - }); - this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); - } - } - - notifyClientChatComposing(json) { - json = json[0]; - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) - return; - - conversation.trigger_typing(); - } - - handleNotifyClientChatClosed(json) { - json = json[0]; //Only one bulk - - //Chat partner has closed the conversation - - //clid: "6" - //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) { - log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); - return; - } - conversation.set_state(chat.PrivateConversationState.CLOSED); - } - - handleNotifyClientUpdated(json) { - json = json[0]; //Only one bulk - - let client = this.connection.client.channelTree.findClient(json["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); - return; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key == "clid") continue; - updates.push({key: key, value: json[key]}); - } - client.updateVariables(...updates); - } - - handleNotifyServerEdited(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - } - - handleNotifyServerUpdated(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(true, ...updates); - } - - handleNotifyMusicPlayerInfo(json) { - json = json[0]; - - let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); - if(!bot || !(bot instanceof MusicClientEntry)) { - log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); - return; - } - - bot.handlePlayerInfo(json); - } - - handleNotifyClientPoke(json) { - json = json[0]; - Modals.spawnPoke(this.connection_handler, { - id: parseInt(json["invokerid"]), - name: json["invokername"], - unique_id: json["invokeruid"] - }, json["msg"]); - - this.connection_handler.sound.play(Sound.USER_POKED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientAdd(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) - this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientRemove(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); - } else { - } - } - - //TODO server chat message - handleNotifyClientChannelGroupChanged(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); - } - } - - handleNotifyChannelSubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = true; - } - } - - handleNotifyChannelUnsubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = false; - for(const client of channel.clients(false)) - this.connection.client.channelTree.deleteClient(client); - } - } - - handleNotifyConversationHistory(json: any[]) { - const conversations = this.connection.client.side_bar.channel_conversations(); - const conversation = conversations.conversation(parseInt(json[0]["cid"])); - if(!conversation) { - log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); - return; - } - - for(const entry of json) { - conversation.register_new_message({ - message: entry["msg"], - sender_unique_id: entry["sender_unique_id"], - sender_name: entry["sender_name"], - timestamp: parseInt(entry["timestamp"]), - sender_database_id: parseInt(entry["sender_database_id"]) - }, false); - } - - /* now update the boxes */ - /* No update needed because the command which triggers this notify should update the chat box on success - conversation.fix_scroll(true); - conversation.handle.update_chat_box(); - */ - } - - handleNotifyConversationMessageDelete(json: any[]) { - let conversation: Conversation; - const conversations = this.connection.client.side_bar.channel_conversations(); - for(const entry of json) { - if(typeof(entry["cid"]) !== "undefined") - conversation = conversations.conversation(parseInt(entry["cid"]), false); - if(!conversation) - continue; - - conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); - } - } - - handleNotifyMusicStatusUpdate(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - client.events.fire("music_status_update", { - player_replay_index: parseInt(json["player_replay_index"]), - player_buffered_index: parseInt(json["player_buffered_index"]) - }); - } - - handleMusicPlayerSongChange(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - const song_id = parseInt(json["song_id"]); - let song: SongInfo; - if(song_id) { - song = new SongInfo(); - JSON.map_to(song, json); - } - - client.events.fire("music_song_change", { - song: song - }); - } - - handleNotifyPlaylistSongAdd(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - client.events.fire("playlist_song_add", { - song: { - song_id: parseInt(json["song_id"]), - song_invoker: json["song_invoker"], - song_previous_song_id: parseInt(json["song_previous_song_id"]), - song_url: json["song_url"], - song_url_loader: json["song_url_loader"], - - song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", - song_metadata: json["song_metadata"] - } - }); - } - - handleNotifyPlaylistSongRemove(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_remove", { song_id: song_id }); - } - - handleNotifyPlaylistSongReorder(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - const previous_song_id = parseInt(json["song_previous_song_id"]); - client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); - } - - handleNotifyPlaylistSongLoaded(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_loaded", { - song_id: song_id, - success: json["success"] == 1, - error_msg: json["load_error_msg"], - metadata: json["song_metadata"] - }); + tree.deleteClient(client); } } + + handleNotifyClientMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + let self = client instanceof LocalClientEntry; + + let channel_to = tree.findChannel(parseInt(json["ctid"])); + let channel_from = tree.findChannel(parseInt(json["cfid"])); + + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); + return 0; + } + + if(!channel_to) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); + return 0; + } + + if(!self) { + if(!channel_from) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); + channel_from = client.currentChannel(); + } else if(channel_from != client.currentChannel()) { + log.error(LogCategory.NETWORKING, + tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), + client.currentChannel().channelId, channel_from.channelId + ); + } + } else { + channel_from = client.currentChannel(); + } + + tree.moveClient(client, channel_to); + + if(self) { + this.connection_handler.update_voice_status(channel_to); + + for(const entry of client.channelTree.clientsByChannel(channel_from)) { + if(entry !== client && entry.get_audio_handle()) { + entry.get_audio_handle().abort_replay(); + entry.speaking = false; + } + } + + const side_bar = this.connection_handler.side_bar; + side_bar.info_frame().update_channel_talk(); + + const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); + if(conversation_to) + conversation_to.update_private_state(); + + if(channel_from) { + const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); + if(conversation_from) + conversation_from.update_private_state(); + } + + side_bar.channel_conversations().update_chat_box(); + } + + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(server.Type.CLIENT_VIEW_MOVE, { + channel_from: channel_from ? { + channel_id: channel_from.channelId, + channel_name: channel_from.channelName() + } : undefined, + channel_from_own: channel_from == own_channel, + + channel_to: channel_to ? { + channel_id: channel_to.channelId, + channel_name: channel_to.channelName() + } : undefined, + channel_to_own: channel_to == own_channel, + + client: { + client_id: client.clientId(), + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier + }, + client_own: self, + + invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), + + message: json["reasonmsg"], + reason: parseInt(json["reasonid"]), + }); + if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { + if(self) + this.connection_handler.sound.play(Sound.USER_MOVED_SELF); + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + if(self) {} //If we do an action we wait for the error response + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + if(self) { + this.connection_handler.sound.play(Sound.CHANNEL_KICKED); + } else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else { + console.warn(tr("Unknown reason id %o"), json["reasonid"]); + } + } + + handleNotifyChannelMoved(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); + return 0; + } + + let prev = tree.findChannel(json["order"]); + if(!prev && json["order"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); + return 0; + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); + return 0; + } + + tree.moveChannel(channel, prev, parent); + } + + handleNotifyChannelEdited(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); + return 0; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + + if(this.connection_handler.getClient().currentChannel() === channel) { + //TODO: Playback sound that your channel has been edited + this.connection_handler.update_voice_status(); + } + } + + handleNotifyTextMessage(json) { + json = json[0]; //Only one bulk + + let mode = json["targetmode"]; + if(mode == 1){ + //json["invokerid"], json["invokername"], json["invokeruid"] + const target_client_id = parseInt(json["target"]); + const target_own = target_client_id === this.connection.client.getClientId(); + + if(target_own && target_client_id === json["invokerid"]) { + log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o"), json); + return; + } + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, + unique_id: target_own ? json["invokeruid"] : undefined, + name: target_own ? json["invokername"] : undefined + }, { + create: target_own, + attach: target_own + }); + if(!conversation) { + log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); + return; + } + + conversation.append_message(json["msg"], { + type: target_own ? "partner" : "self", + name: json["invokername"], + unique_id: json["invokeruid"], + client_id: parseInt(json["invokerid"]) + }); + + if(target_own) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + if(client) /* the client itself might be invisible */ + client.flag_text_unread = conversation.is_unread(); + } else { + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + } + } else if(mode == 2) { + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const own_channel_id = this.connection.client.getClient().currentChannel().channelId; + const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; + const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); + + if(json["invokerid"] == this.connection.client.clientId) + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + else if(channel_id == own_channel_id) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + } + + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(channel_id); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + if(conversation.is_unread() && channel) + channel.flag_text_unread = true; + } else if(mode == 3) { + this.connection_handler.log.log(server.Type.GLOBAL_MESSAGE, { + message: json["msg"], + sender: { + client_unique_id: json["invokeruid"], + client_name: json["invokername"], + client_id: parseInt(json["invokerid"]) + } + }); + + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(0); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); + } + } + + notifyClientChatComposing(json) { + json = json[0]; + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if(!conversation) + return; + + conversation.trigger_typing(); + } + + handleNotifyClientChatClosed(json) { + json = json[0]; //Only one bulk + + //Chat partner has closed the conversation + + //clid: "6" + //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if(!conversation) { + log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); + return; + } + conversation.set_state(pchat.PrivateConversationState.CLOSED); + } + + handleNotifyClientUpdated(json) { + json = json[0]; //Only one bulk + + let client = this.connection.client.channelTree.findClient(json["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); + return; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key == "clid") continue; + updates.push({key: key, value: json[key]}); + } + client.updateVariables(...updates); + } + + handleNotifyServerEdited(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + } + + handleNotifyServerUpdated(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(true, ...updates); + } + + handleNotifyMusicPlayerInfo(json) { + json = json[0]; + + let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); + if(!bot || !(bot instanceof MusicClientEntry)) { + log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); + return; + } + + bot.handlePlayerInfo(json); + } + + handleNotifyClientPoke(json) { + json = json[0]; + Modals.spawnPoke(this.connection_handler, { + id: parseInt(json["invokerid"]), + name: json["invokername"], + unique_id: json["invokeruid"] + }, json["msg"]); + + this.connection_handler.sound.play(Sound.USER_POKED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientAdd(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) + this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientRemove(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); + } else { + } + } + + //TODO server chat message + handleNotifyClientChannelGroupChanged(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); + } + } + + handleNotifyChannelSubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = true; + } + } + + handleNotifyChannelUnsubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = false; + for(const client of channel.clients(false)) + this.connection.client.channelTree.deleteClient(client); + } + } + + handleNotifyConversationHistory(json: any[]) { + const conversations = this.connection.client.side_bar.channel_conversations(); + const conversation = conversations.conversation(parseInt(json[0]["cid"])); + if(!conversation) { + log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); + return; + } + + for(const entry of json) { + conversation.register_new_message({ + message: entry["msg"], + sender_unique_id: entry["sender_unique_id"], + sender_name: entry["sender_name"], + timestamp: parseInt(entry["timestamp"]), + sender_database_id: parseInt(entry["sender_database_id"]) + }, false); + } + + /* now update the boxes */ + /* No update needed because the command which triggers this notify should update the chat box on success + conversation.fix_scroll(true); + conversation.handle.update_chat_box(); + */ + } + + handleNotifyConversationMessageDelete(json: any[]) { + let conversation: Conversation; + const conversations = this.connection.client.side_bar.channel_conversations(); + for(const entry of json) { + if(typeof(entry["cid"]) !== "undefined") + conversation = conversations.conversation(parseInt(entry["cid"]), false); + if(!conversation) + continue; + + conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); + } + } + + handleNotifyMusicStatusUpdate(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + client.events.fire("music_status_update", { + player_replay_index: parseInt(json["player_replay_index"]), + player_buffered_index: parseInt(json["player_buffered_index"]) + }); + } + + handleMusicPlayerSongChange(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + const song_id = parseInt(json["song_id"]); + let song: SongInfo; + if(song_id) { + song = new SongInfo(); + JSON.map_to(song, json); + } + + client.events.fire("music_song_change", { + song: song + }); + } + + handleNotifyPlaylistSongAdd(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + client.events.fire("playlist_song_add", { + song: { + song_id: parseInt(json["song_id"]), + song_invoker: json["song_invoker"], + song_previous_song_id: parseInt(json["song_previous_song_id"]), + song_url: json["song_url"], + song_url_loader: json["song_url_loader"], + + song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", + song_metadata: json["song_metadata"] + } + }); + } + + handleNotifyPlaylistSongRemove(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_remove", { song_id: song_id }); + } + + handleNotifyPlaylistSongReorder(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + const previous_song_id = parseInt(json["song_previous_song_id"]); + client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); + } + + handleNotifyPlaylistSongLoaded(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_loaded", { + song_id: song_id, + success: json["success"] == 1, + error_msg: json["load_error_msg"], + metadata: json["song_metadata"] + }); + } } \ No newline at end of file diff --git a/shared/js/connection/CommandHelper.ts b/shared/js/connection/CommandHelper.ts index 9eff210b..21203bb2 100644 --- a/shared/js/connection/CommandHelper.ts +++ b/shared/js/connection/CommandHelper.ts @@ -1,448 +1,460 @@ -namespace connection { - export class CommandHelper extends AbstractCommandHandler { - private _who_am_i: any; - private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; - private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; +import { + ClientNameInfo, + CommandResult, + ErrorID, + Playlist, PlaylistInfo, PlaylistSong, + QueryList, + QueryListEntry, ServerGroupClient +} from "./ServerConnectionDeclaration"; +import {ChannelEntry} from "../channel-tree/channel"; +import {ChatType} from "../ui/frames/chat"; +import {ClientEntry} from "../channel-tree/client"; +import {AbstractCommandHandler, ServerCommand, SingleCommandHandler} from "./ConnectionBase"; +import {log, LogCategory} from "../log"; - constructor(connection) { - super(connection); +export class CommandHelper extends AbstractCommandHandler { + private _who_am_i: any; + private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; + private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; - this.volatile_handler_boss = false; - this.ignore_consumed = true; + constructor(connection) { + super(connection); + + this.volatile_handler_boss = false; + this.ignore_consumed = true; + } + + initialize() { + this.connection.command_handler_boss().register_handler(this); + } + + destroy() { + if(this.connection) { + const hboss = this.connection.command_handler_boss(); + hboss && hboss.unregister_handler(this); + } + this._awaiters_unique_ids = undefined; + } + + handle_command(command: ServerCommand): boolean { + if(command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + if(command.command == "notifyclientgetnamefromdbid") + this.handle_notifyclientgetnamefromdbid(command.arguments); + else + return false; + return true; + } + + joinChannel(channel: ChannelEntry, password?: string) : Promise { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); + } + + sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { + if(type == ChatType.SERVER) + return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); + else if(type == ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); + else if(type == ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); + } + + updateClient(key: string, value: string) : Promise { + let data = {}; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } + + async info_from_uid(..._unique_ids: string[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_ids = new Set(_unique_ids); + if(!unique_ids.size) return []; + + const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; + + + for(const unique_id of unique_ids) { + request.push({'cluid': unique_id}); + (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) + .push(unique_id_resolvers[unique_id] = info => response.push(info)); } - initialize() { - this.connection.command_handler_boss().register_handler(this); - } - - destroy() { - if(this.connection) { - const hboss = this.connection.command_handler_boss(); - hboss && hboss.unregister_handler(this); + try { + await this.connection.send_command("clientgetnamefromuid", request); + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } else { + throw error; } - this._awaiters_unique_ids = undefined; + } finally { + /* cleanup */ + for(const unique_id of Object.keys(unique_id_resolvers)) + (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); } - handle_command(command: connection.ServerCommand): boolean { - if(command.command == "notifyclientnamefromuid") - this.handle_notifyclientnamefromuid(command.arguments); - if(command.command == "notifyclientgetnamefromdbid") - this.handle_notifyclientgetnamefromdbid(command.arguments); - else - return false; - return true; + return response; + } + + private handle_notifyclientgetnamefromdbid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_dbid[info.client_database_id] || []; + delete this._awaiters_unique_dbid[info.client_database_id]; + + for(const fn of functions) + fn(info); + } + } + + async info_from_cldbid(..._cldbid: number[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_cldbid = new Set(_cldbid); + if(!unique_cldbid.size) return []; + + const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; + + + for(const cldbid of unique_cldbid) { + request.push({'cldbid': cldbid}); + (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) + .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); } - joinChannel(channel: ChannelEntry, password?: string) : Promise { - return this.connection.send_command("clientmove", { - "clid": this.connection.client.getClientId(), - "cid": channel.getChannelId(), - "cpw": password || "" - }); + try { + await this.connection.send_command("clientgetnamefromdbid", request); + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } else { + throw error; + } + } finally { + /* cleanup */ + for(const cldbid of Object.keys(unique_cldbid_resolvers)) + (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); } - sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { - if(type == ChatType.SERVER) - return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); - else if(type == ChatType.CHANNEL) - return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); - else if(type == ChatType.CLIENT) - return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); - } + return response; + } + + private handle_notifyclientnamefromuid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_ids[entry["cluid"]] || []; + delete this._awaiters_unique_ids[entry["cluid"]]; + + for(const fn of functions) + fn(info); + } + } + + request_query_list(server_id: number = undefined) : Promise { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + + const result = {} as QueryList; + + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + + for(const entry of json) { + const rentry = {} as QueryListEntry; + rentry.bounded_server = parseInt(entry["client_bound_server"]); + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + + result.queries.push(rentry); + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); - updateClient(key: string, value: string) : Promise { let data = {}; - data[key] = value; - return this.connection.send_command("clientupdate", data); - } + if(server_id !== undefined) + data["server_id"] = server_id; - async info_from_uid(..._unique_ids: string[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_ids = new Set(_unique_ids); - if(!unique_ids.size) return []; + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); - const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; - - - for(const unique_id of unique_ids) { - request.push({'cluid': unique_id}); - (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) - .push(unique_id_resolvers[unique_id] = info => response.push(info)); - } - - try { - await this.connection.send_command("clientgetnamefromuid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } } - } finally { - /* cleanup */ - for(const unique_id of Object.keys(unique_id_resolvers)) - (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); - } + reject(error); + }); + }); + } - return response; - } + request_playlist_list() : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result: Playlist[] = []; - private handle_notifyclientgetnamefromdbid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; + for(const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], - const functions = this._awaiters_unique_dbid[info.client_database_id] || []; - delete this._awaiters_unique_dbid[info.client_database_id]; + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); + } + } - for(const fn of functions) - fn(info); - } - } - - async info_from_cldbid(..._cldbid: number[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_cldbid = new Set(_cldbid); - if(!unique_cldbid.size) return []; - - const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; - - - for(const cldbid of unique_cldbid) { - request.push({'cldbid': cldbid}); - (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) - .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); - } - - try { - await this.connection.send_command("clientgetnamefromdbid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; + resolve(result); + return true; } - } finally { - /* cleanup */ - for(const cldbid of Object.keys(unique_cldbid_resolvers)) - (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); - } + }; + this.handler_boss.register_single_handler(single_handler); - return response; - } + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); - private handle_notifyclientnamefromuid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - - const functions = this._awaiters_unique_ids[entry["cluid"]] || []; - delete this._awaiters_unique_ids[entry["cluid"]]; - - for(const fn of functions) - fn(info); - } - } - - request_query_list(server_id: number = undefined) : Promise { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyquerylist", - function: command => { - const json = command.arguments; - - const result = {} as QueryList; - - result.flag_all = json[0]["flag_all"]; - result.flag_own = json[0]["flag_own"]; - result.queries = []; - - for(const entry of json) { - const rentry = {} as QueryListEntry; - rentry.bounded_server = parseInt(entry["client_bound_server"]); - rentry.username = entry["client_login_name"]; - rentry.unique_id = entry["client_unique_identifier"]; - - result.queries.push(rentry); - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - let data = {}; - if(server_id !== undefined) - data["server_id"] = server_id; - - this.connection.send_command("querylist", data).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve(undefined); - return; - } - } - reject(error); - }); - }); - } - - request_playlist_list() : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistlist", - function: command => { - const json = command.arguments; - const result: Playlist[] = []; - - for(const entry of json) { - try { - result.push({ - playlist_id: parseInt(entry["playlist_id"]), - playlist_bot_id: parseInt(entry["playlist_bot_id"]), - playlist_title: entry["playlist_title"], - playlist_type: parseInt(entry["playlist_type"]), - playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), - playlist_owner_name: entry["playlist_owner_name"], - - needed_power_modify: parseInt(entry["needed_power_modify"]), - needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), - needed_power_delete: parseInt(entry["needed_power_delete"]), - needed_power_song_add: parseInt(entry["needed_power_song_add"]), - needed_power_song_move: parseInt(entry["needed_power_song_move"]), - needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistlist").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_songs(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistsonglist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); - return false; - } - - const result: PlaylistSong[] = []; - - for(const entry of json) { - try { - result.push({ - song_id: parseInt(entry["song_id"]), - song_invoker: entry["song_invoker"], - song_previous_song_id: parseInt(entry["song_previous_song_id"]), - song_url: entry["song_url"], - song_url_loader: entry["song_url_loader"], - - song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", - song_metadata: entry["song_metadata"] - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_client_list(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistclientlist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); - return false; - } - - const result: number[] = []; - - for(const entry of json) - result.push(parseInt(entry["cldbid"])); - - resolve(result.filter(e => !isNaN(e))); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { resolve([]); return; } - reject(error); - }) - }); - } + } + reject(error); + }) + }); + } - async request_clients_by_server_group(group_id: number) : Promise { - //servergroupclientlist sgid=2 - //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyservergroupclientlist", - function: command => { - if (command.arguments[0]["sgid"] != group_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); - return false; - } + request_playlist_songs(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; - try { - const result: ServerGroupClient[] = []; - for(const entry of command.arguments) - result.push({ - client_database_id: parseInt(entry["cldbid"]), - client_nickname: entry["client_nickname"], - client_unique_identifier: entry["client_unique_identifier"] - }); - resolve(result); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); - reject("failed to parse info"); - } - - return true; + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); + return false; } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } - - request_playlist_info(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistinfo", - function: command => { - const json = command.arguments[0]; - if (json["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); - return; - } + const result: PlaylistSong[] = []; + for(const entry of json) { try { - //resolve - resolve({ - playlist_id: parseInt(json["playlist_id"]), - playlist_title: json["playlist_title"], - playlist_description: json["playlist_description"], - playlist_type: parseInt(json["playlist_type"]), + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], - playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), - playlist_owner_name: json["playlist_owner_name"], - - playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", - playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", - playlist_replay_mode: parseInt(json["playlist_replay_mode"]), - playlist_current_song_id: parseInt(json["playlist_current_song_id"]), - - playlist_max_songs: parseInt(json["playlist_max_songs"]) + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] }); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); - reject("failed to parse info"); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); } - - return true; } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); - /** - * @deprecated - * Its just a workaround for the query management. - * There is no garante that the whoami trick will work forever - */ - current_virtual_server_id() : Promise { - if(this._who_am_i) - return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); - - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - function: command => { - if(command.command != "" && command.command.indexOf("=") == -1) - return false; - - this._who_am_i = command.arguments[0]; - resolve(parseInt(this._who_am_i["virtualserver_id"])); - return true; + this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; } - }; - this.handler_boss.register_single_handler(single_handler); + } + reject(error); + }) + }); + } - this.connection.send_command("whoami").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }); + request_playlist_client_list(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistclientlist", + function: command => { + const json = command.arguments; + + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); + return false; + } + + const result: number[] = []; + + for(const entry of json) + result.push(parseInt(entry["cldbid"])); + + resolve(result.filter(e => !isNaN(e))); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + reject(error); + }) + }); + } + + async request_clients_by_server_group(group_id: number) : Promise { + //servergroupclientlist sgid=2 + //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyservergroupclientlist", + function: command => { + if (command.arguments[0]["sgid"] != group_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); + return false; + } + + try { + const result: ServerGroupClient[] = []; + for(const entry of command.arguments) + result.push({ + client_database_id: parseInt(entry["cldbid"]), + client_nickname: entry["client_nickname"], + client_unique_identifier: entry["client_unique_identifier"] + }); + resolve(result); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + request_playlist_info(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + return; + } + + try { + //resolve + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + + playlist_max_songs: parseInt(json["playlist_max_songs"]) + }); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() : Promise { + if(this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + function: command => { + if(command.command != "" && command.command.indexOf("=") == -1) + return false; + + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); }); - } + }); } } \ No newline at end of file diff --git a/shared/js/connection/ConnectionBase.ts b/shared/js/connection/ConnectionBase.ts index cf975fdb..6e490b17 100644 --- a/shared/js/connection/ConnectionBase.ts +++ b/shared/js/connection/ConnectionBase.ts @@ -1,216 +1,222 @@ -namespace connection { - export interface CommandOptions { - flagset?: string[]; /* default: [] */ - process_result?: boolean; /* default: true */ +import {ConnectionHandler, ConnectionState} from "../ConnectionHandler"; +import {ServerAddress} from "../channel-tree/server"; +import {CommandResult} from "./ServerConnectionDeclaration"; +import {RecorderProfile} from "../voice/RecorderProfile"; +import {CommandHelper} from "./CommandHelper"; +import {connection} from "./HandshakeHandler"; +import HandshakeHandler = connection.HandshakeHandler; - timeout?: number /* default: 1000 */; +export interface CommandOptions { + flagset?: string[]; /* default: [] */ + process_result?: boolean; /* default: true */ + + timeout?: number /* default: 1000 */; +} +export const CommandOptionDefaults: CommandOptions = { + flagset: [], + process_result: true, + timeout: 1000 +}; + +export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; +export abstract class AbstractServerConnection { + readonly client: ConnectionHandler; + readonly command_helper: CommandHelper; + + protected constructor(client: ConnectionHandler) { + this.client = client; + + this.command_helper = new CommandHelper(this); } - export const CommandOptionDefaults: CommandOptions = { - flagset: [], - process_result: true, - timeout: 1000 + + /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ + abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; + + abstract connected() : boolean; + abstract disconnect(reason?: string) : Promise; + + abstract support_voice() : boolean; + abstract voice_connection() : voice.AbstractVoiceConnection | undefined; + + abstract command_handler_boss() : AbstractCommandHandlerBoss; + abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + + abstract get onconnectionstatechanged() : ConnectionStateListener; + abstract set onconnectionstatechanged(listener: ConnectionStateListener); + + abstract remote_address() : ServerAddress; /* only valid when connected */ + abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ + + abstract ping() : { + native: number, + javascript?: number }; +} - export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; - export abstract class AbstractServerConnection { - readonly client: ConnectionHandler; - readonly command_helper: CommandHelper; +export namespace voice { + export enum PlayerState { + PREBUFFERING, + PLAYING, + BUFFERING, + STOPPING, + STOPPED + } - protected constructor(client: ConnectionHandler) { - this.client = client; + export type LatencySettings = { + min_buffer: number; /* milliseconds */ + max_buffer: number; /* milliseconds */ + } - this.command_helper = new CommandHelper(this); + export interface VoiceClient { + client_id: number; + + callback_playback: () => any; + callback_stopped: () => any; + + callback_state_changed: (new_state: PlayerState) => any; + + get_state() : PlayerState; + + get_volume() : number; + set_volume(volume: number) : void; + + abort_replay(); + + support_latency_settings() : boolean; + + reset_latency_settings(); + latency_settings(settings?: LatencySettings) : LatencySettings; + + support_flush() : boolean; + flush(); + } + + export abstract class AbstractVoiceConnection { + readonly connection: AbstractServerConnection; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ - abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; - abstract connected() : boolean; - abstract disconnect(reason?: string) : Promise; + abstract encoding_supported(codec: number) : boolean; + abstract decoding_supported(codec: number) : boolean; - abstract support_voice() : boolean; - abstract voice_connection() : voice.AbstractVoiceConnection | undefined; + abstract register_client(client_id: number) : VoiceClient; + abstract available_clients() : VoiceClient[]; + abstract unregister_client(client: VoiceClient) : Promise; - abstract command_handler_boss() : AbstractCommandHandlerBoss; - abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + abstract voice_recorder() : RecorderProfile; + abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; - abstract get onconnectionstatechanged() : ConnectionStateListener; - abstract set onconnectionstatechanged(listener: ConnectionStateListener); + abstract get_encoder_codec() : number; + abstract set_encoder_codec(codec: number); + } +} - abstract remote_address() : ServerAddress; /* only valid when connected */ - abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ +export class ServerCommand { + command: string; + arguments: any[]; +} - abstract ping() : { - native: number, - javascript?: number - }; +export abstract class AbstractCommandHandler { + readonly connection: AbstractServerConnection; + + handler_boss: AbstractCommandHandlerBoss | undefined; + volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ + + ignore_consumed: boolean = false; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - export namespace voice { - export enum PlayerState { - PREBUFFERING, - PLAYING, - BUFFERING, - STOPPING, - STOPPED + /** + * @return If the command should be consumed + */ + abstract handle_command(command: ServerCommand) : boolean; +} + +export interface SingleCommandHandler { + name?: string; + command?: string; + timeout?: number; + + /* if the return is true then the command handler will be removed */ + function: (command: ServerCommand) => boolean; +} + +export abstract class AbstractCommandHandlerBoss { + readonly connection: AbstractServerConnection; + protected command_handlers: AbstractCommandHandler[] = []; + /* TODO: Timeout */ + protected single_command_handler: SingleCommandHandler[] = []; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + destroy() { + this.command_handlers = undefined; + this.single_command_handler = undefined; + } + + register_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; + + this.command_handlers.remove(handler); /* just to be sure */ + this.command_handlers.push(handler); + handler.handler_boss = this; + } + + unregister_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); + return; } - export type LatencySettings = { - min_buffer: number; /* milliseconds */ - max_buffer: number; /* milliseconds */ - } + this.command_handlers.remove(handler); + handler.handler_boss = undefined; + } - export interface VoiceClient { - client_id: number; - callback_playback: () => any; - callback_stopped: () => any; + register_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.push(handler); + } - callback_state_changed: (new_state: PlayerState) => any; + remove_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.remove(handler); + } - get_state() : PlayerState; + handlers() : AbstractCommandHandler[] { + return this.command_handlers; + } - get_volume() : number; - set_volume(volume: number) : void; + invoke_handle(command: ServerCommand) : boolean { + let flag_consumed = false; - abort_replay(); - - support_latency_settings() : boolean; - - reset_latency_settings(); - latency_settings(settings?: LatencySettings) : LatencySettings; - - support_flush() : boolean; - flush(); - } - - export abstract class AbstractVoiceConnection { - readonly connection: AbstractServerConnection; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; + for(const handler of this.command_handlers) { + try { + if(!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } catch(error) { + console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); } - - abstract connected() : boolean; - abstract encoding_supported(codec: number) : boolean; - abstract decoding_supported(codec: number) : boolean; - - abstract register_client(client_id: number) : VoiceClient; - abstract available_clients() : VoiceClient[]; - abstract unregister_client(client: VoiceClient) : Promise; - - abstract voice_recorder() : RecorderProfile; - abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; - - abstract get_encoder_codec() : number; - abstract set_encoder_codec(codec: number); - } - } - - export class ServerCommand { - command: string; - arguments: any[]; - } - - export abstract class AbstractCommandHandler { - readonly connection: AbstractServerConnection; - - handler_boss: AbstractCommandHandlerBoss | undefined; - volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ - - ignore_consumed: boolean = false; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; } - /** - * @return If the command should be consumed - */ - abstract handle_command(command: ServerCommand) : boolean; - } + for(const handler of [...this.single_command_handler]) { + if(handler.command && handler.command != command.command) + continue; - export interface SingleCommandHandler { - name?: string; - command?: string; - timeout?: number; - - /* if the return is true then the command handler will be removed */ - function: (command: ServerCommand) => boolean; - } - - export abstract class AbstractCommandHandlerBoss { - readonly connection: AbstractServerConnection; - protected command_handlers: AbstractCommandHandler[] = []; - /* TODO: Timeout */ - protected single_command_handler: SingleCommandHandler[] = []; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; - } - - destroy() { - this.command_handlers = undefined; - this.single_command_handler = undefined; - } - - register_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss) - throw "handler already registered"; - - this.command_handlers.remove(handler); /* just to be sure */ - this.command_handlers.push(handler); - handler.handler_boss = this; - } - - unregister_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss !== this) { - console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); - return; + try { + if(handler.function(command)) + this.single_command_handler.remove(handler); + } catch(error) { + console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); } - - this.command_handlers.remove(handler); - handler.handler_boss = undefined; } - - register_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.push(handler); - } - - remove_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.remove(handler); - } - - handlers() : AbstractCommandHandler[] { - return this.command_handlers; - } - - invoke_handle(command: ServerCommand) : boolean { - let flag_consumed = false; - - for(const handler of this.command_handlers) { - try { - if(!flag_consumed || handler.ignore_consumed) - flag_consumed = flag_consumed || handler.handle_command(command); - } catch(error) { - console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); - } - } - - for(const handler of [...this.single_command_handler]) { - if(handler.command && handler.command != command.command) - continue; - - try { - if(handler.function(command)) - this.single_command_handler.remove(handler); - } catch(error) { - console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); - } - } - - return flag_consumed; - } + return flag_consumed; } } \ No newline at end of file diff --git a/shared/js/connection/HandshakeHandler.ts b/shared/js/connection/HandshakeHandler.ts index de9c4910..f4cb14df 100644 --- a/shared/js/connection/HandshakeHandler.ts +++ b/shared/js/connection/HandshakeHandler.ts @@ -1,4 +1,15 @@ -namespace connection { +import {AbstractServerConnection} from "./ConnectionBase"; +import {ConnectParameters, DisconnectReason} from "../ConnectionHandler"; +import {profiles} from "../profiles/ConnectionProfile"; +import {profiles as iprofiles} from "../profiles/Identity"; +import {profiles as tiprofiles} from "../profiles/identities/TeamSpeakIdentity"; +import {native_client} from "../main"; +import {settings} from "../settings"; +import {CommandResult} from "./ServerConnectionDeclaration"; + +export namespace connection { + import identities = iprofiles.identities; + export interface HandshakeIdentityHandler { connection: AbstractServerConnection; @@ -48,7 +59,7 @@ namespace connection { on_teamspeak() { const type = this.profile.selected_type(); - if(type == profiles.identities.IdentitifyType.TEAMSPEAK) + if(type == identities.IdentitifyType.TEAMSPEAK) this.handshake_finished(); else { @@ -122,8 +133,8 @@ namespace connection { } /* required to keep compatibility */ - if(this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) { - data["client_key_offset"] = (this.profile.selected_identity() as profiles.identities.TeaSpeakIdentity).hash_number; + if(this.profile.selected_type() === identities.IdentitifyType.TEAMSPEAK) { + data["client_key_offset"] = (this.profile.selected_identity() as tiprofiles.identities.TeaSpeakIdentity).hash_number; } this.connection.send_command("clientinit", data).catch(error => { diff --git a/shared/js/connection/ServerConnectionDeclaration.ts b/shared/js/connection/ServerConnectionDeclaration.ts index c2a71825..1161c107 100644 --- a/shared/js/connection/ServerConnectionDeclaration.ts +++ b/shared/js/connection/ServerConnectionDeclaration.ts @@ -1,4 +1,6 @@ -enum ErrorID { +import {LaterPromise} from "../utils/helpers"; + +export enum ErrorID { NOT_IMPLEMENTED = 0x2, COMMAND_NOT_FOUND = 0x100, @@ -15,7 +17,7 @@ enum ErrorID { CONVERSATION_IS_PRIVATE = 0x2202 } -class CommandResult { +export class CommandResult { success: boolean; id: number; message: string; @@ -35,39 +37,39 @@ class CommandResult { } } -interface ClientNameInfo { +export interface ClientNameInfo { //cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9 client_unique_id: string; client_nickname: string; client_database_id: number; } -interface ClientNameFromUid { +export interface ClientNameFromUid { promise: LaterPromise, keys: string[], response: ClientNameInfo[] } -interface ServerGroupClient { +export interface ServerGroupClient { client_nickname: string; client_unique_identifier: string; client_database_id: number; } -interface QueryListEntry { +export interface QueryListEntry { username: string; unique_id: string; bounded_server: number; } -interface QueryList { +export interface QueryList { flag_own: boolean; flag_all: boolean; queries: QueryListEntry[]; } -interface Playlist { +export interface Playlist { playlist_id: number; playlist_bot_id: number; playlist_title: string; @@ -83,7 +85,7 @@ interface Playlist { needed_power_song_remove: number; } -interface PlaylistInfo { +export interface PlaylistInfo { playlist_id: number, playlist_title: string, playlist_description: string, @@ -100,7 +102,7 @@ interface PlaylistInfo { playlist_max_songs: number } -interface PlaylistSong { +export interface PlaylistSong { song_id: number; song_previous_song_id: number; song_invoker: string; diff --git a/shared/js/crypto/asn1.ts b/shared/js/crypto/asn1.ts index 8184a4aa..a9bf9b44 100644 --- a/shared/js/crypto/asn1.ts +++ b/shared/js/crypto/asn1.ts @@ -14,7 +14,7 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -namespace asn1 { +export namespace asn1 { declare class Int10 { constructor(value?: any); diff --git a/shared/js/crypto/crc32.ts b/shared/js/crypto/crc32.ts index 8122a8a2..52e14979 100644 --- a/shared/js/crypto/crc32.ts +++ b/shared/js/crypto/crc32.ts @@ -1,4 +1,4 @@ -class Crc32 { +export class Crc32 { private static readonly lookup = [ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, diff --git a/shared/js/crypto/hex.ts b/shared/js/crypto/hex.ts index 9c700341..ca6e319e 100644 --- a/shared/js/crypto/hex.ts +++ b/shared/js/crypto/hex.ts @@ -1,4 +1,4 @@ -namespace hex { +export namespace hex { export function encode(buffer) { let hexCodes = []; let view = new DataView(buffer); diff --git a/shared/js/crypto/sha.ts b/shared/js/crypto/sha.ts index 28998cff..d2057b5d 100644 --- a/shared/js/crypto/sha.ts +++ b/shared/js/crypto/sha.ts @@ -10,7 +10,7 @@ interface Window { } */ -namespace sha { +export namespace sha { /* * [js-sha1]{@link https://github.com/emn178/js-sha1} * diff --git a/shared/js/crypto/uid.ts b/shared/js/crypto/uid.ts index e69de29b..9ade46c7 100644 --- a/shared/js/crypto/uid.ts +++ b/shared/js/crypto/uid.ts @@ -0,0 +1,8 @@ +export function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} \ No newline at end of file diff --git a/shared/js/dns.ts b/shared/js/dns.ts index 866b0daf..af3070e1 100644 --- a/shared/js/dns.ts +++ b/shared/js/dns.ts @@ -1,4 +1,4 @@ -namespace dns { +export namespace dns { export interface AddressTarget { target_ip: string; target_port?: number; diff --git a/shared/js/events.ts b/shared/js/events.ts index 348621a8..9ce4d73f 100644 --- a/shared/js/events.ts +++ b/shared/js/events.ts @@ -1,4 +1,8 @@ -namespace events { +import {guid} from "./crypto/uid"; +import {PlaylistSong} from "./connection/ServerConnectionDeclaration"; +import {MusicClientEntry, SongInfo} from "./channel-tree/client"; + +export namespace events { export interface EventConvert { as() : All[T]; } diff --git a/shared/js/i18n/country.ts b/shared/js/i18n/country.ts index ce152e30..31c81dd8 100644 --- a/shared/js/i18n/country.ts +++ b/shared/js/i18n/country.ts @@ -1,5 +1,4 @@ - -namespace i18n { +export namespace i18n { interface CountryInfo { name: string; alpha_2: string; diff --git a/shared/js/i18n/localize.ts b/shared/js/i18n/localize.ts index 1e469c39..756930fe 100644 --- a/shared/js/i18n/localize.ts +++ b/shared/js/i18n/localize.ts @@ -1,13 +1,10 @@ -function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -} +import {guid} from "../crypto/uid"; +import {log, LogCategory} from "../log"; +import {MessageHelper} from "../ui/frames/chat"; +import {StaticSettings} from "../settings"; +import {createErrorModal} from "../ui/elements/modal"; -namespace i18n { +export namespace i18n { export interface TranslationKey { message: string; line?: number; diff --git a/shared/js/log.ts b/shared/js/log.ts index 4ea9d119..d900686a 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -1,6 +1,8 @@ //Used by CertAccept popup -enum LogCategory { +import {settings} from "./settings"; + +export enum LogCategory { CHANNEL, CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */ CLIENT, @@ -19,7 +21,7 @@ enum LogCategory { DNS } -namespace log { +export namespace log { export enum LogType { TRACE, DEBUG, diff --git a/shared/js/main.ts b/shared/js/main.ts index f2e910bc..354cc186 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -1,19 +1,18 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// - import spawnYesNo = Modals.spawnYesNo; +import {ConnectionHandler} from "./ConnectionHandler"; +import {bipc} from "./BrowserIPC"; +import {log, LogCategory} from "./log"; +import {profiles} from "./profiles/ConnectionProfile"; +import {Modals} from "./ui/modal/ModalConnect"; +import {settings, Settings} from "./settings"; +import {i18n} from "./i18n/localize"; +import {createInfoModal} from "./ui/elements/modal"; +import {MessageHelper} from "./ui/frames/chat"; -const js_render = window.jsrender || $; -const native_client = window.require !== undefined; +export const js_render = window.jsrender || $; +export const native_client = window.require !== undefined; -function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { +export function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) return constraints => navigator.mediaDevices.getUserMedia(constraints); @@ -24,11 +23,12 @@ function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) = return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); } -interface Window { +export interface Window { open_connected_question: () => Promise; } -function setup_close() { +export declare const nodeRequire: typeof require; +export function setup_close() { window.onbeforeunload = event => { if(profiles.requires_save()) profiles.save(); @@ -50,7 +50,7 @@ function setup_close() { })); const exit = () => { - const {remote} = require('electron'); + const {remote} = nodeRequire('electron'); remote.getCurrentWindow().close(); }; @@ -80,8 +80,8 @@ function setup_close() { }; } -declare function moment(...arguments) : any; -function setup_jsrender() : boolean { +export declare function moment(...arguments) : any; +export function setup_jsrender() : boolean { if(!js_render) { loader.critical_error("Missing jsrender extension!"); return false; @@ -115,7 +115,7 @@ function setup_jsrender() : boolean { return true; } -async function initialize() { +export async function initialize() { Settings.initialize(); try { @@ -129,7 +129,7 @@ async function initialize() { bipc.setup(); } -async function initialize_app() { +export async function initialize_app() { try { //Initialize main template const main = $("#tmpl_main").renderTag({ multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), @@ -180,7 +180,7 @@ async function initialize_app() { setup_close(); } -function str2ab8(str) { +export function str2ab8(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { @@ -190,7 +190,7 @@ function str2ab8(str) { } /* FIXME Dont use atob, because it sucks for non UTF-8 tings */ -function arrayBufferBase64(base64: string) { +export function arrayBufferBase64(base64: string) { base64 = atob(base64); const buf = new ArrayBuffer(base64.length); const bufView = new Uint8Array(buf); @@ -200,7 +200,7 @@ function arrayBufferBase64(base64: string) { return buf; } -function base64_encode_ab(source: ArrayBufferLike) { +export function base64_encode_ab(source: ArrayBufferLike) { const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let base64 = ""; diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index c781f468..55e70ad0 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -1,17 +1,24 @@ /// -enum GroupType { +import {LaterPromise} from "../utils/helpers"; +import {PermissionManager, PermissionValue} from "./PermissionManager"; +import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase"; +import {ConnectionHandler} from "../ConnectionHandler"; +import {log, LogCategory} from "../log"; +import {CommandResult} from "../connection/ServerConnectionDeclaration"; + +export enum GroupType { QUERY, TEMPLATE, NORMAL } -enum GroupTarget { +export enum GroupTarget { SERVER, CHANNEL } -class GroupProperties { +export class GroupProperties { iconid: number = 0; sortid: number = 0; @@ -19,12 +26,12 @@ class GroupProperties { namemode: number = 0; } -class GroupPermissionRequest { +export class GroupPermissionRequest { group_id: number; promise: LaterPromise; } -class Group { +export class Group { properties: GroupProperties = new GroupProperties(); readonly handle: GroupManager; @@ -63,7 +70,7 @@ class Group { } } -class GroupManager extends connection.AbstractCommandHandler { +export class GroupManager extends AbstractCommandHandler { readonly handle: ConnectionHandler; serverGroups: Group[] = []; @@ -83,7 +90,7 @@ class GroupManager extends connection.AbstractCommandHandler { this.channelGroups = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyservergrouplist": case "notifychannelgrouplist": diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 9b558d1c..960ee70c 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -2,7 +2,14 @@ /// /// -enum PermissionType { +import {ConnectionHandler} from "../ConnectionHandler"; +import {log, LogCategory} from "../log"; +import {LaterPromise} from "../utils/helpers"; +import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase"; +import LogType = log.LogType; +import {CommandResult, ErrorID} from "../connection/ServerConnectionDeclaration"; + +export enum PermissionType { B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view", B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view", @@ -352,7 +359,7 @@ enum PermissionType { I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client" } -class PermissionInfo { +export class PermissionInfo { name: string; id: number; description: string; @@ -363,21 +370,21 @@ class PermissionInfo { } } -class PermissionGroup { +export class PermissionGroup { begin: number; end: number; deep: number; name: string; } -class GroupedPermissions { +export class GroupedPermissions { group: PermissionGroup; permissions: PermissionInfo[]; children: GroupedPermissions[]; parent: GroupedPermissions; } -class PermissionValue { +export class PermissionValue { readonly type: PermissionInfo; value: number; flag_skip: boolean; @@ -411,13 +418,13 @@ class PermissionValue { } } -class NeededPermissionValue extends PermissionValue { +export class NeededPermissionValue extends PermissionValue { constructor(type, value) { super(type, value); } } -namespace permissions { +export namespace permissions { export type PermissionRequestKeys = { client_id?: number; channel_id?: number; @@ -473,13 +480,13 @@ namespace permissions { } } -type RequestLists = +export type RequestLists = "requests_channel_permissions" | "requests_client_permissions" | "requests_client_channel_permissions" | "requests_playlist_permissions" | "requests_playlist_client_permissions"; -class PermissionManager extends connection.AbstractCommandHandler { +export class PermissionManager extends AbstractCommandHandler { readonly handle: ConnectionHandler; permissionList: PermissionInfo[] = []; @@ -603,7 +610,7 @@ class PermissionManager extends connection.AbstractCommandHandler { this._cacheNeededPermissions = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyclientneededpermissions": this.onNeededPermissions(command.arguments); diff --git a/shared/js/profiles/ConnectionProfile.ts b/shared/js/profiles/ConnectionProfile.ts index f30cbe54..f46266b3 100644 --- a/shared/js/profiles/ConnectionProfile.ts +++ b/shared/js/profiles/ConnectionProfile.ts @@ -1,251 +1,260 @@ -namespace profiles { - export class ConnectionProfile { - id: string; +import {MessageHelper} from "../ui/frames/chat"; +import {createErrorModal} from "../ui/elements/modal"; +import {guid} from "../crypto/uid"; +import {decode_identity, IdentitifyType, Identity} from "./Identity"; +import {static_forum_identity} from "./identities/TeaForumIdentity"; +import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity"; +import {AbstractServerConnection} from "../connection/ConnectionBase"; +import {connection} from "../connection/HandshakeHandler"; - profile_name: string; - default_username: string; - default_password: string; +import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; - selected_identity_type: string = "unset"; - identities: { [key: string]: identities.Identity } = {}; +export class ConnectionProfile { + id: string; - constructor(id: string) { - this.id = id; - } + profile_name: string; + default_username: string; + default_password: string; - connect_username(): string { - if (this.default_username && this.default_username !== "Another TeaSpeak user") - return this.default_username; + selected_identity_type: string = "unset"; + identities: { [key: string]: Identity } = {}; - let selected = this.selected_identity(); - let name = selected ? selected.fallback_name() : undefined; - return name || "Another TeaSpeak user"; - } + constructor(id: string) { + this.id = id; + } - selected_identity(current_type?: identities.IdentitifyType): identities.Identity { - if (!current_type) - current_type = this.selected_type(); + connect_username(): string { + if (this.default_username && this.default_username !== "Another TeaSpeak user") + return this.default_username; - if (current_type === undefined) - return undefined; + let selected = this.selected_identity(); + let name = selected ? selected.fallback_name() : undefined; + return name || "Another TeaSpeak user"; + } - if (current_type == identities.IdentitifyType.TEAFORO) { - return identities.static_forum_identity(); - } else if (current_type == identities.IdentitifyType.TEAMSPEAK || current_type == identities.IdentitifyType.NICKNAME) { - return this.identities[identities.IdentitifyType[current_type].toLowerCase()]; - } + selected_identity(current_type?: IdentitifyType): Identity { + if (!current_type) + current_type = this.selected_type(); + if (current_type === undefined) return undefined; + + if (current_type == IdentitifyType.TEAFORO) { + return static_forum_identity(); + } else if (current_type == IdentitifyType.TEAMSPEAK || current_type == IdentitifyType.NICKNAME) { + return this.identities[IdentitifyType[current_type].toLowerCase()]; } - selected_type?(): identities.IdentitifyType { - return this.selected_identity_type ? identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; - } - - set_identity(type: identities.IdentitifyType, identity: identities.Identity) { - this.identities[identities.IdentitifyType[type].toLowerCase()] = identity; - } - - spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { - const identity = this.selected_identity(); - if (!identity) - return undefined; - return identity.spawn_identity_handshake_handler(connection); - } - - encode?(): string { - const identity_data = {}; - for (const key in this.identities) - if (this.identities[key]) - identity_data[key] = this.identities[key].encode(); - - return JSON.stringify({ - version: 1, - username: this.default_username, - password: this.default_password, - profile_name: this.profile_name, - identity_type: this.selected_identity_type, - identity_data: identity_data, - id: this.id - }); - } - - valid(): boolean { - const identity = this.selected_identity(); - if (!identity || !identity.valid()) return false; - - return true; - } + return undefined; } - async function decode_profile(data): Promise { - data = JSON.parse(data); - if (data.version !== 1) - return "invalid version"; - - const result: ConnectionProfile = new ConnectionProfile(data.id); - result.default_username = data.username; - result.default_password = data.password; - result.profile_name = data.profile_name; - result.selected_identity_type = (data.identity_type || "").toLowerCase(); - - if (data.identity_data) { - for (const key in data.identity_data) { - const type = identities.IdentitifyType[key.toUpperCase() as string]; - const _data = data.identity_data[key]; - if (type == undefined) continue; - - const identity = await identities.decode_identity(type, _data); - if (identity == undefined) continue; - - result.identities[key.toLowerCase()] = identity; - } - } - - return result; + selected_type?(): IdentitifyType { + return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; } - interface ProfilesData { - version: number; - profiles: string[]; + set_identity(type: IdentitifyType, identity: Identity) { + this.identities[IdentitifyType[type].toLowerCase()] = identity; } - let available_profiles: ConnectionProfile[] = []; - - export async function load() { - available_profiles = []; - - const profiles_json = localStorage.getItem("profiles"); - let profiles_data: ProfilesData = (() => { - try { - return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; - } catch (error) { - debugger; - console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); - createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); - return {version: 0}; - } - })(); - - if (profiles_data.version === 0) { - profiles_data = { - version: 1, - profiles: [] - }; - } - if (profiles_data.version == 1) { - for (const profile_data of profiles_data.profiles) { - const profile = await decode_profile(profile_data); - if (typeof (profile) === 'string') { - console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); - continue; - } - available_profiles.push(profile); - } - } - - if (!find_profile("default")) { //Create a default profile and teaforo profile - { - const profile = create_new_profile("default", "default"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "Default Profile"; - - /* generate default identity */ - try { - const identity = await identities.TeaSpeakIdentity.generate_new(); - let active = true; - setTimeout(() => { - active = false; - }, 1000); - await identity.improve_level(8, 1, () => active); - profile.set_identity(identities.IdentitifyType.TEAMSPEAK, identity); - profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAMSPEAK]; - } catch (error) { - createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); - } - } - - { /* forum identity (works only when connected to the forum) */ - const profile = create_new_profile("TeaSpeak Forum", "teaforo"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "TeaSpeak Forum profile"; - - profile.set_identity(identities.IdentitifyType.TEAFORO, identities.static_forum_identity()); - profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAFORO]; - } - - save(); - } + spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler { + const identity = this.selected_identity(); + if (!identity) + return undefined; + return identity.spawn_identity_handshake_handler(connection); } - export function create_new_profile(name: string, id?: string): ConnectionProfile { - const profile = new ConnectionProfile(id || guid()); - profile.profile_name = name; - profile.default_username = ""; - available_profiles.push(profile); - return profile; - } + encode?(): string { + const identity_data = {}; + for (const key in this.identities) + if (this.identities[key]) + identity_data[key] = this.identities[key].encode(); - let _requires_save = false; - - export function save() { - const profiles: string[] = []; - for (const profile of available_profiles) - profiles.push(profile.encode()); - - const data = JSON.stringify({ + return JSON.stringify({ version: 1, - profiles: profiles + username: this.default_username, + password: this.default_password, + profile_name: this.profile_name, + identity_type: this.selected_identity_type, + identity_data: identity_data, + id: this.id }); - localStorage.setItem("profiles", data); } - export function mark_need_save() { - _requires_save = true; + valid(): boolean { + const identity = this.selected_identity(); + if (!identity || !identity.valid()) return false; + + return true; } +} - export function requires_save(): boolean { - return _requires_save; - } +async function decode_profile(data): Promise { + data = JSON.parse(data); + if (data.version !== 1) + return "invalid version"; - export function profiles(): ConnectionProfile[] { - return available_profiles; - } + const result: ConnectionProfile = new ConnectionProfile(data.id); + result.default_username = data.username; + result.default_password = data.password; + result.profile_name = data.profile_name; + result.selected_identity_type = (data.identity_type || "").toLowerCase(); - export function find_profile(id: string): ConnectionProfile | undefined { - for (const profile of profiles()) - if (profile.id == id) - return profile; + if (data.identity_data) { + for (const key in data.identity_data) { + const type = IdentitifyType[key.toUpperCase() as string]; + const _data = data.identity_data[key]; + if (type == undefined) continue; - return undefined; - } + const identity = await decode_identity(type, _data); + if (identity == undefined) continue; - export function find_profile_by_name(name: string): ConnectionProfile | undefined { - name = name.toLowerCase(); - for (const profile of profiles()) - if ((profile.profile_name || "").toLowerCase() == name) - return profile; - - return undefined; - } - - - export function default_profile(): ConnectionProfile { - return find_profile("default"); - } - - export function set_default_profile(profile: ConnectionProfile) { - const old_default = default_profile(); - if (old_default && old_default != profile) { - old_default.id = guid(); + result.identities[key.toLowerCase()] = identity; } - profile.id = "default"; - return old_default; } - export function delete_profile(profile: ConnectionProfile) { - available_profiles.remove(profile); + return result; +} + +interface ProfilesData { + version: number; + profiles: string[]; +} + +let available_profiles: ConnectionProfile[] = []; + +export async function load() { + available_profiles = []; + + const profiles_json = localStorage.getItem("profiles"); + let profiles_data: ProfilesData = (() => { + try { + return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; + } catch (error) { + debugger; + console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); + createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); + return {version: 0}; + } + })(); + + if (profiles_data.version === 0) { + profiles_data = { + version: 1, + profiles: [] + }; } + if (profiles_data.version == 1) { + for (const profile_data of profiles_data.profiles) { + const profile = await decode_profile(profile_data); + if (typeof (profile) === 'string') { + console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); + continue; + } + available_profiles.push(profile); + } + } + + if (!find_profile("default")) { //Create a default profile and teaforo profile + { + const profile = create_new_profile("default", "default"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "Default Profile"; + + /* generate default identity */ + try { + const identity = await TeaSpeakIdentity.generate_new(); + let active = true; + setTimeout(() => { + active = false; + }, 1000); + await identity.improve_level(8, 1, () => active); + profile.set_identity(IdentitifyType.TEAMSPEAK, identity); + profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAMSPEAK]; + } catch (error) { + createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); + } + } + + { /* forum identity (works only when connected to the forum) */ + const profile = create_new_profile("TeaSpeak Forum", "teaforo"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "TeaSpeak Forum profile"; + + profile.set_identity(IdentitifyType.TEAFORO, static_forum_identity()); + profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO]; + } + + save(); + } +} + +export function create_new_profile(name: string, id?: string): ConnectionProfile { + const profile = new ConnectionProfile(id || guid()); + profile.profile_name = name; + profile.default_username = ""; + available_profiles.push(profile); + return profile; +} + +let _requires_save = false; + +export function save() { + const profiles: string[] = []; + for (const profile of available_profiles) + profiles.push(profile.encode()); + + const data = JSON.stringify({ + version: 1, + profiles: profiles + }); + localStorage.setItem("profiles", data); +} + +export function mark_need_save() { + _requires_save = true; +} + +export function requires_save(): boolean { + return _requires_save; +} + +export function profiles(): ConnectionProfile[] { + return available_profiles; +} + +export function find_profile(id: string): ConnectionProfile | undefined { + for (const profile of profiles()) + if (profile.id == id) + return profile; + + return undefined; +} + +export function find_profile_by_name(name: string): ConnectionProfile | undefined { + name = name.toLowerCase(); + for (const profile of profiles()) + if ((profile.profile_name || "").toLowerCase() == name) + return profile; + + return undefined; +} + + +export function default_profile(): ConnectionProfile { + return find_profile("default"); +} + +export function set_default_profile(profile: ConnectionProfile) { + const old_default = default_profile(); + if (old_default && old_default != profile) { + old_default.id = guid(); + } + profile.id = "default"; + return old_default; +} + +export function delete_profile(profile: ConnectionProfile) { + available_profiles.remove(profile); } \ No newline at end of file diff --git a/shared/js/profiles/Identity.ts b/shared/js/profiles/Identity.ts index c658f46d..6e4dfbdc 100644 --- a/shared/js/profiles/Identity.ts +++ b/shared/js/profiles/Identity.ts @@ -1,110 +1,116 @@ -namespace profiles.identities { - export enum IdentitifyType { - TEAFORO, - TEAMSPEAK, - NICKNAME +import {AbstractCommandHandler, AbstractServerConnection, ServerCommand} from "../connection/ConnectionBase"; +import {connection} from "../connection/HandshakeHandler"; + +import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; +import {NameIdentity} from "./identities/NameIdentity"; +import {TeaForumIdentity} from "./identities/TeaForumIdentity"; +import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity"; + +export enum IdentitifyType { + TEAFORO, + TEAMSPEAK, + NICKNAME +} + +export interface Identity { + fallback_name(): string | undefined ; + uid() : string; + type() : IdentitifyType; + + valid() : boolean; + + encode?() : string; + decode(data: string) : Promise; + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler; +} + +export async function decode_identity(type: IdentitifyType, data: string) : Promise { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeaSpeakIdentity(undefined, undefined); + break; + } + if(!identity) + return undefined; + + try { + await identity.decode(data) + } catch(error) { + /* todo better error handling! */ + console.error(error); + return undefined; } - export interface Identity { - fallback_name(): string | undefined ; - uid() : string; - type() : IdentitifyType; + return identity; +} - valid() : boolean; +export function create_identity(type: IdentitifyType) { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeaSpeakIdentity(undefined, undefined); + break; + } + return identity; +} - encode?() : string; - decode(data: string) : Promise; +export class HandshakeCommandHandler extends AbstractCommandHandler { + readonly handle: T; - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler; + constructor(connection: AbstractServerConnection, handle: T) { + super(connection); + this.handle = handle; } - export async function decode_identity(type: IdentitifyType, data: string) : Promise { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - if(!identity) - return undefined; - try { - await identity.decode(data) - } catch(error) { - /* todo better error handling! */ - console.error(error); - return undefined; + handle_command(command: ServerCommand): boolean { + if($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if(command.command == "error") { + return false; + } else { + console.warn(tr("Received unknown command while handshaking (%o)"), command); } + return true; + } +} - return identity; +export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { + connection: AbstractServerConnection; + + protected callbacks: ((success: boolean, message?: string) => any)[] = []; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - export function create_identity(type: IdentitifyType) { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - return identity; + register_callback(callback: (success: boolean, message?: string) => any) { + this.callbacks.push(callback); } - export class HandshakeCommandHandler extends connection.AbstractCommandHandler { - readonly handle: T; + abstract start_handshake(); - constructor(connection: connection.AbstractServerConnection, handle: T) { - super(connection); - this.handle = handle; - } - - - handle_command(command: connection.ServerCommand): boolean { - if($.isFunction(this[command.command])) - this[command.command](command.arguments); - else if(command.command == "error") { - return false; - } else { - console.warn(tr("Received unknown command while handshaking (%o)"), command); - } - return true; - } + protected trigger_success() { + for(const callback of this.callbacks) + callback(true); } - export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { - connection: connection.AbstractServerConnection; - - protected callbacks: ((success: boolean, message?: string) => any)[] = []; - - protected constructor(connection: connection.AbstractServerConnection) { - this.connection = connection; - } - - register_callback(callback: (success: boolean, message?: string) => any) { - this.callbacks.push(callback); - } - - abstract start_handshake(); - - protected trigger_success() { - for(const callback of this.callbacks) - callback(true); - } - - protected trigger_fail(message: string) { - for(const callback of this.callbacks) - callback(false, message); - } + protected trigger_fail(message: string) { + for(const callback of this.callbacks) + callback(false, message); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts index 51cbefc7..876f6c46 100644 --- a/shared/js/profiles/identities/NameIdentity.ts +++ b/shared/js/profiles/identities/NameIdentity.ts @@ -1,88 +1,92 @@ -/// +import {CommandResult} from "../../connection/ServerConnectionDeclaration"; +import {log, LogCategory} from "../../log"; +import {AbstractServerConnection} from "../../connection/ConnectionBase"; +import {connection} from "../../connection/HandshakeHandler"; -namespace profiles.identities { - class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: NameIdentity; - handler: HandshakeCommandHandler; +import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; +import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; - constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) { - super(connection); - this.identity = identity; +class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: NameIdentity; + handler: HandshakeCommandHandler; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); - } + constructor(connection: AbstractServerConnection, identity: NameIdentity) { + super(connection); + this.identity = identity; - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - client_nickname: this.identity.name() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }).then(() => this.trigger_success()); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); } - export class NameIdentity implements Identity { - private _name: string; + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + client_nickname: this.identity.name() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }).then(() => this.trigger_success()); + } - constructor(name?: string) { - this._name = name; - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } - set_name(name: string) { this._name = name; } + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} - name() : string { return this._name; } +export class NameIdentity implements Identity { + private _name: string; - fallback_name(): string | undefined { - return this._name; - } + constructor(name?: string) { + this._name = name; + } - uid(): string { - return btoa(this._name); //FIXME hash! - } + set_name(name: string) { this._name = name; } - type(): IdentitifyType { - return IdentitifyType.NICKNAME; - } + name() : string { return this._name; } - valid(): boolean { - return this._name != undefined && this._name.length >= 5; - } + fallback_name(): string | undefined { + return this._name; + } - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; + uid(): string { + return btoa(this._name); //FIXME hash! + } - this._name = data["name"]; - return; - } + type(): IdentitifyType { + return IdentitifyType.NICKNAME; + } - encode?() : string { - return JSON.stringify({ - version: 1, - name: this._name - }); - } + valid(): boolean { + return this._name != undefined && this._name.length >= 5; + } - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { - return new NameHandshakeHandler(connection, this); - } + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; + + this._name = data["name"]; + return; + } + + encode?() : string { + return JSON.stringify({ + version: 1, + name: this._name + }); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { + return new NameHandshakeHandler(connection, this); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeaForumIdentity.ts b/shared/js/profiles/identities/TeaForumIdentity.ts index 2f44e31e..d9224645 100644 --- a/shared/js/profiles/identities/TeaForumIdentity.ts +++ b/shared/js/profiles/identities/TeaForumIdentity.ts @@ -1,122 +1,126 @@ -/// +import {AbstractServerConnection} from "../../connection/ConnectionBase"; +import {log, LogCategory} from "../../log"; +import {CommandResult} from "../../connection/ServerConnectionDeclaration"; +import {forum} from "./teaspeak-forum"; +import {connection} from "../../connection/HandshakeHandler"; +import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; +import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; -namespace profiles.identities { - class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: TeaForumIdentity; - handler: HandshakeCommandHandler; +class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: TeaForumIdentity; + handler: HandshakeCommandHandler; - constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); - } - - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - data: this.identity.data().data_json() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - - - private handle_proof(json) { - this.connection.send_command("handshakeindentityproof", { - proof: this.identity.data().data_sign() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + constructor(connection: AbstractServerConnection, identity: TeaForumIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } - export class TeaForumIdentity implements Identity { - private readonly identity_data: forum.Data; + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + data: this.identity.data().data_json() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); - valid() : boolean { - return !!this.identity_data && !this.identity_data.is_expired(); - } - - constructor(data: forum.Data) { - this.identity_data = data; - } - - data() : forum.Data { - return this.identity_data; - } - - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; - - return; - } - - encode() : string { - return JSON.stringify({ - version: 1 - }); - } - - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { - return new TeaForumHandshakeHandler(connection, this); - } - - fallback_name(): string | undefined { - return this.identity_data ? this.identity_data.name() : undefined; - } - - type(): profiles.identities.IdentitifyType { - return IdentitifyType.TEAFORO; - } - - uid(): string { - //FIXME: Real UID! - return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); - } + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); } - let static_identity: TeaForumIdentity; - export function set_static_identity(identity: TeaForumIdentity) { - static_identity = identity; + private handle_proof(json) { + this.connection.send_command("handshakeindentityproof", { + proof: this.identity.data().data_sign() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); } - export function update_forum() { - if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { - static_identity = new TeaForumIdentity(forum.data()); - } else { - static_identity = undefined; - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); } - export function valid_static_forum_identity() : boolean { - return static_identity && static_identity.valid(); + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} + +export class TeaForumIdentity implements Identity { + private readonly identity_data: forum.Data; + + valid() : boolean { + return !!this.identity_data && !this.identity_data.is_expired(); } - export function static_forum_identity() : TeaForumIdentity | undefined { - return static_identity; + constructor(data: forum.Data) { + this.identity_data = data; } + + data() : forum.Data { + return this.identity_data; + } + + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; + + return; + } + + encode() : string { + return JSON.stringify({ + version: 1 + }); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { + return new TeaForumHandshakeHandler(connection, this); + } + + fallback_name(): string | undefined { + return this.identity_data ? this.identity_data.name() : undefined; + } + + type(): IdentitifyType { + return IdentitifyType.TEAFORO; + } + + uid(): string { + //FIXME: Real UID! + return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); + } +} + +let static_identity: TeaForumIdentity; + +export function set_static_identity(identity: TeaForumIdentity) { + static_identity = identity; +} + +export function update_forum() { + if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { + static_identity = new TeaForumIdentity(forum.data()); + } else { + static_identity = undefined; + } +} + +export function valid_static_forum_identity() : boolean { + return static_identity && static_identity.valid(); +} + +export function static_forum_identity() : TeaForumIdentity | undefined { + return static_identity; } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts index b7c5be9c..287adb02 100644 --- a/shared/js/profiles/identities/TeamSpeakIdentity.ts +++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts @@ -1,88 +1,115 @@ -/// +import {arrayBufferBase64, base64_encode_ab, str2ab8} from "../../main"; +import {sha} from "../../crypto/sha"; +import {asn1} from "../../crypto/asn1"; +import {AbstractServerConnection} from "../../connection/ConnectionBase"; +import {log, LogCategory} from "../../log"; +import {CommandResult} from "../../connection/ServerConnectionDeclaration"; +import {settings} from "../../settings"; +import {connection} from "../../connection/HandshakeHandler"; +import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; +import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; -namespace profiles.identities { - export namespace CryptoHelper { - export function base64_url_encode(str){ - return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); +export namespace CryptoHelper { + export function base64_url_encode(str){ + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); + } + + export function base64_url_decode(str: string, pad?: boolean){ + if(typeof(pad) === 'undefined' || pad) + str = (str + '===').slice(0, str.length + (str.length % 4)); + return str.replace(/-/g, '+').replace(/_/g, '/'); + } + + export function arraybuffer_to_string(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } + + export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { + /* + Tomcrypt public key export: + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + */ + + const key_data = await crypto.subtle.exportKey("jwk", crypto_key); + + let index = 0; + const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ + const buffer = new Uint8Array(length); /* fixed ASN1 length */ + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* the flags bit string */ + buffer[index++] = 0x03; /* type */ + buffer[index++] = 0x02; /* length */ + buffer[index++] = 0x07; /* data */ + buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ + } + { /* key size (const 32 for P-256) */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x01; /* length */ + buffer[index++] = 0x20; + } + try { /* Public kex X */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.x, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse x coordinate (invalid base64)"; + throw error; } - export function base64_url_decode(str: string, pad?: boolean){ - if(typeof(pad) === 'undefined' || pad) - str = (str + '===').slice(0, str.length + (str.length % 4)); - return str.replace(/-/g, '+').replace(/_/g, '/'); + try { /* Public kex Y */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.y, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; } - export function arraybuffer_to_string(buf) { - return String.fromCharCode.apply(null, new Uint16Array(buf)); - } - - export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { - /* - Tomcrypt public key export: - if (type == PK_PRIVATE) { - flags[0] = 1; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_INTEGER, 1UL, key->k, - LTC_ASN1_EOL, 0UL, NULL); - } else { - flags[0] = 0; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_EOL, 0UL, NULL); - } - - */ - - const key_data = await crypto.subtle.exportKey("jwk", crypto_key); - - let index = 0; - const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ - const buffer = new Uint8Array(length); /* fixed ASN1 length */ - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* the flags bit string */ - buffer[index++] = 0x03; /* type */ - buffer[index++] = 0x02; /* length */ - buffer[index++] = 0x07; /* data */ - buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ - } - { /* key size (const 32 for P-256) */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x01; /* length */ - buffer[index++] = 0x20; - } - try { /* Public kex X */ + if(!public_key) { + try { /* Public kex K */ buffer[index++] = 0x02; /* type */ buffer[index++] = 0x20; /* length */ - const raw = atob(base64_url_decode(key_data.x, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse x coordinate (invalid base64)"; - throw error; - } - - try { /* Public kex Y */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.y, false)); + const raw = atob(base64_url_decode(key_data.d, false)); if(raw.charCodeAt(0) > 0x7F) { buffer[index - 1] += 1; buffer[index++] = 0; @@ -95,777 +122,757 @@ namespace profiles.identities { throw "failed to parse y coordinate (invalid base64)"; throw error; } - - if(!public_key) { - try { /* Public kex K */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.d, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse y coordinate (invalid base64)"; - throw error; - } - } - - buffer[1] = index - 2; /* set the final sequence length */ - - return base64_encode_ab(buffer.buffer.slice(0, index)); } - const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; - function c_strlen(buffer: Uint8Array, offset: number) : number { - let index = 0; - while(index + offset < buffer.length && buffer[index + offset] != 0) - index++; - return index; - } + buffer[1] = index - 2; /* set the final sequence length */ - export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { - /* buffer could contains a zero! */ - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - return arraybuffer_to_string(buffer); - } - - export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - return base64_encode_ab(buffer); - } - - /** - * @param buffer base64 encoded ASN.1 string - */ - export function decode_tomcrypt_key(buffer: string) { - let decoded; - - try { - decoded = asn1.decode(atob(buffer)); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse key buffer (invalid base64)"; - throw error; - } - - let {x, y, k} = { - x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), - y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), - k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) - }; - - if(x.length > 32) { - if(x.charCodeAt(0) != 0) - throw "Invalid X coordinate! (Too long)"; - x = x.substr(1); - } - - if(y.length > 32) { - if(y.charCodeAt(0) != 0) - throw "Invalid Y coordinate! (Too long)"; - y = y.substr(1); - } - - if(k.length > 32) { - if(k.charCodeAt(0) != 0) - throw "Invalid private coordinate! (Too long)"; - k = k.substr(1); - } - - /* - console.log("Key x: %s (%d)", btoa(x), x.length); - console.log("Key y: %s (%d)", btoa(y), y.length); - console.log("Key k: %s (%d)", btoa(k), k.length); - */ - return { - crv: "P-256", - d: base64_url_encode(btoa(k)), - x: base64_url_encode(btoa(x)), - y: base64_url_encode(btoa(y)), - - ext: true, - key_ops:["deriveKey", "sign"], - kty:"EC", - }; - } + return base64_encode_ab(buffer.buffer.slice(0, index)); } - class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { - identity: TeaSpeakIdentity; - handler: HandshakeCommandHandler; + const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; + function c_strlen(buffer: Uint8Array, offset: number) : number { + let index = 0; + while(index + offset < buffer.length && buffer[index + offset] != 0) + index++; + return index; + } - constructor(connection: connection.AbstractServerConnection, identity: TeaSpeakIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { + /* buffer could contains a zero! */ + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + return arraybuffer_to_string(buffer); + } + + export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + + return base64_encode_ab(buffer); + } + + /** + * @param buffer base64 encoded ASN.1 string + */ + export function decode_tomcrypt_key(buffer: string) { + let decoded; + + try { + decoded = asn1.decode(atob(buffer)); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse key buffer (invalid base64)"; + throw error; } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - publicKey: this.identity.public_key - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + let {x, y, k} = { + x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), + y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), + k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) + }; + + if(x.length > 32) { + if(x.charCodeAt(0) != 0) + throw "Invalid X coordinate! (Too long)"; + x = x.substr(1); + } + + if(y.length > 32) { + if(y.charCodeAt(0) != 0) + throw "Invalid Y coordinate! (Too long)"; + y = y.substr(1); + } + + if(k.length > 32) { + if(k.charCodeAt(0) != 0) + throw "Invalid private coordinate! (Too long)"; + k = k.substr(1); + } + + /* + console.log("Key x: %s (%d)", btoa(x), x.length); + console.log("Key y: %s (%d)", btoa(y), y.length); + console.log("Key k: %s (%d)", btoa(k), k.length); + */ + return { + crv: "P-256", + d: base64_url_encode(btoa(k)), + x: base64_url_encode(btoa(x)), + y: base64_url_encode(btoa(y)), + + ext: true, + key_ops:["deriveKey", "sign"], + kty:"EC", + }; + } +} + +class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { + identity: TeaSpeakIdentity; + handler: HandshakeCommandHandler; + + constructor(connection: AbstractServerConnection, identity: TeaSpeakIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + publicKey: this.identity.public_key + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + + private handle_proof(json) { + if(!json[0]["digest"]) { + this.trigger_fail("server too old"); + return; + } + + this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { + this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); if(error instanceof CommandResult) error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - - private handle_proof(json) { - if(!json[0]["digest"]) { - this.trigger_fail("server too old"); - return; - } - - this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { - this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - }).catch(error => { - this.trigger_fail("failed to sign message"); - }); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + }).catch(error => { + this.trigger_fail("failed to sign message"); + }); } - class IdentityPOWWorker { - private _worker: Worker; - private _current_hash: string; - private _best_level: number; - - async initialize(key: string) { - this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - - /* initialize */ - await new Promise((resolve, reject) => { - const timeout_id = setTimeout(() => reject("timeout"), 1000); - - this._worker.onmessage = event => { - clearTimeout(timeout_id); - - if(!event.data) { - reject("invalid data"); - return; - } - - if(!event.data.success) { - reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - this._worker.onerror = event => { - log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); - clearTimeout(timeout_id); - reject("Failed to load worker (" + event.message + ")"); - }; - }); - - /* set data */ - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "set_data", - private_key: key, - code: "set_data" - }); - - const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); - - this._worker.onmessage = event => { - clearTimeout(timeout_id); - - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - }); - } - - async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { - this._current_hash = hash; - if(target < this._best_level) - return true; - - return await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "mine", - hash: this._current_hash, - iterations: iterations, - target: target, - code: "mine" - }); - - const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - if(event.data.result) { - this._best_level = event.data.level; - this._current_hash = event.data.hash; - resolve(true); - } else { - resolve(false); /* no result */ - } - }; - }); - } - - current_hash() : string { - return this._current_hash; - } - - current_level() : number { - return this._best_level; - } - - async finalize(timeout?: number) { - try { - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "finalize", - code: "finalize" - }); - - const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); - - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - resolve(); - }; - }); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); - } - - this._worker.terminate(); - this._worker = undefined; - } - - private handle_message(message: any) { - log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); } - export class TeaSpeakIdentity implements Identity { - static async generate_new() : Promise { - let key: CryptoKeyPair; - try { - key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(e) { - log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); - throw "Failed to generate keypair"; - } - const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} - const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); - await identity.initialize(); - return identity; - } +class IdentityPOWWorker { + private _worker: Worker; + private _current_hash: string; + private _best_level: number; - static async import_ts(ts_string: string, ini?: boolean) : Promise { - const parse_string = string => { - /* parsing without INI structure */ - const V_index = string.indexOf('V'); - if(V_index == -1) throw "invalid input (missing V)"; + async initialize(key: string) { + this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - return { - hash: string.substr(0, V_index), - data: string.substr(V_index + 1), - name: "TeaSpeak user" + /* initialize */ + await new Promise((resolve, reject) => { + const timeout_id = setTimeout(() => reject("timeout"), 1000); + + this._worker.onmessage = event => { + clearTimeout(timeout_id); + + if(!event.data) { + reject("invalid data"); + return; } + + if(!event.data.success) { + reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); }; + this._worker.onerror = event => { + log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); + clearTimeout(timeout_id); + reject("Failed to load worker (" + event.message + ")"); + }; + }); - const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { - /* parsing with INI structure */ - let identity: string, name: string; - - for(const line of ts_string.split("\n")) { - if(line.startsWith("identity=")) - identity = line.substr(9); - else if(line.startsWith("nickname=")) - name = line.substr(9); - } - - if(!identity) throw "missing identity keyword"; - identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; - if(!identity) throw "invalid identity key value"; - - const result = parse_string(identity); - result.name = name || result.name; - return result; - })(); - - if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - - let buffer; - try { - buffer = new Uint8Array(arrayBufferBase64(data)); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); - throw "failed to base data (base64 decode failed)"; - } - const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - - const identity = new TeaSpeakIdentity(key64, hash, name, false); - await identity.initialize(); - return identity; - } - - hash_number: string; /* hash suffix for the private key */ - private_key: string; /* base64 representation of the private key */ - _name: string; - - public_key: string; /* only set when initialized */ - - private _initialized: boolean; - private _crypto_key: CryptoKey; - private _crypto_key_sign: CryptoKey; - - private _unique_id: string; - - constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { - this.private_key = private_key; - this.hash_number = hash || "0"; - this._name = name; - - if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { - this.initialize().catch(error => { - log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); - this._initialized = false; - }); - } - } - - fallback_name(): string | undefined { - return this._name; - } - - uid(): string { - return this._unique_id; - } - - type(): IdentitifyType { - return IdentitifyType.TEAMSPEAK; - } - - valid(): boolean { - return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; - } - - async decode(data: string) : Promise { - const json = JSON.parse(data); - if(!json) throw "invalid json"; - - if(json.version == 2) { - this.private_key = json.key; - this.hash_number = json.hash; - this._name = json.name; - } else if(json.version == 1) { - const key = json.key; - this._name = json.name; - - const clone = await TeaSpeakIdentity.import_ts(key, false); - this.private_key = clone.private_key; - this.hash_number = clone.hash_number; - } else - throw "invalid version"; - - await this.initialize(); - } - - encode?() : string { - return JSON.stringify({ - key: this.private_key, - hash: this.hash_number, - name: this._name, - version: 2 + /* set data */ + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "set_data", + private_key: key, + code: "set_data" }); - } - async level() : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; + const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); - const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + this._worker.onmessage = event => { + clearTimeout(timeout_id); - let level = 0; - while(level < hash.byteLength && hash[level] == 0) - level++; - - if(level >= hash.byteLength) { - level = 256; - } else { - let byte = hash[level]; - level <<= 3; - while((byte & 0x1) == 0) { - level++; - byte >>= 1; + if (!event.data) { + reject("invalid data"); + return; } - } - return level; - } + if (!event.data.success) { + reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } - /** - * @param {string} a - * @param {string} b - * @description b must be smaller (in bytes) then a - */ - private string_add(a: string, b: string) { - const char_result: number[] = []; - const char_a = [...a].reverse().map(e => e.charCodeAt(0)); - const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + }); + } - let carry = false; - while(char_b.length > 0) { - let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } + async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { + this._current_hash = hash; + if(target < this._best_level) + return true; - while(char_a.length > 0) { - let result = char_a.pop_front() + (carry ? 1 : 0); - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } + return await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "mine", + hash: this._current_hash, + iterations: iterations, + target: target, + code: "mine" + }); - if(carry) - char_result.push(49); + const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - return String.fromCharCode.apply(null, char_result.slice().reverse()); - } + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } - async improve_level_for(time: number, threads: number) : Promise { - let active = true; - setTimeout(() => active = false, time); + if (!event.data.success) { + reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } - return await this.improve_level(-1, threads, () => active); - } - - async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; - if(target == -1) /* get the highest level possible */ - target = 0; - else if(target <= await this.level()) - return true; - - const workers: IdentityPOWWorker[] = []; - - const iterations = 100000; - let current_hash; - const next_hash = () => { - if(!current_hash) - return (current_hash = this.hash_number); - - if(current_hash.length < iterations.toString().length) { - current_hash = this.string_add(iterations.toString(), current_hash); + if(event.data.result) { + this._best_level = event.data.level; + this._current_hash = event.data.hash; + resolve(true); } else { - current_hash = this.string_add(current_hash, iterations.toString()); + resolve(false); /* no result */ } - return current_hash; }; + }); + } - { /* init */ - const initialize_promise: Promise[] = []; - for(let index = 0; index < threads; index++) { - const worker = new IdentityPOWWorker(); - workers.push(worker); - initialize_promise.push(worker.initialize(this.public_key)); - } + current_hash() : string { + return this._current_hash; + } - try { - await Promise.all(initialize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to initialize"; - } + current_level() : number { + return this._best_level; + } + + async finalize(timeout?: number) { + try { + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + + clearTimeout(timeout_id); + + if (!event.data) { + reject("invalid data"); + return; + } + + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + resolve(); + }; + }); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } + + this._worker.terminate(); + this._worker = undefined; + } + + private handle_message(message: any) { + log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); + } +} + +export class TeaSpeakIdentity implements Identity { + static async generate_new() : Promise { + let key: CryptoKeyPair; + try { + key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(e) { + log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); + throw "Failed to generate keypair"; + } + const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); + + const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); + await identity.initialize(); + return identity; + } + + static async import_ts(ts_string: string, ini?: boolean) : Promise { + const parse_string = string => { + /* parsing without INI structure */ + const V_index = string.indexOf('V'); + if(V_index == -1) throw "invalid input (missing V)"; + + return { + hash: string.substr(0, V_index), + data: string.substr(V_index + 1), + name: "TeaSpeak user" + } + }; + + const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { + /* parsing with INI structure */ + let identity: string, name: string; + + for(const line of ts_string.split("\n")) { + if(line.startsWith("identity=")) + identity = line.substr(9); + else if(line.startsWith("nickname=")) + name = line.substr(9); } - let result = false; - let best_level = 0; - let target_level = target > 0 ? target : await this.level() + 1; + if(!identity) throw "missing identity keyword"; + identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; + if(!identity) throw "invalid identity key value"; - const worker_promise: Promise[] = []; + const result = parse_string(identity); + result.name = name || result.name; + return result; + })(); - const hash_timestamps: number[] = []; - let last_hashrate_update: number = 0; + if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - const update_hashrate = () => { - if(!callback_status) return; - const now = Date.now(); - hash_timestamps.push(now); + let buffer; + try { + buffer = new Uint8Array(arrayBufferBase64(data)); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); + throw "failed to base data (base64 decode failed)"; + } + const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - if(last_hashrate_update + 1000 < now) { - last_hashrate_update = now; + const identity = new TeaSpeakIdentity(key64, hash, name, false); + await identity.initialize(); + return identity; + } - const timeout = now - 10 * 1000; /* 10s */ - const rounds = hash_timestamps.filter(e => e > timeout); - callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) - } - }; + hash_number: string; /* hash suffix for the private key */ + private_key: string; /* base64 representation of the private key */ + _name: string; + + public_key: string; /* only set when initialized */ + + private _initialized: boolean; + private _crypto_key: CryptoKey; + private _crypto_key_sign: CryptoKey; + + private _unique_id: string; + + constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { + this.private_key = private_key; + this.hash_number = hash || "0"; + this._name = name; + + if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { + this.initialize().catch(error => { + log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); + this._initialized = false; + }); + } + } + + fallback_name(): string | undefined { + return this._name; + } + + uid(): string { + return this._unique_id; + } + + type(): IdentitifyType { + return IdentitifyType.TEAMSPEAK; + } + + valid(): boolean { + return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; + } + + async decode(data: string) : Promise { + const json = JSON.parse(data); + if(!json) throw "invalid json"; + + if(json.version == 2) { + this.private_key = json.key; + this.hash_number = json.hash; + this._name = json.name; + } else if(json.version == 1) { + const key = json.key; + this._name = json.name; + + const clone = await TeaSpeakIdentity.import_ts(key, false); + this.private_key = clone.private_key; + this.hash_number = clone.hash_number; + } else + throw "invalid version"; + + await this.initialize(); + } + + encode?() : string { + return JSON.stringify({ + key: this.private_key, + hash: this.hash_number, + name: this._name, + version: 2 + }); + } + + async level() : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + + const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + + let level = 0; + while(level < hash.byteLength && hash[level] == 0) + level++; + + if(level >= hash.byteLength) { + level = 256; + } else { + let byte = hash[level]; + level <<= 3; + while((byte & 0x1) == 0) { + level++; + byte >>= 1; + } + } + + return level; + } + + /** + * @param {string} a + * @param {string} b + * @description b must be smaller (in bytes) then a + */ + private string_add(a: string, b: string) { + const char_result: number[] = []; + const char_a = [...a].reverse().map(e => e.charCodeAt(0)); + const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + + let carry = false; + while(char_b.length > 0) { + let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; + if((carry = result > 57)) + result -= 10; + char_result.push(result); + } + + while(char_a.length > 0) { + let result = char_a.pop_front() + (carry ? 1 : 0); + if((carry = result > 57)) + result -= 10; + char_result.push(result); + } + + if(carry) + char_result.push(49); + + return String.fromCharCode.apply(null, char_result.slice().reverse()); + } + + + async improve_level_for(time: number, threads: number) : Promise { + let active = true; + setTimeout(() => active = false, time); + + return await this.improve_level(-1, threads, () => active); + } + + async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + if(target == -1) /* get the highest level possible */ + target = 0; + else if(target <= await this.level()) + return true; + + const workers: IdentityPOWWorker[] = []; + + const iterations = 100000; + let current_hash; + const next_hash = () => { + if(!current_hash) + return (current_hash = this.hash_number); + + if(current_hash.length < iterations.toString().length) { + current_hash = this.string_add(iterations.toString(), current_hash); + } else { + current_hash = this.string_add(current_hash, iterations.toString()); + } + return current_hash; + }; + + { /* init */ + const initialize_promise: Promise[] = []; + for(let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } try { - result = await new Promise((resolve, reject) => { - let active = true; + await Promise.all(initialize_promise); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } + } - const exit = () => { - const timeout = setTimeout(() => resolve(true), 1000); - Promise.all(worker_promise).then(result => { - clearTimeout(timeout); - resolve(true); - }).catch(error => resolve(true)); - active = false; + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : await this.level() + 1; + + const worker_promise: Promise[] = []; + + const hash_timestamps: number[] = []; + let last_hashrate_update: number = 0; + + const update_hashrate = () => { + if(!callback_status) return; + const now = Date.now(); + hash_timestamps.push(now); + + if(last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + + const timeout = now - 10 * 1000; /* 10s */ + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) + } + }; + + try { + result = await new Promise((resolve, reject) => { + let active = true; + + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; + }; + + for(const worker of workers) { + const worker_mine = () => { + if(!active) return; + + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + + worker_promise.remove(p); + + if(result.valueOf()) { + if(worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + + log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if(callback_level) + callback_level(best_level); + } + + if(active) { + if(target > 0) + exit(); + else + target_level = best_level + 1; + } + } + + if(active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + + log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + + return Promise.resolve(); + }); + + worker_promise.push(p); }; - for(const worker of workers) { - const worker_mine = () => { - if(!active) return; - - const promise = worker.mine(next_hash(), iterations, target_level); - const p = promise.then(result => { - update_hashrate(); - - worker_promise.remove(p); - - if(result.valueOf()) { - if(worker.current_level() > best_level) { - this.hash_number = worker.current_hash(); - - log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); - best_level = worker.current_level(); - if(callback_level) - callback_level(best_level); - } - - if(active) { - if(target > 0) - exit(); - else - target_level = best_level + 1; - } - } - - if(active && (active = active_callback())) - setTimeout(() => worker_mine(), 0); - else { - exit(); - } - - return Promise.resolve(); - }).catch(error => { - worker_promise.remove(p); - - log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); - reject(error); - - return Promise.resolve(); - }); - - worker_promise.push(p); - }; - - worker_mine(); - } - }); - } catch(error) { - //error already printed before reject had been called - } - - { /* shutdown */ - const finalize_promise: Promise[] = []; - for(const worker of workers) - finalize_promise.push(worker.finalize(250)); - - try { - await Promise.all(finalize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to finalize"; + worker_mine(); } - } - - - return result; + }); + } catch(error) { + //error already printed before reject had been called } - private async initialize() { - if(!this.private_key) - throw "Invalid private key"; - - let jwk: any; - try { - jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); - if(!jwk) - throw "result undefined"; - } catch(error) { - throw "failed to parse key (" + error + ")"; - } + { /* shutdown */ + const finalize_promise: Promise[] = []; + for(const worker of workers) + finalize_promise.push(worker.finalize(250)); try { - this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); + await Promise.all(finalize_promise); } catch(error) { log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto sign key"; + throw "failed to finalize"; } - - try { - this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto key"; - } - - try { - this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); - this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to calculate unique id"; - } - - this._initialized = true; - //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); } - async export_ts(ini?: boolean) : Promise { - if(!this.private_key) - throw "Invalid private key"; - const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); - if(!ini) return identity; + return result; + } - return "[Identity]\n" + - "id=TeaWeb-Exported\n" + - "identity=\"" + identity + "\"\n" + - "nickname=\"" + this.fallback_name() + "\"\n" + - "phonetic_nickname="; + private async initialize() { + if(!this.private_key) + throw "Invalid private key"; + + let jwk: any; + try { + jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); + if(!jwk) + throw "result undefined"; + } catch(error) { + throw "failed to parse key (" + error + ")"; } - async sign_message(message: string, hash: string = "SHA-256") : Promise { - /* bring this to libtomcrypt format */ - const sign_buffer = await crypto.subtle.sign({ - name: "ECDSA", - hash: hash - }, this._crypto_key_sign, str2ab8(message)); - const sign = new Uint8Array(sign_buffer); - /* first 32 r bits | last 32 s bits */ - - const buffer = new Uint8Array(72); - let index = 0; - - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* integer r */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[0] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = sign[i]; - } - { /* integer s */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[32] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = sign[32 + i]; - } - buffer[1] = index - 2; - - return base64_encode_ab(buffer.subarray(0, index)); + try { + this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto sign key"; } - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { - return new TeaSpeakHandshakeHandler(connection, this); + try { + this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto key"; } + + try { + this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); + this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to calculate unique id"; + } + + this._initialized = true; + //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); + } + + async export_ts(ini?: boolean) : Promise { + if(!this.private_key) + throw "Invalid private key"; + + const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); + if(!ini) return identity; + + return "[Identity]\n" + + "id=TeaWeb-Exported\n" + + "identity=\"" + identity + "\"\n" + + "nickname=\"" + this.fallback_name() + "\"\n" + + "phonetic_nickname="; + } + + async sign_message(message: string, hash: string = "SHA-256") : Promise { + /* bring this to libtomcrypt format */ + const sign_buffer = await crypto.subtle.sign({ + name: "ECDSA", + hash: hash + }, this._crypto_key_sign, str2ab8(message)); + const sign = new Uint8Array(sign_buffer); + /* first 32 r bits | last 32 s bits */ + + const buffer = new Uint8Array(72); + let index = 0; + + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* integer r */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[0] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[i]; + } + { /* integer s */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[32] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[32 + i]; + } + buffer[1] = index - 2; + + return base64_encode_ab(buffer.subarray(0, index)); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection): HandshakeIdentityHandler { + return new TeaSpeakHandshakeHandler(connection, this); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/teaspeak-forum.ts b/shared/js/profiles/identities/teaspeak-forum.ts index 1ab6cd53..802cb87f 100644 --- a/shared/js/profiles/identities/teaspeak-forum.ts +++ b/shared/js/profiles/identities/teaspeak-forum.ts @@ -1,8 +1,11 @@ -interface Window { +import {Settings, settings} from "../../settings"; +import {update_forum} from "./TeaForumIdentity"; + +declare interface Window { grecaptcha: GReCaptcha; } -interface GReCaptcha { +export interface GReCaptcha { render(container: string | HTMLElement, parameters: { sitekey: string; theme?: "dark" | "light"; @@ -18,10 +21,10 @@ interface GReCaptcha { reset(widget_id?: string); } -namespace forum { +export namespace forum { export namespace gcaptcha { export async function initialize() { - if(typeof(window.grecaptcha) === "undefined") { + if(typeof((window as any).grecaptcha) === "undefined") { let script = document.createElement("script"); script.async = true; @@ -50,7 +53,7 @@ namespace forum { } } - if(typeof(window.grecaptcha) === "undefined") + if(typeof((window as any).grecaptcha) === "undefined") throw tr("failed to load recaptcha"); } @@ -62,9 +65,9 @@ namespace forum { throw tr("initialisation failed"); } if(container.attr("captcha-uuid")) - window.grecaptcha.reset(container.attr("captcha-uuid")); + (window as any).grecaptcha.reset(container.attr("captcha-uuid")); else { - container.attr("captcha-uuid", window.grecaptcha.render(container[0], { + container.attr("captcha-uuid", (window as any).grecaptcha.render(container[0], { "sitekey": key, callback: callback_data })); @@ -206,7 +209,7 @@ namespace forum { localStorage.setItem("teaspeak-forum-data", response["data"]); localStorage.setItem("teaspeak-forum-sign", response["sign"]); localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); - profiles.identities.update_forum(); + update_forum(); } catch(error) { console.error(tr("Failed to parse forum given data: %o"), error); return { @@ -266,7 +269,7 @@ namespace forum { _data = new Data(_data.auth_key, response["data"], response["sign"]); localStorage.setItem("teaspeak-forum-data", response["data"]); localStorage.setItem("teaspeak-forum-sign", response["sign"]); - profiles.identities.update_forum(); + update_forum(); } catch(error) { console.error(tr("Failed to parse forum given data: %o"), error); throw tr("failed to parse data"); @@ -320,7 +323,7 @@ namespace forum { localStorage.removeItem("teaspeak-forum-data"); localStorage.removeItem("teaspeak-forum-sign"); localStorage.removeItem("teaspeak-forum-auth"); - profiles.identities.update_forum(); + update_forum(); } loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { diff --git a/shared/js/proto.ts b/shared/js/proto.ts index 18a162a7..37ee32e9 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -1,5 +1,3 @@ -//Used by CertAccept popup - interface Array { remove(elem?: T): boolean; last?(): T; diff --git a/shared/js/settings.ts b/shared/js/settings.ts index 59a9c8eb..d90dc807 100644 --- a/shared/js/settings.ts +++ b/shared/js/settings.ts @@ -1,6 +1,9 @@ /// //Used by CertAccept popup +import {log, LogCategory} from "./log"; +import {createErrorModal} from "./ui/elements/modal"; + if(typeof(customElements) !== "undefined") { try { class X_Properties extends HTMLElement {} @@ -14,7 +17,7 @@ if(typeof(customElements) !== "undefined") { } /* T = value type */ -interface SettingsKey { +export interface SettingsKey { key: string; fallback_keys?: string | string[]; @@ -25,7 +28,7 @@ interface SettingsKey { require_restart?: boolean; } -class SettingsBase { +export class SettingsBase { protected static readonly UPDATE_DIRECT: boolean = true; protected static transformStO?(input?: string, _default?: T, default_type?: string) : T { @@ -77,7 +80,7 @@ class SettingsBase { } } -class StaticSettings extends SettingsBase { +export class StaticSettings extends SettingsBase { private static _instance: StaticSettings; static get instance() : StaticSettings { if(!this._instance) @@ -139,7 +142,7 @@ class StaticSettings extends SettingsBase { } } -class Settings extends StaticSettings { +export class Settings extends StaticSettings { static readonly KEY_USER_IS_NEW: SettingsKey = { key: 'user_is_new_user', default_value: true @@ -433,7 +436,7 @@ class Settings extends StaticSettings { } } -class ServerSettings extends SettingsBase { +export class ServerSettings extends SettingsBase { private cacheServer = {}; private _server_unique_id: string; private _server_save_worker: NodeJS.Timer; @@ -511,4 +514,4 @@ class ServerSettings extends SettingsBase { } } -let settings: Settings; \ No newline at end of file +export let settings: Settings; \ No newline at end of file diff --git a/shared/js/sound/Sounds.ts b/shared/js/sound/Sounds.ts index cbb75329..f2992312 100644 --- a/shared/js/sound/Sounds.ts +++ b/shared/js/sound/Sounds.ts @@ -1,4 +1,8 @@ -enum Sound { +import {settings} from "../settings"; +import {log, LogCategory} from "../log"; +import {ConnectionHandler} from "../ConnectionHandler"; + +export enum Sound { SOUND_TEST = "sound.test", SOUND_EGG = "sound.egg", @@ -61,7 +65,7 @@ enum Sound { GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self" } -namespace sound { +export namespace sound { export interface SoundHandle { key: string; filename: string; diff --git a/shared/js/stats.ts b/shared/js/stats.ts index ce9ca84b..cefdfff4 100644 --- a/shared/js/stats.ts +++ b/shared/js/stats.ts @@ -1,4 +1,6 @@ -namespace stats { +import {log, LogCategory} from "./log"; + +export namespace stats { const LOG_PREFIX = "[Statistics] "; export enum CloseCodes { diff --git a/shared/js/ui/channel-tree/channel.css b/shared/js/ui/channel-tree/channel.css new file mode 100644 index 00000000..49a762ab --- /dev/null +++ b/shared/js/ui/channel-tree/channel.css @@ -0,0 +1,81 @@ +.channel-container { + display: flex; + flex-direction: column; +} +.channel-container .container-channel { + position: relative; + display: flex; + flex-direction: row; + justify-content: stretch; + width: 100%; + min-height: 16px; + align-items: center; + cursor: pointer; +} +.channel-container .container-channel .channel-type { + flex-grow: 0; + flex-shrink: 0; + margin-right: 2px; +} +.channel-container .container-channel .container-channel-name { + display: flex; + flex-direction: row; + flex-grow: 1; + flex-shrink: 1; + justify-content: left; + max-width: 100%; + /* important for the repetitive channel name! */ + overflow-x: hidden; + height: 16px; +} +.channel-container .container-channel .container-channel-name.align-right { + justify-content: right; +} +.channel-container .container-channel .container-channel-name.align-center, .channel-container .container-channel .container-channel-name.align-repetitive { + justify-content: center; +} +.channel-container .container-channel .container-channel-name .channel-name { + align-self: center; + color: #828282; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.channel-container .container-channel .container-channel-name.align-repetitive .channel-name { + text-overflow: clip; +} +.channel-container .container-channel .icons { + display: flex; + flex-direction: row; + flex-grow: 0; + flex-shrink: 0; +} +.channel-container .container-channel.move-selected { + border-bottom: 1px solid black; +} +.channel-container .container-channel .show-channel-normal-only { + display: none; +} +.channel-container .container-channel .show-channel-normal-only.channel-normal { + display: block; +} +.channel-container .container-channel .icon_no_sound { + position: relative; + display: flex; +} +.channel-container .container-channel .icon_no_sound .background { + width: 10px; + height: 14px; + background: red; + position: absolute; + top: 1px; + left: 3px; + z-index: -1; +} +.channel-container .container-clients { + display: flex; + flex-direction: column; +} + +/*# sourceMappingURL=channel.css.map */ diff --git a/shared/js/ui/channel-tree/channel.css.map b/shared/js/ui/channel-tree/channel.css.map new file mode 100644 index 00000000..5c2199e9 --- /dev/null +++ b/shared/js/ui/channel-tree/channel.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["channel.scss","colors.scss"],"names":[],"mappings":"AAEA;EACI;EACA;;AAEA;EACI;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACI;EACA;EAEA;;AAGJ;EACI;EACA;EAEA;EACA;EAEA;EAEA;AAAiB;EACjB;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA,OC/CW;EDiDX;EACA;EACA;EACA;;AAIA;EACI;;AAKZ;EACI;EACA;EAEA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EAEA;EACA;EACA;EACA;EACA;;AAKZ;EACI;EACA","file":"channel.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/channel.scss b/shared/js/ui/channel-tree/channel.scss index e69de29b..6fa7ebbc 100644 --- a/shared/js/ui/channel-tree/channel.scss +++ b/shared/js/ui/channel-tree/channel.scss @@ -0,0 +1,106 @@ +@import "colors"; + +.channel-container { + display: flex; + flex-direction: column; + + .container-channel { + position: relative; + + display: flex; + flex-direction: row; + justify-content: stretch; + + width: 100%; + min-height: 16px; + + align-items: center; + cursor: pointer; + + .channel-type { + flex-grow: 0; + flex-shrink: 0; + + margin-right: 2px; + } + + .container-channel-name { + display: flex; + flex-direction: row; + + flex-grow: 1; + flex-shrink: 1; + + justify-content: left; + + max-width: 100%; /* important for the repetitive channel name! */ + overflow-x: hidden; + height: 16px; + + &.align-right { + justify-content: right; + } + + &.align-center, &.align-repetitive { + justify-content: center; + } + + .channel-name { + align-self: center; + color: $channel-tree-entry-color; + + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &.align-repetitive { + .channel-name { + text-overflow: clip; + } + } + } + + .icons { + display: flex; + flex-direction: row; + + flex-grow: 0; + flex-shrink: 0; + } + + &.move-selected { + border-bottom: 1px solid black; + } + + .show-channel-normal-only { + display: none; + + &.channel-normal { + display: block; + } + } + + .icon_no_sound { + position: relative; + display: flex; + + .background { + width: 10px; + height: 14px; + + background: red; + position: absolute; + top: 1px; + left: 3px; + z-index: -1; + } + } + } + + .container-clients { + display: flex; + flex-direction: column; + } +} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/channel.tsx b/shared/js/ui/channel-tree/channel.tsx index e69de29b..3e24be3b 100644 --- a/shared/js/ui/channel-tree/channel.tsx +++ b/shared/js/ui/channel-tree/channel.tsx @@ -0,0 +1,120 @@ +import * as React from "react"; +import {ChannelEntry} from "../../channel-tree/channel"; + +const cssChannel = require("./channel.scss"); +const cssTree = require("./tree.scss"); + +class ChannelIcon extends React.Component<{ channel: ChannelEntry }, {}> { + channel_icon_classes() { + const class_list = []; + const channel = this.props.channel; + + if(channel.formattedChannelName() !== channel.channelName()) + return "channel-spacer"; + + let icon_color; + if(channel.properties.channel_flag_password && !channel.cached_password()) + icon_color = "yellow"; + else if(channel.properties.channel_flag_maxclients_unlimited && channel.clients().length >= channel.properties.channel_maxclients) + icon_color = "red"; + else if(channel.properties.channel_flag_maxfamilyclients_unlimited && channel.clients(true).length >= channel.properties.channel_maxfamilyclients) + icon_color = "red"; + else + icon_color = "green"; + + + return "channel-normal client-channel_" + icon_color + (channel.flag_subscribed ? "_subscribed" : ""); + } + + render() { + return
; + } +} + +class ChannelIcons extends React.Component<{channel: ChannelEntry}, {}> { + render_icon(target: HTMLDivElement) { + const props = this.props.channel.properties; + if(!props.channel_icon_id) return; + + const tag = this.props.channel.channelTree.client.fileManager.icons.generateTag(props.channel_icon_id); + tag.appendTo($(target)); + } + + render() { + const icons = []; + + const props = this.props.channel.properties; + if(props.channel_flag_default) { + icons.push(
); + } + if(props.channel_flag_password) { + icons.push(
); + } + if(props.channel_codec_quality > 4) { + icons.push(
); + } + if(props.channel_needed_talk_power > 0) { + icons.push(
); + } + if(props.channel_icon_id != 0) { + icons.push(
this.render_icon(e) }/>) + } + if(!audio.codec.supported(props.channel_codec)) { + icons.push(
+
+
+
) + } + + return (
{icons}
); + } +} + +class ChannelLine extends React.Component<{ channel: ChannelEntry }, {}> { + + render() { + let depth = 1; + let parent = this.props.channel; + while((parent = parent.parent)) + depth++; + + //TODO: On handle spacer alignments in channel name! + return ( +
+
+ + ); + } +} + +class ChannelClientsView extends React.Component<{}, {}> { +} + +class SubChannelView extends React.Component<{ channels: ChannelEntry[] }, {}> { + + render() { + return this.props.channels.map(e => ); + } +} + +export class Channel extends React.Component<{ channel: ChannelEntry }, {}> { + children: ChannelEntry[]; + + channel_entry() : ChannelEntry { return this.props.channel; } + + render() { + return ( +
+ + + +
+ ); + } +} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/colors.css b/shared/js/ui/channel-tree/colors.css new file mode 100644 index 00000000..f48e716e --- /dev/null +++ b/shared/js/ui/channel-tree/colors.css @@ -0,0 +1,3 @@ + + +/*# sourceMappingURL=colors.css.map */ diff --git a/shared/js/ui/channel-tree/colors.css.map b/shared/js/ui/channel-tree/colors.css.map new file mode 100644 index 00000000..6ee84d1b --- /dev/null +++ b/shared/js/ui/channel-tree/colors.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"colors.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/colors.scss b/shared/js/ui/channel-tree/colors.scss index e69de29b..b12d3a2e 100644 --- a/shared/js/ui/channel-tree/colors.scss +++ b/shared/js/ui/channel-tree/colors.scss @@ -0,0 +1,3 @@ + +$channel-tree-new-message-color: #a814147F; +$channel-tree-entry-color: #828282; \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.css b/shared/js/ui/channel-tree/tree.css new file mode 100644 index 00000000..3f5f7faf --- /dev/null +++ b/shared/js/ui/channel-tree/tree.css @@ -0,0 +1,38 @@ +/* Some general browser helpers */ +.tree-container .tree .entry .depth-filler { + font-size: 16px; + flex-shrink: 0; + flex-grow: 0; +} +.tree-container .marker-text-unread { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 1px; + background-color: #a814147F; + opacity: 1; + -moz-transition: opacity 0.25s; + -o-transition: opacity 0.25s; + -webkit-transition: opacity 0.25s; + transition: opacity 0.25s; +} +.tree-container .marker-text-unread:before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 24px; + background: -moz-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); + /* FF3.6-15 */ + background: -webkit-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); + /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); + /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ +} +.tree-container .marker-text-unread.hidden { + opacity: 0; +} + +/*# sourceMappingURL=tree.css.map */ diff --git a/shared/js/ui/channel-tree/tree.css.map b/shared/js/ui/channel-tree/tree.css.map new file mode 100644 index 00000000..ab305210 --- /dev/null +++ b/shared/js/ui/channel-tree/tree.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../../../css/static/mixin.scss","tree.scss"],"names":[],"mappings":"AAAA;ACQY;EACI;EAEA;EACA;;AAKZ;EACI;EACA;EACA;EACA;EAEA;EACA;EAEA;EDvBP,iBC4CO;ED3CP,eC2CO;ED1CP,oBC0CO;EDzCP,YCyCO;;AAnBA;EACI;EACA;EAEA;EACA;EACA;EAEA;EAEA;AAAyF;EACzF;AAA2F;EAC3F;AAAuF;;AAG3F;EACI","file":"tree.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.scss b/shared/js/ui/channel-tree/tree.scss index e69de29b..b54c059a 100644 --- a/shared/js/ui/channel-tree/tree.scss +++ b/shared/js/ui/channel-tree/tree.scss @@ -0,0 +1,50 @@ +@import "../../../css/static/mixin"; +@import "../../../css/static/properties"; + +.tree-container { + .tree { + + .entry { + + .depth-filler { + font-size: 16px; + + flex-shrink: 0; + flex-grow: 0; + } + } + } + + .marker-text-unread { + position: absolute; + left: 0; + top: 0; + bottom: 0; + + width: 1px; + background-color: #a814147F; + + opacity: 1; + + &:before { + content: ''; + position: absolute; + + left: 0; + top: 0; + bottom: 0; + + width: 24px; + + background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + } + + &.hidden { + opacity: 0; + } + + @include transition(opacity $button_hover_animation_time); + } +} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.tsx b/shared/js/ui/channel-tree/tree.tsx index e69de29b..ea705ddf 100644 --- a/shared/js/ui/channel-tree/tree.tsx +++ b/shared/js/ui/channel-tree/tree.tsx @@ -0,0 +1,5 @@ +import * as React from "react"; + +export class TreeView { + +} \ No newline at end of file diff --git a/shared/js/ui/client_move.ts b/shared/js/ui/client_move.ts index 5ccfa8a1..c99f7c61 100644 --- a/shared/js/ui/client_move.ts +++ b/shared/js/ui/client_move.ts @@ -1,6 +1,4 @@ -/// - -class ClientMover { +export class ClientMover { static readonly listener_root = $(document); static readonly move_element = $("#mouse-move"); readonly channel_tree: ChannelTree; diff --git a/shared/js/ui/elements/context_divider.ts b/shared/js/ui/elements/context_divider.ts index 3ec65814..c0426898 100644 --- a/shared/js/ui/elements/context_divider.ts +++ b/shared/js/ui/elements/context_divider.ts @@ -1,10 +1,14 @@ +import {settings} from "../../settings"; +import {log, LogCategory} from "../../log"; + +declare const $: any; interface JQuery { dividerfy() : this; } if(!$.fn.dividerfy) { $.fn.dividerfy = function(this: JQuery) : JQuery { - this.find(".container-seperator").each(function (this: T) { + (this as any).find(".container-seperator").each(function (this: T) { if(!this.previousElementSibling) return; if(!this.nextElementSibling) return; diff --git a/shared/js/ui/elements/context_menu.ts b/shared/js/ui/elements/context_menu.ts index f56eb023..c3543e2e 100644 --- a/shared/js/ui/elements/context_menu.ts +++ b/shared/js/ui/elements/context_menu.ts @@ -1,4 +1,4 @@ -namespace contextmenu { +export namespace contextmenu { export interface MenuEntry { callback?: () => void; type: MenuEntryType; diff --git a/shared/js/ui/elements/modal.ts b/shared/js/ui/elements/modal.ts index a0030309..68838f63 100644 --- a/shared/js/ui/elements/modal.ts +++ b/shared/js/ui/elements/modal.ts @@ -1,13 +1,13 @@ -/// +import {KeyCode} from "../../PPTListener"; -enum ElementType { +export enum ElementType { HEADER, BODY, FOOTER } -type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; -const ModalFunctions = { +export type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; +export const ModalFunctions = { divify: function (val: JQuery) { if(val.length > 1) return $.spawn("div").append(val); @@ -54,7 +54,7 @@ const ModalFunctions = { } }; -class ModalProperties { +export class ModalProperties { template?: string; header: BodyCreator = () => "HEADER"; body: BodyCreator = () => "BODY"; @@ -89,7 +89,7 @@ class ModalProperties { full_size?: boolean = false; } -namespace modal { +export namespace modal { export function initialize_modals() { register_global_events(); } @@ -184,7 +184,7 @@ let _global_modal_count = 0; let _global_modal_last: HTMLElement; let _global_modal_last_time: number; -class Modal { +export class Modal { private _htmlTag: JQuery; properties: ModalProperties; shown: boolean; @@ -296,11 +296,11 @@ class Modal { } } -function createModal(data: ModalProperties | any) : Modal { +export function createModal(data: ModalProperties | any) : Modal { return new Modal(ModalFunctions.warpProperties(data)); } -class InputModalProperties extends ModalProperties { +export class InputModalProperties extends ModalProperties { maxLength?: number; field_title?: string; @@ -310,7 +310,7 @@ class InputModalProperties extends ModalProperties { error_message?: string; } -function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { +export function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { props = ModalFunctions.warpProperties(props); props.template_properties || (props.template_properties = {}); props.template_properties.field_title = props.field_title; @@ -370,7 +370,7 @@ function createInputModal(headMessage: BodyCreator, question: BodyCreator, valid return modal; } -function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +export function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; @@ -382,7 +382,7 @@ function createErrorModal(header: BodyCreator, message: BodyCreator, props: Moda return modal; } -function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +export function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; @@ -393,73 +393,3 @@ function createInfoModal(header: BodyCreator, message: BodyCreator, props: Modal modal.htmlTag.find(".modal-body").addClass("modal-info"); return modal; } - -/* extend jquery */ - -interface ModalElements { - header?: BodyCreator; - body?: BodyCreator; - footer?: BodyCreator; -} - -interface JQuery { - modalize(entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal; -} - -$.fn.modalize = function (this: JQuery, entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal { - properties = properties || {} as ModalProperties; - entry_callback = entry_callback || ((a,b,c) => undefined); - - let tag_modal = this[0].tagName.toLowerCase() == "modal" ? this : undefined; /* TODO may throw exception? */ - - let tag_head = tag_modal ? tag_modal.find("modal-header") : ModalFunctions.jqueriefy(properties.header); - let tag_body = tag_modal ? tag_modal.find("modal-body") : this; - let tag_footer = tag_modal ? tag_modal.find("modal-footer") : ModalFunctions.jqueriefy(properties.footer); - - const result = entry_callback(tag_head as any, tag_body, tag_footer as any) || {}; - properties.header = result.header || tag_head; - properties.body = result.body || tag_body; - properties.footer = result.footer || tag_footer; - return createModal(properties); -}; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/shared/js/ui/elements/net_graph.ts b/shared/js/ui/elements/net_graph.ts index 5e440872..a605d1cc 100644 --- a/shared/js/ui/elements/net_graph.ts +++ b/shared/js/ui/elements/net_graph.ts @@ -1,4 +1,4 @@ -namespace net.graph { +export namespace net.graph { export type Entry = { timestamp: number; diff --git a/shared/js/ui/elements/slider.ts b/shared/js/ui/elements/slider.ts index 0e956585..83c2f77c 100644 --- a/shared/js/ui/elements/slider.ts +++ b/shared/js/ui/elements/slider.ts @@ -1,4 +1,4 @@ -interface SliderOptions { +export interface SliderOptions { min_value?: number; max_value?: number; initial_value?: number; @@ -8,11 +8,11 @@ interface SliderOptions { value_field?: JQuery | JQuery[]; } -interface Slider { +export interface Slider { value(value?: number) : number; } -function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { +export function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { options = Object.assign( { initial_value: 0, min_value: 0, diff --git a/shared/js/ui/elements/tooltip.ts b/shared/js/ui/elements/tooltip.ts index b64f96b5..89627bbe 100644 --- a/shared/js/ui/elements/tooltip.ts +++ b/shared/js/ui/elements/tooltip.ts @@ -1,8 +1,8 @@ -function tooltip(entry: JQuery) { +export function tooltip(entry: JQuery) { return tooltip.initialize(entry); } -namespace tooltip { +export namespace tooltip { let _global_tooltip: JQuery; export type Handle = { show(); diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index b2d6dad3..ff205531 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -13,12 +13,27 @@ client_away_message Value: '' */ -let control_bar: ControlBar; /* global variable to access the control bar */ +import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler"; +import {top_menu} from "./MenuBar"; +import {Settings, settings} from "../../settings"; +import {createErrorModal, createInfoModal, createInputModal} from "../elements/modal"; +import {default_recorder} from "../../voice/RecorderProfile"; +import {sound, Sound} from "../../sound/Sounds"; +import {Modals} from "../modal/ModalConnect"; +import {Modal as ModalsS} from "../modal/ModalSettings"; +import {log} from "./server_log"; +import {MessageHelper} from "./chat"; +import {CommandResult} from "../../connection/ServerConnectionDeclaration"; +import {PermissionType} from "../../permission/PermissionManager"; +import {bookmarks} from "../../bookmarks"; +import {contextmenu} from "../elements/context_menu"; -type MicrophoneState = "disabled" | "muted" | "enabled"; -type HeadphoneState = "muted" | "enabled"; -type AwayState = "away-global" | "away" | "online"; -class ControlBar { +export let control_bar: ControlBar; /* global variable to access the control bar */ + +export type MicrophoneState = "disabled" | "muted" | "enabled"; +export type HeadphoneState = "muted" | "enabled"; +export type AwayState = "away-global" | "away" | "online"; +export class ControlBar { private _button_away_active: AwayState; private _button_microphone: MicrophoneState; private _button_speakers: HeadphoneState; @@ -421,7 +436,7 @@ class ControlBar { } private on_open_settings() { - Modals.spawnSettingsModal(); + ModalsS.spawnSettingsModal(); } private on_open_connect() { diff --git a/shared/js/ui/frames/MenuBar.ts b/shared/js/ui/frames/MenuBar.ts index 04885afa..10fb83cb 100644 --- a/shared/js/ui/frames/MenuBar.ts +++ b/shared/js/ui/frames/MenuBar.ts @@ -1,4 +1,12 @@ -namespace top_menu { +import {bookmarks} from "../../bookmarks"; +import {Modals} from "../modal/ModalBookmarks"; +import {DisconnectReason} from "../../ConnectionHandler"; +import {control_bar} from "./ControlBar"; +import {createErrorModal} from "../elements/modal"; +import {PermissionType} from "../../permission/PermissionManager"; +import {Sound} from "../../sound/Sounds"; + +export namespace top_menu { export interface HRItem { } export interface MenuItem { diff --git a/shared/js/ui/frames/chat.ts b/shared/js/ui/frames/chat.ts index 9de0d55b..c0f38ee0 100644 --- a/shared/js/ui/frames/chat.ts +++ b/shared/js/ui/frames/chat.ts @@ -1,4 +1,6 @@ -enum ChatType { +import {log, LogCategory} from "../../log"; + +export enum ChatType { GENERAL, SERVER, CHANNEL, @@ -6,7 +8,7 @@ enum ChatType { } declare const xbbcode: any; -namespace MessageHelper { +export namespace MessageHelper { export function htmlEscape(message: string) : string[] { const div = document.createElement('div'); div.innerText = message; diff --git a/shared/js/ui/frames/chat_frame.ts b/shared/js/ui/frames/chat_frame.ts index b871ce95..6921f9b4 100644 --- a/shared/js/ui/frames/chat_frame.ts +++ b/shared/js/ui/frames/chat_frame.ts @@ -1,5 +1,11 @@ /* the bar on the right with the chats (Channel & Client) */ -namespace chat { +import {ChannelEntry} from "../../channel-tree/channel"; +import {Modals} from "../modal/ModalMusicManage"; +import {MessageHelper} from "./chat"; +import {ServerEntry} from "../../channel-tree/server"; +import {ConnectionHandler} from "../../ConnectionHandler"; + +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/connection_handlers.ts b/shared/js/ui/frames/connection_handlers.ts index 4d9fb936..be3347f2 100644 --- a/shared/js/ui/frames/connection_handlers.ts +++ b/shared/js/ui/frames/connection_handlers.ts @@ -1,7 +1,11 @@ +import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler"; +import {Settings, settings} from "../../settings"; +import {control_bar} from "./ControlBar"; +import {top_menu} from "./MenuBar"; -let server_connections: ServerConnectionManager; +export let server_connections: ServerConnectionManager; -class ServerConnectionManager { +export class ServerConnectionManager { private connection_handlers: ConnectionHandler[] = []; private active_handler: ConnectionHandler | undefined; diff --git a/shared/js/ui/frames/hostbanner.ts b/shared/js/ui/frames/hostbanner.ts index 2b0466d1..6dffddb5 100644 --- a/shared/js/ui/frames/hostbanner.ts +++ b/shared/js/ui/frames/hostbanner.ts @@ -1,4 +1,8 @@ -class Hostbanner { +import {ConnectionHandler} from "../../ConnectionHandler"; +import {Settings, settings} from "../../settings"; +import {log, LogCategory} from "../../log"; + +export class Hostbanner { readonly html_tag: JQuery; readonly client: ConnectionHandler; diff --git a/shared/js/ui/frames/image_preview.ts b/shared/js/ui/frames/image_preview.ts index af46412f..ee69ca3f 100644 --- a/shared/js/ui/frames/image_preview.ts +++ b/shared/js/ui/frames/image_preview.ts @@ -1,4 +1,4 @@ -namespace image_preview { +export namespace image_preview { let preview_overlay: JQuery; let container_image: JQuery; let button_open_in_browser: JQuery; diff --git a/shared/js/ui/frames/server_log.ts b/shared/js/ui/frames/server_log.ts index d6e29f22..8107611a 100644 --- a/shared/js/ui/frames/server_log.ts +++ b/shared/js/ui/frames/server_log.ts @@ -1,566 +1,571 @@ -namespace log { - export namespace server { - export enum Type { - CONNECTION_BEGIN = "connection_begin", - CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", - CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", - CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", - CONNECTION_LOGIN = "connection_login", - CONNECTION_CONNECTED = "connection_connected", - CONNECTION_FAILED = "connection_failed", +import {ConnectionHandler, ViewReasonId} from "../../ConnectionHandler"; +import {PermissionInfo} from "../../permission/PermissionManager"; +import {htmltags} from "../htmltags"; +import {MessageHelper} from "./chat"; +import {i18n} from "../../i18n/localize"; - DISCONNECTED = "disconnected", +export namespace server { + export enum Type { + CONNECTION_BEGIN = "connection_begin", + CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", + CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", + CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", + CONNECTION_LOGIN = "connection_login", + CONNECTION_CONNECTED = "connection_connected", + CONNECTION_FAILED = "connection_failed", - CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", - CONNECTION_COMMAND_ERROR = "connection_command_error", + DISCONNECTED = "disconnected", - GLOBAL_MESSAGE = "global_message", + CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", + CONNECTION_COMMAND_ERROR = "connection_command_error", - SERVER_WELCOME_MESSAGE = "server_welcome_message", - SERVER_HOST_MESSAGE = "server_host_message", - SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", + GLOBAL_MESSAGE = "global_message", - SERVER_CLOSED = "server_closed", - SERVER_BANNED = "server_banned", - SERVER_REQUIRES_PASSWORD = "server_requires_password", + SERVER_WELCOME_MESSAGE = "server_welcome_message", + SERVER_HOST_MESSAGE = "server_host_message", + SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", - CLIENT_VIEW_ENTER = "client_view_enter", - CLIENT_VIEW_LEAVE = "client_view_leave", - CLIENT_VIEW_MOVE = "client_view_move", + SERVER_CLOSED = "server_closed", + SERVER_BANNED = "server_banned", + SERVER_REQUIRES_PASSWORD = "server_requires_password", - CLIENT_NICKNAME_CHANGED = "client_nickname_changed", - CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", + CLIENT_VIEW_ENTER = "client_view_enter", + CLIENT_VIEW_LEAVE = "client_view_leave", + CLIENT_VIEW_MOVE = "client_view_move", - CLIENT_SERVER_GROUP_ADD = "client_server_group_add", - CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", - CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", + CLIENT_NICKNAME_CHANGED = "client_nickname_changed", + CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", - CHANNEL_CREATE = "channel_create", - CHANNEL_DELETE = "channel_delete", + CLIENT_SERVER_GROUP_ADD = "client_server_group_add", + CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", + CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", - ERROR_CUSTOM = "error_custom", - ERROR_PERMISSION = "error_permission", + CHANNEL_CREATE = "channel_create", + CHANNEL_DELETE = "channel_delete", - RECONNECT_SCHEDULED = "reconnect_scheduled", - RECONNECT_EXECUTE = "reconnect_execute", - RECONNECT_CANCELED = "reconnect_canceled" - } + ERROR_CUSTOM = "error_custom", + ERROR_PERMISSION = "error_permission", - export namespace base { - export type Client = { - client_unique_id: string; - client_name: string; - client_id: number; - } - export type Channel = { - channel_id: number; - channel_name: string; - } - export type Server = { - server_name: string; - server_unique_id: string; - } - export type ServerAddress = { - server_hostname: string; - server_port: number; - } - } - - export namespace event { - export type GlobalMessage = { - sender: base.Client; - message: string; - } - export type ConnectBegin = { - address: base.ServerAddress; - client_nickname: string; - } - export type ErrorCustom = { - message: string; - } - - export type ReconnectScheduled = { - timeout: number; - } - - export type ReconnectCanceled = { } - export type ReconnectExecute = { } - - export type ErrorPermission = { - permission: PermissionInfo; - } - - export type WelcomeMessage = { - message: string; - } - - export type HostMessageDisconnect = { - message: string; - } - - export type ClientMove = { - channel_from?: base.Channel; - channel_from_own: boolean; - - channel_to?: base.Channel; - channel_to_own: boolean; - - client: base.Client; - client_own: boolean; - - invoker?: base.Client; - - message?: string; - reason: ViewReasonId; - } - - export type ClientEnter = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ClientLeave = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ChannelCreate = { - creator: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ChannelDelete = { - deleter: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ConnectionConnected = { - own_client: base.Client; - } - export type ConnectionFailed = {}; - export type ConnectionLogin = {} - export type ConnectionHostnameResolve = {}; - export type ConnectionHostnameResolved = { - address: base.ServerAddress; - } - export type ConnectionHostnameResolveError = { - message: string; - } - - export type ConnectionVoiceSetupFailed = { - reason: string; - reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ - } - - export type ConnectionCommandError = { - error: any; - } - - export type ClientNicknameChanged = { - own_client: boolean; - - client: base.Client; - - old_name: string; - new_name: string; - } - - export type ClientNicknameChangeFailed = { - reason: string; - } - - export type ServerClosed = { - message: string; - } - - export type ServerRequiresPassword = {} - - export type ServerBanned = { - message: string; - time: number; - - invoker: base.Client; - } - } - - export type LogMessage = { - type: Type; - timestamp: number; - data: any; - } - - export interface TypeInfo { - "connection_begin" : event.ConnectBegin; - "global_message": event.GlobalMessage; - - "error_custom": event.ErrorCustom; - "error_permission": event.ErrorPermission; - - "connection_hostname_resolved": event.ConnectionHostnameResolved; - "connection_hostname_resolve": event.ConnectionHostnameResolve; - "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; - "connection_failed": event.ConnectionFailed; - "connection_login": event.ConnectionLogin; - "connection_connected": event.ConnectionConnected; - "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; - "connection_command_error": event.ConnectionCommandError; - - "reconnect_scheduled": event.ReconnectScheduled; - "reconnect_canceled": event.ReconnectCanceled; - "reconnect_execute": event.ReconnectExecute; - - "server_welcome_message": event.WelcomeMessage; - "server_host_message": event.WelcomeMessage; - "server_host_message_disconnect": event.HostMessageDisconnect; - - "server_closed": event.ServerClosed; - "server_requires_password": event.ServerRequiresPassword; - "server_banned": event.ServerBanned; - - "client_view_enter": event.ClientEnter; - "client_view_move": event.ClientMove; - "client_view_leave": event.ClientLeave; - - "client_nickname_change_failed": event.ClientNicknameChangeFailed, - "client_nickname_changed": event.ClientNicknameChanged, - - "channel_create": event.ChannelCreate; - "channel_delete": event.ChannelDelete; - - "disconnected": any; - } - - export type MessageBuilderOptions = {}; - export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; - - export const MessageBuilders: {[key: string]: MessageBuilder} = { - "error_custom": (data: event.ErrorCustom, options) => { - return [$.spawn("div").addClass("log-error").text(data.message)] - } - }; + RECONNECT_SCHEDULED = "reconnect_scheduled", + RECONNECT_EXECUTE = "reconnect_execute", + RECONNECT_CANCELED = "reconnect_canceled" } - export class ServerLog { - private readonly handle: ConnectionHandler; - private history_length: number = 100; + export namespace base { + export type Client = { + client_unique_id: string; + client_name: string; + client_id: number; + } + export type Channel = { + channel_id: number; + channel_name: string; + } + export type Server = { + server_name: string; + server_unique_id: string; + } + export type ServerAddress = { + server_hostname: string; + server_port: number; + } + } - private _log: server.LogMessage[] = []; - private _html_tag: JQuery; - private _log_container: JQuery; - private auto_follow: boolean; /* automatic scroll to bottom */ - private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ - - constructor(handle: ConnectionHandler) { - this.handle = handle; - this.auto_follow = true; - - this._html_tag = $.spawn("div").addClass("container-log"); - this._log_container = $.spawn("div").addClass("container-messages"); - this._log_container.appendTo(this._html_tag); - - this._html_tag.on('scroll', event => { - if(Date.now() - this._ignore_event < 100) { - this._ignore_event = 0; - return; - } - - this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; - }); + export namespace event { + export type GlobalMessage = { + sender: base.Client; + message: string; + } + export type ConnectBegin = { + address: base.ServerAddress; + client_nickname: string; + } + export type ErrorCustom = { + message: string; } - log(type: T, data: server.TypeInfo[T]) { - const event = { - data: data, - timestamp: Date.now(), - type: type as any - }; - - this._log.push(event); - while(this._log.length > this.history_length) - this._log.pop_front(); - - this.append_log(event); + export type ReconnectScheduled = { + timeout: number; } - html_tag() : JQuery { - return this._html_tag; + export type ReconnectCanceled = { } + export type ReconnectExecute = { } + + export type ErrorPermission = { + permission: PermissionInfo; } - destroy() { - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._log_container = undefined; - - this._log = undefined; + export type WelcomeMessage = { + message: string; } - private _scroll_task: number; + export type HostMessageDisconnect = { + message: string; + } - private append_log(message: server.LogMessage) { - let container = $.spawn("div").addClass("log-message"); + export type ClientMove = { + channel_from?: base.Channel; + channel_from_own: boolean; - /* build timestamp */ - { - const num = number => ('00' + number).substr(-2); - const date = new Date(message.timestamp); - $.spawn("div") - .addClass("timestamp") - .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") - .appendTo(container); + channel_to?: base.Channel; + channel_to_own: boolean; + + client: base.Client; + client_own: boolean; + + invoker?: base.Client; + + message?: string; + reason: ViewReasonId; + } + + export type ClientEnter = { + channel_from?: base.Channel; + channel_to?: base.Channel; + + client: base.Client; + invoker?: base.Client; + + message?: string; + own_channel: boolean; + + reason: ViewReasonId; + ban_time?: number; + } + + export type ClientLeave = { + channel_from?: base.Channel; + channel_to?: base.Channel; + + client: base.Client; + invoker?: base.Client; + + message?: string; + own_channel: boolean; + + reason: ViewReasonId; + ban_time?: number; + } + + export type ChannelCreate = { + creator: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ChannelDelete = { + deleter: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ConnectionConnected = { + own_client: base.Client; + } + export type ConnectionFailed = {}; + export type ConnectionLogin = {} + export type ConnectionHostnameResolve = {}; + export type ConnectionHostnameResolved = { + address: base.ServerAddress; + } + export type ConnectionHostnameResolveError = { + message: string; + } + + export type ConnectionVoiceSetupFailed = { + reason: string; + reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ + } + + export type ConnectionCommandError = { + error: any; + } + + export type ClientNicknameChanged = { + own_client: boolean; + + client: base.Client; + + old_name: string; + new_name: string; + } + + export type ClientNicknameChangeFailed = { + reason: string; + } + + export type ServerClosed = { + message: string; + } + + export type ServerRequiresPassword = {} + + export type ServerBanned = { + message: string; + time: number; + + invoker: base.Client; + } + } + + export type LogMessage = { + type: Type; + timestamp: number; + data: any; + } + + export interface TypeInfo { + "connection_begin" : event.ConnectBegin; + "global_message": event.GlobalMessage; + + "error_custom": event.ErrorCustom; + "error_permission": event.ErrorPermission; + + "connection_hostname_resolved": event.ConnectionHostnameResolved; + "connection_hostname_resolve": event.ConnectionHostnameResolve; + "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; + "connection_failed": event.ConnectionFailed; + "connection_login": event.ConnectionLogin; + "connection_connected": event.ConnectionConnected; + "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; + "connection_command_error": event.ConnectionCommandError; + + "reconnect_scheduled": event.ReconnectScheduled; + "reconnect_canceled": event.ReconnectCanceled; + "reconnect_execute": event.ReconnectExecute; + + "server_welcome_message": event.WelcomeMessage; + "server_host_message": event.WelcomeMessage; + "server_host_message_disconnect": event.HostMessageDisconnect; + + "server_closed": event.ServerClosed; + "server_requires_password": event.ServerRequiresPassword; + "server_banned": event.ServerBanned; + + "client_view_enter": event.ClientEnter; + "client_view_move": event.ClientMove; + "client_view_leave": event.ClientLeave; + + "client_nickname_change_failed": event.ClientNicknameChangeFailed, + "client_nickname_changed": event.ClientNicknameChanged, + + "channel_create": event.ChannelCreate; + "channel_delete": event.ChannelDelete; + + "disconnected": any; + } + + export type MessageBuilderOptions = {}; + export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; + + export const MessageBuilders: {[key: string]: MessageBuilder} = { + "error_custom": (data: event.ErrorCustom, options) => { + return [$.spawn("div").addClass("log-error").text(data.message)] + } + }; +} + +export class ServerLog { + private readonly handle: ConnectionHandler; + private history_length: number = 100; + + private _log: server.LogMessage[] = []; + private _html_tag: JQuery; + private _log_container: JQuery; + private auto_follow: boolean; /* automatic scroll to bottom */ + private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ + + constructor(handle: ConnectionHandler) { + this.handle = handle; + this.auto_follow = true; + + this._html_tag = $.spawn("div").addClass("container-log"); + this._log_container = $.spawn("div").addClass("container-messages"); + this._log_container.appendTo(this._html_tag); + + this._html_tag.on('scroll', event => { + if(Date.now() - this._ignore_event < 100) { + this._ignore_event = 0; + return; } - /* build message data */ - { - const builder = server.MessageBuilders[message.type]; - if(!builder) { - MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); - } else { - const elements = builder(message.data, {}); - if(!elements || elements.length == 0) - return; /* discard message */ - container.append(...elements); - } + this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; + }); + } + + log(type: T, data: server.TypeInfo[T]) { + const event = { + data: data, + timestamp: Date.now(), + type: type as any + }; + + this._log.push(event); + while(this._log.length > this.history_length) + this._log.pop_front(); + + this.append_log(event); + } + + html_tag() : JQuery { + return this._html_tag; + } + + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._log_container = undefined; + + this._log = undefined; + } + + private _scroll_task: number; + + private append_log(message: server.LogMessage) { + let container = $.spawn("div").addClass("log-message"); + + /* build timestamp */ + { + const num = number => ('00' + number).substr(-2); + const date = new Date(message.timestamp); + $.spawn("div") + .addClass("timestamp") + .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") + .appendTo(container); + } + + /* build message data */ + { + const builder = server.MessageBuilders[message.type]; + if(!builder) { + MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); + } else { + const elements = builder(message.data, {}); + if(!elements || elements.length == 0) + return; /* discard message */ + container.append(...elements); } - this._ignore_event = Date.now(); - this._log_container.append(container); + } + this._ignore_event = Date.now(); + this._log_container.append(container); - /* max history messages! */ - const messages = this._log_container.children(); - let index = 0; - while(messages.length - index > this.history_length) - index++; - const hide_elements = messages.filter(idx => idx < index); - hide_elements.hide(250, () => hide_elements.remove()); + /* max history messages! */ + const messages = this._log_container.children(); + let index = 0; + while(messages.length - index > this.history_length) + index++; + const hide_elements = messages.filter(idx => idx < index); + hide_elements.hide(250, () => hide_elements.remove()); - if(this.auto_follow) { - clearTimeout(this._scroll_task); + if(this.auto_follow) { + clearTimeout(this._scroll_task); - /* do not enforce a recalculate style here */ - this._scroll_task = setTimeout(() => { - this._html_tag.scrollTop(this._html_tag[0].scrollHeight); - this._scroll_task = 0; - }, 5) as any; - } + /* do not enforce a recalculate style here */ + this._scroll_task = setTimeout(() => { + this._html_tag.scrollTop(this._html_tag[0].scrollHeight); + this._scroll_task = 0; + }, 5) as any; } } } /* impl of the parsers */ -namespace log { - export namespace server { - namespace impl { - const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ - client_unique_id: client.client_unique_id, - client_id: client.client_id, - client_name: client.client_name, - add_braces: braces - }); - const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ - channel_display_name: channel.channel_name, - channel_name: channel.channel_name, - channel_id: channel.channel_id, - add_braces: braces - }); +export namespace server { + namespace impl { + import MessageBuilders = server.MessageBuilders; + import base = server.base; + import tra = i18n.tra; + const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ + client_unique_id: client.client_unique_id, + client_id: client.client_id, + client_name: client.client_name, + add_braces: braces + }); + const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ + channel_display_name: channel.channel_name, + channel_name: channel.channel_name, + channel_id: channel.channel_id, + add_braces: braces + }); - MessageBuilders["connection_begin"] = (data: event.ConnectBegin, options) => { - return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); - }; + MessageBuilders["connection_begin"] = (data: server.event.ConnectBegin, options) => { + return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); + }; - MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname")); - MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); - MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); + MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname")); + MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); + MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); - MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in...")); - MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed.")); - MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); + MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in...")); + MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed.")); + MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); - MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { - return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); - }; + MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { + return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); + }; - MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { - return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); - }; + MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { + return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); + }; - MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { - if(data.reason == ViewReasonId.VREASON_SYSTEM) { - return undefined; - } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - /* client appeared */ - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); - } - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), - client_tag(data.client), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), - client_tag(data.client), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } + MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { + if(data.reason == ViewReasonId.VREASON_SYSTEM) { + return undefined; + } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + /* client appeared */ + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); } - return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; - }; - - MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { - if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker) ); - } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to) + channel_tag(data.channel_to), + client_tag(data.invoker) ); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), + } + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "" ); - } - return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; - }; - - MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { - if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { - return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { - return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), client_tag(data.client), - channel_tag(data.channel_from), + channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "" ); - } else if(data.reason == ViewReasonId.VREASON_BAN) { - let duration = "permanently"; - if(data.ban_time) - duration = "for " + formatDate(data.ban_time); - return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"), - client_tag(data.client), - duration, - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { - return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); } + } + return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; + }; - return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; - }; + MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { + if(data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to) + ); + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } + return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; + }; - MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; + MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { + if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { + return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { + return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), + client_tag(data.client), + channel_tag(data.channel_from), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else if(data.reason == ViewReasonId.VREASON_BAN) { + let duration = "permanently"; + if(data.ban_time) + duration = "for " + formatDate(data.ban_time); + return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"), + client_tag(data.client), + duration, + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { + return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); + } - MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; + return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; + }; - MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { - if(data.own_client) { - return MessageHelper.formatMessage(tr("Nickname successfully changed.")); - } else { - return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); - } - }; + MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; - MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { - return []; /* we do not show global messages within log */ - }; + MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; - MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server")); + MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { + if(data.own_client) { + return MessageHelper.formatMessage(tr("Nickname successfully changed.")); + } else { + return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); + } + }; - MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { - return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now"))) - }; + MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { + return []; /* we do not show global messages within log */ + }; - MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { - return tra("Canceled reconnect.") - }; + MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server")); - MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { - return tra("Reconnecting...") - }; + MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { + return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now"))) + }; - MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { - let result: JQuery[]; + MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { + return tra("Canceled reconnect.") + }; - const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second")); - if(data.invoker.client_id > 0) { - if(data.message) - result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); - else - result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); - } else { - if(data.message) - result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); - else - result = tra("You've been banned from the server for {0}.", time); - } + MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { + return tra("Reconnecting...") + }; - return result.map(e => e.addClass("log-error")); - }; - } + MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { + let result: JQuery[]; + + const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second")); + if(data.invoker.client_id > 0) { + if(data.message) + result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); + else + result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); + } else { + if(data.message) + result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); + else + result = tra("You've been banned from the server for {0}.", time); + } + + return result.map(e => e.addClass("log-error")); + }; } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/chat_box.ts b/shared/js/ui/frames/side/chat_box.ts index 863f9335..12474fcf 100644 --- a/shared/js/ui/frames/side/chat_box.ts +++ b/shared/js/ui/frames/side/chat_box.ts @@ -1,4 +1,4 @@ -namespace chat { +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/chat_helper.ts b/shared/js/ui/frames/side/chat_helper.ts index c3e7396f..79e3bf55 100644 --- a/shared/js/ui/frames/side/chat_helper.ts +++ b/shared/js/ui/frames/side/chat_helper.ts @@ -1,4 +1,4 @@ -namespace chat { +export namespace chat { export namespace helpers { //https://regex101.com/r/YQbfcX/2 //static readonly URL_REGEX = /^(?([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?(?:[^\s?]+)?)(?:\?(?\S+))?)?$/gm; diff --git a/shared/js/ui/frames/side/client_info.ts b/shared/js/ui/frames/side/client_info.ts index 9847b42b..cacbed19 100644 --- a/shared/js/ui/frames/side/client_info.ts +++ b/shared/js/ui/frames/side/client_info.ts @@ -1,4 +1,11 @@ -namespace chat { +import {ClientEntry, LocalClientEntry} from "../../../channel-tree/client"; +import {Modals} from "../../modal/ModalClientInfo"; +import {htmltags} from "../../htmltags"; +import {image_preview} from "../image_preview"; +import {i18n} from "../../../i18n/country"; +import {GroupManager} from "../../../permission/GroupManager"; + +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/conversations.ts b/shared/js/ui/frames/side/conversations.ts index 3c45863a..5351a673 100644 --- a/shared/js/ui/frames/side/conversations.ts +++ b/shared/js/ui/frames/side/conversations.ts @@ -1,4 +1,10 @@ -namespace chat { +import {htmltags} from "../../htmltags"; +import {MessageHelper} from "../chat"; +import {CommandResult, ErrorID} from "../../../connection/ServerConnectionDeclaration"; +import {log, LogCategory} from "../../../log"; +import {createErrorModal} from "../../elements/modal"; + +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/music_info.ts b/shared/js/ui/frames/side/music_info.ts index 41cbebbc..3b79e309 100644 --- a/shared/js/ui/frames/side/music_info.ts +++ b/shared/js/ui/frames/side/music_info.ts @@ -1,6 +1,7 @@ -namespace chat { - import PlayerState = connection.voice.PlayerState; +import {MusicClientEntry} from "../../../channel-tree/client"; +import {image_preview} from "../image_preview"; +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/private_conversations.ts b/shared/js/ui/frames/side/private_conversations.ts index f85ffe7c..08edd3ba 100644 --- a/shared/js/ui/frames/side/private_conversations.ts +++ b/shared/js/ui/frames/side/private_conversations.ts @@ -1,5 +1,9 @@ /* the bar on the right with the chats (Channel & Client) */ -namespace chat { +import {log, LogCategory} from "../../../log"; +import {htmltags} from "../../htmltags"; +import {MessageHelper} from "../chat"; + +export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/htmltags.ts b/shared/js/ui/htmltags.ts index b208043f..76c98159 100644 --- a/shared/js/ui/htmltags.ts +++ b/shared/js/ui/htmltags.ts @@ -1,4 +1,4 @@ -namespace htmltags { +export namespace htmltags { let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0}; function initialize() { diff --git a/shared/js/ui/modal/ModalAbout.ts b/shared/js/ui/modal/ModalAbout.ts index 39207f1c..518e5a3a 100644 --- a/shared/js/ui/modal/ModalAbout.ts +++ b/shared/js/ui/modal/ModalAbout.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { function format_date(date: number) { const d = new Date(date); diff --git a/shared/js/ui/modal/ModalAvatar.ts b/shared/js/ui/modal/ModalAvatar.ts index c5fcac24..e877f45f 100644 --- a/shared/js/ui/modal/ModalAvatar.ts +++ b/shared/js/ui/modal/ModalAvatar.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { //TODO: Test if we could render this image and not only the browser by knowing the type. export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) { const modal = createModal({ diff --git a/shared/js/ui/modal/ModalAvatarList.ts b/shared/js/ui/modal/ModalAvatarList.ts index 021430f2..beca0f7e 100644 --- a/shared/js/ui/modal/ModalAvatarList.ts +++ b/shared/js/ui/modal/ModalAvatarList.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { const avatar_to_uid = (id: string) => { const buffer = new Uint8Array(id.length / 2); for(let index = 0; index < id.length; index += 2) { diff --git a/shared/js/ui/modal/ModalBanClient.ts b/shared/js/ui/modal/ModalBanClient.ts index bb7b0627..24a47d6b 100644 --- a/shared/js/ui/modal/ModalBanClient.ts +++ b/shared/js/ui/modal/ModalBanClient.ts @@ -1,8 +1,3 @@ -/// -/// -/// - -namespace Modals { export type BanEntry = { name?: string; unique_id: string; diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index e5460964..dc6344de 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -1,9 +1,4 @@ -/// -/// -/// -/// - -namespace Modals { +export namespace Modals { export function openBanList(client: ConnectionHandler) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalBookmarks.ts b/shared/js/ui/modal/ModalBookmarks.ts index 950b3658..875a34f2 100644 --- a/shared/js/ui/modal/ModalBookmarks.ts +++ b/shared/js/ui/modal/ModalBookmarks.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { export function spawnBookmarkModal() { let modal: Modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalChangeLatency.ts b/shared/js/ui/modal/ModalChangeLatency.ts index 3b8dca95..7690dcd3 100644 --- a/shared/js/ui/modal/ModalChangeLatency.ts +++ b/shared/js/ui/modal/ModalChangeLatency.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { let modal: Modal; export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any, callback_flush?: () => any) { if(modal) modal.close(); diff --git a/shared/js/ui/modal/ModalChangeVolume.ts b/shared/js/ui/modal/ModalChangeVolume.ts index b9b0778a..691f61ab 100644 --- a/shared/js/ui/modal/ModalChangeVolume.ts +++ b/shared/js/ui/modal/ModalChangeVolume.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { //TODO: Use the max limit! let modal: Modal; diff --git a/shared/js/ui/modal/ModalChannelInfo.ts b/shared/js/ui/modal/ModalChannelInfo.ts index db91989f..ace6027b 100644 --- a/shared/js/ui/modal/ModalChannelInfo.ts +++ b/shared/js/ui/modal/ModalChannelInfo.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function openChannelInfo(channel: ChannelEntry) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalClientInfo.ts b/shared/js/ui/modal/ModalClientInfo.ts index 482a42d5..67953f79 100644 --- a/shared/js/ui/modal/ModalClientInfo.ts +++ b/shared/js/ui/modal/ModalClientInfo.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { type InfoUpdateCallback = (info: ClientConnectionInfo) => any; export function openClientInfo(client: ClientEntry) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalConnect.ts b/shared/js/ui/modal/ModalConnect.ts index cefdf403..9c9be5a4 100644 --- a/shared/js/ui/modal/ModalConnect.ts +++ b/shared/js/ui/modal/ModalConnect.ts @@ -1,7 +1,5 @@ -/// - //FIXME: Move this shit out of this file! -namespace connection_log { +export namespace connection_log { //TODO: Save password data export type ConnectionData = { name: string; @@ -91,7 +89,7 @@ namespace connection_log { }); } -namespace Modals { +export namespace Modals { export function spawnConnectModal(options: { default_connect_new_tab?: boolean /* default false */ }, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) { diff --git a/shared/js/ui/modal/ModalCreateChannel.ts b/shared/js/ui/modal/ModalCreateChannel.ts index 5f7be3bc..5a5ba4d6 100644 --- a/shared/js/ui/modal/ModalCreateChannel.ts +++ b/shared/js/ui/modal/ModalCreateChannel.ts @@ -1,6 +1,4 @@ -/// - -namespace Modals { +export namespace Modals { export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) { let properties: ChannelProperties = { } as ChannelProperties; //The changes properties const modal = createModal({ diff --git a/shared/js/ui/modal/ModalGroupAssignment.ts b/shared/js/ui/modal/ModalGroupAssignment.ts index 52deb24d..9d2e05d1 100644 --- a/shared/js/ui/modal/ModalGroupAssignment.ts +++ b/shared/js/ui/modal/ModalGroupAssignment.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { let current_modal: Modal; export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise) { if(current_modal) diff --git a/shared/js/ui/modal/ModalIconSelect.ts b/shared/js/ui/modal/ModalIconSelect.ts index 42911e61..65f915d8 100644 --- a/shared/js/ui/modal/ModalIconSelect.ts +++ b/shared/js/ui/modal/ModalIconSelect.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) { selected_icon = selected_icon || 0; let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1); diff --git a/shared/js/ui/modal/ModalIdentity.ts b/shared/js/ui/modal/ModalIdentity.ts index 1c5f9296..12aec88b 100644 --- a/shared/js/ui/modal/ModalIdentity.ts +++ b/shared/js/ui/modal/ModalIdentity.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity, name: string): Modal { let modal: Modal; let elapsed_timer: NodeJS.Timer; diff --git a/shared/js/ui/modal/ModalInvite.ts b/shared/js/ui/modal/ModalInvite.ts index 45a7acaa..77865c63 100644 --- a/shared/js/ui/modal/ModalInvite.ts +++ b/shared/js/ui/modal/ModalInvite.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { type URLGeneratorSettings = { flag_direct: boolean, flag_resolved: boolean diff --git a/shared/js/ui/modal/ModalKeySelect.ts b/shared/js/ui/modal/ModalKeySelect.ts index 8ad60b2d..194f925b 100644 --- a/shared/js/ui/modal/ModalKeySelect.ts +++ b/shared/js/ui/modal/ModalKeySelect.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function spawnKeySelect(callback: (key?: ppt.KeyEvent) => void) { let modal = createModal({ header: tr("Select a key"), diff --git a/shared/js/ui/modal/ModalMusicManage.ts b/shared/js/ui/modal/ModalMusicManage.ts index 1736ca05..ce84ee24 100644 --- a/shared/js/ui/modal/ModalMusicManage.ts +++ b/shared/js/ui/modal/ModalMusicManage.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) { const ev_registry = new events.Registry(); ev_registry.enable_debug("music-manage"); diff --git a/shared/js/ui/modal/ModalNewcomer.ts b/shared/js/ui/modal/ModalNewcomer.ts index 20f414a8..6a8d350d 100644 --- a/shared/js/ui/modal/ModalNewcomer.ts +++ b/shared/js/ui/modal/ModalNewcomer.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { const next_step: {[key: string]:string} = { "welcome": "microphone", //"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */ diff --git a/shared/js/ui/modal/ModalPlaylistEdit.ts b/shared/js/ui/modal/ModalPlaylistEdit.ts index 69ec39c5..e6a4e48b 100644 --- a/shared/js/ui/modal/ModalPlaylistEdit.ts +++ b/shared/js/ui/modal/ModalPlaylistEdit.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { export function spawnPlaylistSongInfo(song: PlaylistSong) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalPlaylistList.ts b/shared/js/ui/modal/ModalPlaylistList.ts index 58c7451c..887a65f4 100644 --- a/shared/js/ui/modal/ModalPlaylistList.ts +++ b/shared/js/ui/modal/ModalPlaylistList.ts @@ -1,9 +1,4 @@ -/// -/// -/// -/// - -namespace Modals { +export namespace Modals { export function spawnPlaylistManage(client: ConnectionHandler) { { createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open(); diff --git a/shared/js/ui/modal/ModalPoke.ts b/shared/js/ui/modal/ModalPoke.ts index d4769a2c..744bf155 100644 --- a/shared/js/ui/modal/ModalPoke.ts +++ b/shared/js/ui/modal/ModalPoke.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { let global_modal: PokeModal; interface ServerEntry { diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts index af3e8560..0fc337d1 100644 --- a/shared/js/ui/modal/ModalQuery.ts +++ b/shared/js/ui/modal/ModalQuery.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) { let modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalQueryManage.ts b/shared/js/ui/modal/ModalQueryManage.ts index c856ab83..da9cd35a 100644 --- a/shared/js/ui/modal/ModalQueryManage.ts +++ b/shared/js/ui/modal/ModalQueryManage.ts @@ -1,8 +1,4 @@ -/// -/// -/// - -namespace Modals { +export namespace Modals { /* export function spawnQueryManage(client: ConnectionHandler) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalServerEdit.ts b/shared/js/ui/modal/ModalServerEdit.ts index 089f1522..cf94122e 100644 --- a/shared/js/ui/modal/ModalServerEdit.ts +++ b/shared/js/ui/modal/ModalServerEdit.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise) { const properties = Object.assign({}, server.properties); diff --git a/shared/js/ui/modal/ModalServerInfo.ts b/shared/js/ui/modal/ModalServerInfo.ts index 08fe0c71..d470f174 100644 --- a/shared/js/ui/modal/ModalServerInfo.ts +++ b/shared/js/ui/modal/ModalServerInfo.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function openServerInfo(server: ServerEntry) { let modal: Modal; let update_callbacks: ServerBandwidthInfoUpdateCallback[] = []; diff --git a/shared/js/ui/modal/ModalServerInfoBandwidth.ts b/shared/js/ui/modal/ModalServerInfoBandwidth.ts index 8bb6fdc8..4965b8f8 100644 --- a/shared/js/ui/modal/ModalServerInfoBandwidth.ts +++ b/shared/js/ui/modal/ModalServerInfoBandwidth.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export enum RequestInfoStatus { SUCCESS, UNKNOWN, diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts index 155e3076..523b28e1 100644 --- a/shared/js/ui/modal/ModalSettings.ts +++ b/shared/js/ui/modal/ModalSettings.ts @@ -1,4 +1,4 @@ -namespace Modals { +export namespace Modals { export function spawnSettingsModal(default_page?: string) : Modal { let modal: Modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalYesNo.ts b/shared/js/ui/modal/ModalYesNo.ts index 4562d4ac..8a9af6b1 100644 --- a/shared/js/ui/modal/ModalYesNo.ts +++ b/shared/js/ui/modal/ModalYesNo.ts @@ -1,6 +1,6 @@ -/// +import {BodyCreator, ModalFunctions} from "../elements/modal"; -namespace Modals { +export namespace Modals { export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: { text_yes?: string, text_no?: string, diff --git a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts index f2c5ecfd..edf6b296 100644 --- a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts +++ b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts @@ -1,7 +1,5 @@ -/// /* first needs the AbstractPermissionEdit */ - /* Canvas Permission Editor */ -namespace pe { +export namespace pe { namespace ui { export namespace scheme { export interface CheckBox { diff --git a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts index bf3692ee..32a13d0b 100644 --- a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts +++ b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts @@ -1,6 +1,4 @@ -/// /* first needs the AbstractPermissionEdit */ - -namespace pe { +export namespace pe { class HTMLPermission { readonly handle: HTMLPermissionEditor; readonly group: HTMLPermissionGroup; diff --git a/shared/js/ui/modal/permission/ModalPermissionEdit.ts b/shared/js/ui/modal/permission/ModalPermissionEdit.ts index f405dd9c..fcb85f8a 100644 --- a/shared/js/ui/modal/permission/ModalPermissionEdit.ts +++ b/shared/js/ui/modal/permission/ModalPermissionEdit.ts @@ -1,12 +1,8 @@ -/// -/// -/// - interface JQuery { dropdown: any; } -namespace Modals { +export namespace Modals { export namespace PermissionEditor { export interface PermissionEntry { tag: JQuery; diff --git a/shared/js/ui/modal/permission/SenselessPermissions.ts b/shared/js/ui/modal/permission/SenselessPermissions.ts index 06ceed6a..1ff6524b 100644 --- a/shared/js/ui/modal/permission/SenselessPermissions.ts +++ b/shared/js/ui/modal/permission/SenselessPermissions.ts @@ -1,6 +1,6 @@ -/// +import {PermissionType} from "../../../permission/PermissionManager"; -namespace permissions { +export namespace permissions { export const senseless_server_group_permissions: PermissionType[] = [ PermissionType.B_CHANNEL_GROUP_INHERITANCE_END ]; diff --git a/shared/js/utils/helpers.ts b/shared/js/utils/helpers.ts index 1b8cc2d9..f3104911 100644 --- a/shared/js/utils/helpers.ts +++ b/shared/js/utils/helpers.ts @@ -1,6 +1,6 @@ -/// +import {sha} from "../crypto/sha"; -namespace helpers { +export namespace helpers { export function hashPassword(password: string) : Promise { return new Promise((resolve, reject) => { sha.sha1(password).then(result => { @@ -10,7 +10,7 @@ namespace helpers { } } -class LaterPromise extends Promise { +export class LaterPromise extends Promise { private _handle: Promise; private _resolve: ($: T) => any; private _reject: ($: any) => any; diff --git a/shared/js/voice/RecorderBase.ts b/shared/js/voice/RecorderBase.ts index ba32fb38..3fb11f55 100644 --- a/shared/js/voice/RecorderBase.ts +++ b/shared/js/voice/RecorderBase.ts @@ -1,4 +1,4 @@ -namespace audio { +export namespace audio { export namespace recorder { export interface InputDevice { unique_id: string; diff --git a/shared/js/voice/RecorderProfile.ts b/shared/js/voice/RecorderProfile.ts index ec5ea0dc..d149a6ac 100644 --- a/shared/js/voice/RecorderProfile.ts +++ b/shared/js/voice/RecorderProfile.ts @@ -1,7 +1,7 @@ -/// +import {ConnectionHandler} from "../ConnectionHandler"; -type VadType = "threshold" | "push_to_talk" | "active"; -interface RecorderProfileConfig { +export type VadType = "threshold" | "push_to_talk" | "active"; +export interface RecorderProfileConfig { version: number; /* devices unique id */ @@ -25,8 +25,8 @@ interface RecorderProfileConfig { } } -let default_recorder: RecorderProfile; /* needs initialize */ -class RecorderProfile { +export let default_recorder: RecorderProfile; /* needs initialize */ +export class RecorderProfile { readonly name; readonly volatile; /* not saving profile */ diff --git a/shared/loader/app.ts b/shared/loader/app.ts index 29b8e0dd..4542f478 100644 --- a/shared/loader/app.ts +++ b/shared/loader/app.ts @@ -207,10 +207,10 @@ const loader_javascript = { {url: "js/ui/modal/permission/CanvasPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]}, {url: "js/ui/modal/permission/HTMLPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]}, - "js/ui/channel.js", - "js/ui/client.js", - "js/ui/server.js", - "js/ui/view.js", + "js/channel-tree/channel.js", + "js/channel-tree/client.js", + "js/channel-tree/server.js", + "js/channel-tree/view.js", "js/ui/client_move.js", "js/ui/htmltags.js", diff --git a/tsconfig.json b/tsconfig.json index 07b098f0..dbf1f46a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "module": "commonjs", "sourceMap": true, "lib": ["es6", "dom", "dom.iterable"], - "removeComments": true /* we dont really need them within the target files */ + "removeComments": true, /* we dont really need them within the target files */ + "jsx": "react" }, "exclude": [ "node_modules", diff --git a/webpack.config.js b/webpack.config.js index 3a71062c..9c9a692f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,7 +3,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const isDevelopment = process.env.NODE_ENV === 'development'; module.exports = { - entry: './src/index.tsx', + entry: './shared/js/main.ts', devtool: 'inline-source-map', mode: "development", plugins: [ @@ -36,8 +36,16 @@ module.exports = { }, { test: /\.tsx?$/, - use: 'ts-loader', exclude: /node_modules/, + + loader: [ + { + loader: 'ts-loader', + options: { + //transpileOnly: true + } + } + ] }, ], }, From 6ebe7711788f2221b0e7874ca89fec811311863e Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 28 Mar 2020 11:30:03 +0100 Subject: [PATCH 03/23] Correct sorting of modifiers --- tools/dtsgen/declarator.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/dtsgen/declarator.ts b/tools/dtsgen/declarator.ts index f85cfd4a..46424aa9 100644 --- a/tools/dtsgen/declarator.ts +++ b/tools/dtsgen/declarator.ts @@ -24,6 +24,20 @@ function has_modifier(modifiers: ts.ModifiersArra function append_modifier(modifiers: ts.ModifiersArray | undefined, target: T) : ts.ModifiersArray { if(has_modifier(modifiers, target)) return modifiers; + const sort_oder: {[key: number]:number} = {}; + sort_oder[SyntaxKind.AbstractKeyword] = 20; + sort_oder[SyntaxKind.AsyncKeyword] = 20; + sort_oder[SyntaxKind.ConstKeyword] = 20; + sort_oder[SyntaxKind.DeclareKeyword] = 20; + sort_oder[SyntaxKind.DefaultKeyword] = 20; + sort_oder[SyntaxKind.ExportKeyword] = 10; + sort_oder[SyntaxKind.PublicKeyword] = 30; + sort_oder[SyntaxKind.PrivateKeyword] = 30; + sort_oder[SyntaxKind.ProtectedKeyword] = 30; + sort_oder[SyntaxKind.ReadonlyKeyword] = 30; + sort_oder[SyntaxKind.StaticKeyword] = 30; + + const comparator = (a: ts.Modifier, b: ts.Modifier) => sort_oder[a.kind] - sort_oder[b.kind]; return ts.createNodeArray( [ts.createModifier(target as number), ...(modifiers || [])].map((e, index, array) => { const range = ts.getCommentRange(e); @@ -37,7 +51,7 @@ function append_modifier(modifiers: ts.ModifiersA else console.warn("Dropping comment on node because first node already has a comment"); return e; - }), + }).sort(comparator), (modifiers || {hasTrailingComma: false}).hasTrailingComma ); } From 5f0c1b303a4e9bb0115a16041955af3ffe58fc9a Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 28 Mar 2020 13:58:36 +0100 Subject: [PATCH 04/23] Updated some declaration --- client/generated/client.js | 37115 ++++++++++++++++ client/generated/client.js.map | 1 + scripts/build_declarations.sh | 22 +- shared/generate_declarations.sh | 12 +- shared/tsconfig/dtsconfig_app.json | 10 +- tools/dtsgen/declarator.ts | 59 +- tools/dtsgen/index.ts | 78 +- tools/dtsgen/out.d.ts | 6 - tools/dtsgen/out.d/module_a.d.ts | 8 + tools/dtsgen/out.d/test_01.d.ts | 5 + tools/dtsgen/out.d/test_02.d.ts | 8 + tools/dtsgen/test_modular/module_a.ts | 11 + tools/dtsgen/test_modular/test_01.ts | 9 + tools/dtsgen/test_modular/test_02.ts | 14 + tools/dtsgen/test_modular/tools/dtsgen/out.d | 5 + .../{test => test_non_modular}/test_01.ts | 0 .../{test => test_non_modular}/test_02.ts | 0 .../{test => test_non_modular}/test_03.ts | 0 .../{test => test_non_modular}/test_04.ts | 0 .../{test => test_non_modular}/test_05.ts | 0 .../{test => test_non_modular}/test_06.ts | 0 .../{test => test_non_modular}/test_07.ts | 0 tsconfig.json | 6 +- web/js/audio/AudioPlayer.ts | 2 + 24 files changed, 37293 insertions(+), 78 deletions(-) create mode 100644 client/generated/client.js create mode 100644 client/generated/client.js.map delete mode 100644 tools/dtsgen/out.d.ts create mode 100644 tools/dtsgen/out.d/module_a.d.ts create mode 100644 tools/dtsgen/out.d/test_01.d.ts create mode 100644 tools/dtsgen/out.d/test_02.d.ts create mode 100644 tools/dtsgen/test_modular/module_a.ts create mode 100644 tools/dtsgen/test_modular/test_01.ts create mode 100644 tools/dtsgen/test_modular/test_02.ts create mode 100644 tools/dtsgen/test_modular/tools/dtsgen/out.d rename tools/dtsgen/{test => test_non_modular}/test_01.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_02.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_03.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_04.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_05.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_06.ts (100%) rename tools/dtsgen/{test => test_non_modular}/test_07.ts (100%) diff --git a/client/generated/client.js b/client/generated/client.js new file mode 100644 index 00000000..9d836ffc --- /dev/null +++ b/client/generated/client.js @@ -0,0 +1,37115 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { + step(generator.next(value)); + } + catch (e) { + reject(e); + } } + function rejected(value) { try { + step(generator["throw"](value)); + } + catch (e) { + reject(e); + } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["a5a03ca85c8240449664e3b10fd8fcc34885e4fa5642d49e9e09fc9a59670cdc"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["a5a03ca85c8240449664e3b10fd8fcc34885e4fa5642d49e9e09fc9a59670cdc"] = "a5a03ca85c8240449664e3b10fd8fcc34885e4fa5642d49e9e09fc9a59670cdc"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Wl8vrliw", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (68,48)" }, { name: "TRAT_MvL", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (84,51)" }, { name: "NHpPYFZ5", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (91,51)" }, { name: "xmHiPaM0", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (104,51)" }, { name: "bkWPmlLQ", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (122,34)" }, { name: "VLiaGQGx", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (227,43)" }, { name: "qDGtVdSI", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (235,44)" }, { name: "GkVHr9AU", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (242,39)" }, { name: "laReoH3i", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (346,56)" }, { name: "hs5uHkoO", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (356,60)" }, { name: "lr1fckIz", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (367,55)" }, { name: "dy0SUBbK", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (371,56)" }, { name: "Cw58TNMU", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (375,56)" }, { name: "N46z9byK", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (379,52)" }, { name: "DychQff8", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (389,56)" }, { name: "fJ5NHwOd", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (397,56)" }, { name: "ja_xbzKa", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (398,53)" }, { name: "EES58bn8", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (406,55)" }, { name: "PMONgu7p", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (411,55)" }, { name: "E3YbqUC5", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (415,52)" }, { name: "SYcW1j9s", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (426,55)" }, { name: "QG0sTpJs", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (431,55)" }, { name: "PeE5Y0gF", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (437,52)" }, { name: "LnZyAOSW", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (564,48)" }, { name: "UIzPrFR8", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (567,48)" }, { name: "n5XDhypA", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (652,47)" }, { name: "jIjjhpiZ", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (656,51)" }, { name: "NBBzTYcx", path: "D:/TeaSpeak/web/shared/js/BrowserIPC.ts (669,34)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var bipc; +(function (bipc) { + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + class BasicIPCHandler { + constructor() { + this._channels = []; + this._query_results = {}; + this._cert_accept_callbacks = {}; + this._cert_accept_succeeded = {}; + } + setup() { + this.unique_id = uuidv4(); /* lets get an unique identifier */ + } + get_local_address() { return this.unique_id; } + handle_message(message) { + //log.trace(LogCategory.IPC, tr("Received message %o"), message); + if (message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) { + if (message.type == "process-query") { + log.debug(LogCategory.IPC, _translations.Wl8vrliw || (_translations.Wl8vrliw = tr("Received a device query from %s.")), message.sender); + this.send_message("process-query-response", { + request_query_id: message.data.query_id, + request_timestamp: message.data.timestamp, + device_id: this.unique_id, + protocol: BasicIPCHandler.PROTOCOL_VERSION + }, message.sender); + return; + } + } + else if (message.receiver === this.unique_id) { + if (message.type == "process-query-response") { + const response = message.data; + if (this._query_results[response.request_query_id]) + this._query_results[response.request_query_id].push(response); + else { + log.warn(LogCategory.IPC, _translations.TRAT_MvL || (_translations.TRAT_MvL = tr("Received a query response for an unknown request."))); + } + return; + } + else if (message.type == "certificate-accept-callback") { + const data = message.data; + if (!this._cert_accept_callbacks[data.request_id]) { + log.warn(LogCategory.IPC, _translations.NHpPYFZ5 || (_translations.NHpPYFZ5 = tr("Received certificate accept callback for an unknown request ID."))); + return; + } + this._cert_accept_callbacks[data.request_id](); + delete this._cert_accept_callbacks[data.request_id]; + this.send_message("certificate-accept-succeeded", {}, message.sender); + return; + } + else if (message.type == "certificate-accept-succeeded") { + if (!this._cert_accept_succeeded[message.sender]) { + log.warn(LogCategory.IPC, _translations.xmHiPaM0 || (_translations.xmHiPaM0 = tr("Received certificate accept succeeded, but haven't a callback."))); + return; + } + this._cert_accept_succeeded[message.sender](); + return; + } + } + if (message.type === "channel") { + const data = message.data; + let channel_invoked = false; + for (const channel of this._channels) + if (channel.channel_id === data.channel_id && (typeof (channel.target_id) === "undefined" || channel.target_id === message.sender)) { + if (channel.message_handler) + channel.message_handler(message.sender, message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID, data); + channel_invoked = true; + } + if (!channel_invoked) { + console.warn(_translations.bkWPmlLQ || (_translations.bkWPmlLQ = tr("Received channel message for unknown channel (%s)")), data.channel_id); + } + } + } + create_channel(target_id, channel_id) { + let channel = { + target_id: target_id, + channel_id: channel_id || uuidv4(), + message_handler: undefined, + send_message: (type, data, target) => { + if (typeof target !== "undefined") { + if (typeof channel.target_id === "string" && target != channel.target_id) + throw "target id does not match channel target"; + } + this.send_message("channel", { + type: type, + data: data, + channel_id: channel.channel_id + }, target || channel.target_id || BasicIPCHandler.BROADCAST_UNIQUE_ID); + } + }; + this._channels.push(channel); + return channel; + } + channels() { return this._channels; } + delete_channel(channel) { + this._channels = this._channels.filter(e => e !== channel); + } + query_processes(timeout) { + return __awaiter(this, void 0, void 0, function* () { + const query_id = uuidv4(); + this._query_results[query_id] = []; + this.send_message("process-query", { + query_id: query_id, + timestamp: Date.now() + }); + yield new Promise(resolve => setTimeout(resolve, timeout || 250)); + const result = this._query_results[query_id]; + delete this._query_results[query_id]; + return result; + }); + } + register_certificate_accept_callback(callback) { + const id = uuidv4(); + this._cert_accept_callbacks[id] = callback; + return this.unique_id + ":" + id; + } + post_certificate_accpected(id, timeout) { + return new Promise((resolve, reject) => { + const data = id.split(":"); + const timeout_id = setTimeout(() => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + reject("timeout"); + }, timeout || 250); + this._cert_accept_succeeded[data[0]] = () => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + resolve(); + }; + this.send_message("certificate-accept-callback", { + request_id: data[1] + }, data[0]); + }); + } + } + BasicIPCHandler.BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000"; + BasicIPCHandler.PROTOCOL_VERSION = 1; + bipc.BasicIPCHandler = BasicIPCHandler; + class BroadcastChannelIPC extends BasicIPCHandler { + constructor() { + super(); + } + setup() { + super.setup(); + this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME); + this.channel.onmessage = this.on_message.bind(this); + this.channel.onmessageerror = this.on_error.bind(this); + } + on_message(event) { + if (typeof (event.data) !== "string") { + log.warn(LogCategory.IPC, _translations.VLiaGQGx || (_translations.VLiaGQGx = tr("Received message with an invalid type (%s): %o")), typeof (event.data), event.data); + return; + } + let message; + try { + message = JSON.parse(event.data); + } + catch (error) { + log.error(LogCategory.IPC, _translations.qDGtVdSI || (_translations.qDGtVdSI = tr("Received an invalid encoded message: %o")), event.data); + return; + } + super.handle_message(message); + } + on_error(event) { + log.warn(LogCategory.IPC, _translations.GkVHr9AU || (_translations.GkVHr9AU = tr("Received error: %o")), event); + } + send_message(type, data, target) { + const message = {}; + message.sender = this.unique_id; + message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID; + message.timestamp = Date.now(); + message.type = type; + message.data = data; + this.channel.postMessage(JSON.stringify(message)); + } + } + BroadcastChannelIPC.CHANNEL_NAME = "TeaSpeak-Web"; + let connect; + (function (connect) { + /* The connect process: + * 1. Broadcast an offer + * 2. Wait 50ms for all offer responses or until the first one respond with "ok" + * 3. Select (if possible) on accepted offer and execute the connect + */ + class ConnectHandler { + constructor(ipc_handler) { + this.callback_available = () => false; + this.callback_execute = () => false; + this._pending_connect_offers = []; + this._pending_connects_requests = []; + this.ipc_handler = ipc_handler; + } + setup() { + this.ipc_channel = this.ipc_handler.create_channel(undefined, ConnectHandler.CHANNEL_NAME); + this.ipc_channel.message_handler = this.on_message.bind(this); + } + on_message(sender, broadcast, message) { + if (broadcast) { + if (message.type == "offer") { + const data = message.data; + const response = { + accepted: this.callback_available(data.data), + request_id: data.request_id + }; + if (response.accepted) { + log.debug(LogCategory.IPC, _translations.laReoH3i || (_translations.laReoH3i = tr("Received new connect offer from %s: %s")), sender, data.request_id); + const ld = { + remote_handler: sender, + data: data.data, + id: data.request_id, + timeout: 0 + }; + this._pending_connect_offers.push(ld); + ld.timeout = setTimeout(() => { + log.debug(LogCategory.IPC, _translations.hs5uHkoO || (_translations.hs5uHkoO = tr("Dropping connect request %s, because we never received an execute.")), ld.id); + this._pending_connect_offers.remove(ld); + }, 120 * 1000); + } + this.ipc_channel.send_message("offer-answer", response, sender); + } + } + else { + if (message.type == "offer-answer") { + const data = message.data; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, _translations.lr1fckIz || (_translations.lr1fckIz = tr("Received connect offer answer with unknown request id (%s).")), data.request_id); + return; + } + if (!data.accepted) { + log.debug(LogCategory.IPC, _translations.dy0SUBbK || (_translations.dy0SUBbK = tr("Client %s rejected the connect offer (%s).")), sender, request.id); + return; + } + if (request.remote_handler) { + log.debug(LogCategory.IPC, _translations.Cw58TNMU || (_translations.Cw58TNMU = tr("Client %s accepted the connect offer (%s), but offer has already been accepted.")), sender, request.id); + return; + } + log.debug(LogCategory.IPC, _translations.N46z9byK || (_translations.N46z9byK = tr("Client %s accepted the connect offer (%s). Request local acceptance.")), sender, request.id); + request.remote_handler = sender; + clearTimeout(request.timeout); + request.callback_avail().then(flag => { + if (!flag) { + request.callback_failed("local avail rejected"); + return; + } + log.debug(LogCategory.IPC, _translations.DychQff8 || (_translations.DychQff8 = tr("Executing connect with client %s")), request.remote_handler); + this.ipc_channel.send_message("execute", { + request_id: request.id + }, request.remote_handler); + request.timeout = setTimeout(() => { + request.callback_failed("connect execute timeout"); + }, 1000); + }).catch(error => { + log.error(LogCategory.IPC, _translations.fJ5NHwOd || (_translations.fJ5NHwOd = tr("Local avail callback caused an error: %o")), error); + request.callback_failed(_translations.ja_xbzKa || (_translations.ja_xbzKa = tr("local avail callback caused an error"))); + }); + } + else if (message.type == "executed") { + const data = message.data; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, _translations.EES58bn8 || (_translations.EES58bn8 = tr("Received connect executed with unknown request id (%s).")), data.request_id); + return; + } + if (request.remote_handler != sender) { + log.warn(LogCategory.IPC, _translations.PMONgu7p || (_translations.PMONgu7p = tr("Received connect executed for request %s, but from wrong client: %s (expected %s)")), data.request_id, sender, request.remote_handler); + return; + } + log.debug(LogCategory.IPC, _translations.E3YbqUC5 || (_translations.E3YbqUC5 = tr("Received connect executed response from client %s for request %s. Succeeded: %o (%s)")), sender, data.request_id, data.succeeded, data.message); + clearTimeout(request.timeout); + if (data.succeeded) + request.callback_success(); + else + request.callback_failed(data.message); + } + else if (message.type == "execute") { + const data = message.data; + const request = this._pending_connect_offers.find(e => e.id === data.request_id); + if (!request) { + log.warn(LogCategory.IPC, _translations.SYcW1j9s || (_translations.SYcW1j9s = tr("Received connect execute with unknown request id (%s).")), data.request_id); + return; + } + if (request.remote_handler != sender) { + log.warn(LogCategory.IPC, _translations.QG0sTpJs || (_translations.QG0sTpJs = tr("Received connect execute for request %s, but from wrong client: %s (expected %s)")), data.request_id, sender, request.remote_handler); + return; + } + clearTimeout(request.timeout); + this._pending_connect_offers.remove(request); + log.debug(LogCategory.IPC, _translations.PeE5Y0gF || (_translations.PeE5Y0gF = tr("Executing connect for %s")), data.request_id); + const cr = this.callback_execute(request.data); + const response = { + request_id: data.request_id, + succeeded: typeof (cr) !== "string" && cr, + message: typeof (cr) === "string" ? cr : "", + }; + this.ipc_channel.send_message("executed", response, request.remote_handler); + } + } + } + post_connect_request(data, callback_avail) { + return new Promise((resolve, reject) => { + const pd = { + data: data, + id: uuidv4(), + timeout: 0, + callback_success: () => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + resolve(); + }, + callback_failed: error => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + reject(error); + }, + callback_avail: callback_avail, + }; + this._pending_connects_requests.push(pd); + this.ipc_channel.send_message("offer", { + request_id: pd.id, + data: pd.data + }); + pd.timeout = setTimeout(() => { + pd.callback_failed("received no response to offer"); + }, 50); + }); + } + } + ConnectHandler.CHANNEL_NAME = "connect"; + connect.ConnectHandler = ConnectHandler; + })(connect = bipc.connect || (bipc.connect = {})); + let mproxy; + (function (mproxy) { + class MethodProxy { + constructor(ipc_handler, connect_params) { + this._proxied_methods = {}; + this._proxied_callbacks = {}; + this.ipc_handler = ipc_handler; + this._ipc_parameters = connect_params; + this._connected = false; + this._slave = typeof (connect_params) !== "undefined"; + this._local = typeof (connect_params) !== "undefined" && connect_params.channel_id === "local" && connect_params.client_id === "local"; + } + setup() { + if (this._local) { + this._connected = true; + this.on_connected(); + } + else { + if (this._slave) + this._ipc_channel = this.ipc_handler.create_channel(this._ipc_parameters.client_id, this._ipc_parameters.channel_id); + else + this._ipc_channel = this.ipc_handler.create_channel(); + this._ipc_channel.message_handler = this._handle_message.bind(this); + if (this._slave) + this._ipc_channel.send_message("initialize", {}); + } + } + finalize() { + if (!this._local) { + if (this._connected) + this._ipc_channel.send_message("finalize", {}); + this.ipc_handler.delete_channel(this._ipc_channel); + this._ipc_channel = undefined; + } + for (const promise of Object.values(this._proxied_callbacks)) + promise.reject("disconnected"); + this._proxied_callbacks = {}; + this._connected = false; + this.on_disconnected(); + } + register_method(method) { + let method_name; + if (typeof method === "function") { + log.debug(LogCategory.IPC, _translations.LnZyAOSW || (_translations.LnZyAOSW = tr("Registering method proxy for %s")), method.name); + method_name = method.name; + } + else { + log.debug(LogCategory.IPC, _translations.UIzPrFR8 || (_translations.UIzPrFR8 = tr("Registering method proxy for %s")), method); + method_name = method; + } + if (!this[method_name]) + throw "method is missing in current object"; + this._proxied_methods[method_name] = this[method_name]; + if (!this._local) { + this[method_name] = (...args) => { + if (!this._connected) + return Promise.reject("not connected"); + const proxy_callback = { + promise_id: uuidv4() + }; + this._proxied_callbacks[proxy_callback.promise_id] = proxy_callback; + proxy_callback.promise = new Promise((resolve, reject) => { + proxy_callback.resolve = resolve; + proxy_callback.reject = reject; + }); + this._ipc_channel.send_message("invoke", { + promise_id: proxy_callback.promise_id, + arguments: [...args], + method_name: method_name + }); + return proxy_callback.promise; + }; + } + } + _handle_message(remote_id, boradcast, message) { + if (message.type === "finalize") { + this._handle_finalize(); + } + else if (message.type === "initialize") { + this._handle_remote_callback(remote_id); + } + else if (message.type === "invoke") { + this._handle_invoke(message.data); + } + else if (message.type === "result") { + this._handle_result(message.data); + } + } + _handle_finalize() { + this.on_disconnected(); + this.finalize(); + this._connected = false; + } + _handle_remote_callback(remote_id) { + if (!this._ipc_channel.target_id) { + if (this._slave) + throw "initialize wrong state!"; + this._ipc_channel.target_id = remote_id; /* now we're able to send messages */ + this.on_connected(); + this._ipc_channel.send_message("initialize", true); + } + else { + if (!this._slave) + throw "initialize wrong state!"; + this.on_connected(); + } + this._connected = true; + } + _send_result(promise_id, success, message) { + this._ipc_channel.send_message("result", { + promise_id: promise_id, + result: message, + success: success + }); + } + _handle_invoke(data) { + if (this._proxied_methods[data.method_name]) + throw "we could not invoke a local proxied method!"; + if (!this[data.method_name]) { + this._send_result(data.promise_id, false, "missing method"); + return; + } + try { + log.info(LogCategory.IPC, _translations.n5XDhypA || (_translations.n5XDhypA = tr("Invoking method %s with arguments: %o")), data.method_name, data.arguments); + const promise = this[data.method_name](...data.arguments); + promise.then(result => { + log.info(LogCategory.IPC, _translations.jIjjhpiZ || (_translations.jIjjhpiZ = tr("Result: %o")), result); + this._send_result(data.promise_id, true, result); + }).catch(error => { + this._send_result(data.promise_id, false, error); + }); + } + catch (error) { + this._send_result(data.promise_id, false, error); + return; + } + } + _handle_result(data) { + if (!this._proxied_callbacks[data.promise_id]) { + console.warn(_translations.NBBzTYcx || (_translations.NBBzTYcx = tr("Received proxy method result for unknown promise"))); + return; + } + const callback = this._proxied_callbacks[data.promise_id]; + delete this._proxied_callbacks[data.promise_id]; + if (data.success) + callback.resolve(data.result); + else + callback.reject(data.result); + } + generate_connect_parameters() { + if (this._slave) + throw "only masters can generate connect parameters!"; + if (!this._ipc_channel) + throw "please call setup() before"; + return { + channel_id: this._ipc_channel.channel_id, + client_id: this.ipc_handler.get_local_address() + }; + } + is_slave() { return this._local || this._slave; } /* the popout modal */ + is_master() { return this._local || !this._slave; } /* the host (teaweb application) */ + } + mproxy.MethodProxy = MethodProxy; + })(mproxy = bipc.mproxy || (bipc.mproxy = {})); + let handler; + let connect_handler; + function setup() { + if (!supported()) + return; + handler = new BroadcastChannelIPC(); + handler.setup(); + connect_handler = new connect.ConnectHandler(handler); + connect_handler.setup(); + } + bipc.setup = setup; + function get_handler() { + return handler; + } + bipc.get_handler = get_handler; + function get_connect_handler() { + return connect_handler; + } + bipc.get_connect_handler = get_connect_handler; + function supported() { + /* ios does not support this */ + return typeof (window.BroadcastChannel) !== "undefined"; + } + bipc.supported = supported; +})(bipc || (bipc = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e9465f7ab2e9767c098f57c8330ffbeb335dfeb60c7988bf4ae7bdaefb93a0be"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e9465f7ab2e9767c098f57c8330ffbeb335dfeb60c7988bf4ae7bdaefb93a0be"] = "e9465f7ab2e9767c098f57c8330ffbeb335dfeb60c7988bf4ae7bdaefb93a0be"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Z7deKCo1", path: "D:/TeaSpeak/web/shared/js/log.ts (168,30)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +//Used by CertAccept popup +var LogCategory; +(function (LogCategory) { + LogCategory[LogCategory["CHANNEL"] = 0] = "CHANNEL"; + LogCategory[LogCategory["CHANNEL_PROPERTIES"] = 1] = "CHANNEL_PROPERTIES"; + LogCategory[LogCategory["CLIENT"] = 2] = "CLIENT"; + LogCategory[LogCategory["BOOKMARKS"] = 3] = "BOOKMARKS"; + LogCategory[LogCategory["SERVER"] = 4] = "SERVER"; + LogCategory[LogCategory["PERMISSIONS"] = 5] = "PERMISSIONS"; + LogCategory[LogCategory["GENERAL"] = 6] = "GENERAL"; + LogCategory[LogCategory["NETWORKING"] = 7] = "NETWORKING"; + LogCategory[LogCategory["VOICE"] = 8] = "VOICE"; + LogCategory[LogCategory["CHAT"] = 9] = "CHAT"; + LogCategory[LogCategory["AUDIO"] = 10] = "AUDIO"; + LogCategory[LogCategory["I18N"] = 11] = "I18N"; + LogCategory[LogCategory["IPC"] = 12] = "IPC"; + LogCategory[LogCategory["IDENTITIES"] = 13] = "IDENTITIES"; + LogCategory[LogCategory["STATISTICS"] = 14] = "STATISTICS"; + LogCategory[LogCategory["DNS"] = 15] = "DNS"; +})(LogCategory || (LogCategory = {})); +var log; +(function (log_1) { + let LogType; + (function (LogType) { + LogType[LogType["TRACE"] = 0] = "TRACE"; + LogType[LogType["DEBUG"] = 1] = "DEBUG"; + LogType[LogType["INFO"] = 2] = "INFO"; + LogType[LogType["WARNING"] = 3] = "WARNING"; + LogType[LogType["ERROR"] = 4] = "ERROR"; + })(LogType = log_1.LogType || (log_1.LogType = {})); + let category_mapping = new Map([ + [LogCategory.CHANNEL, "Channel "], + [LogCategory.CHANNEL_PROPERTIES, "Channel "], + [LogCategory.CLIENT, "Client "], + [LogCategory.SERVER, "Server "], + [LogCategory.BOOKMARKS, "Bookmark "], + [LogCategory.PERMISSIONS, "Permission "], + [LogCategory.GENERAL, "General "], + [LogCategory.NETWORKING, "Network "], + [LogCategory.VOICE, "Voice "], + [LogCategory.AUDIO, "Audio "], + [LogCategory.CHANNEL, "Chat "], + [LogCategory.I18N, "I18N "], + [LogCategory.IDENTITIES, "Identities "], + [LogCategory.IPC, "IPC "], + [LogCategory.STATISTICS, "Statistics "], + [LogCategory.DNS, "DNS "] + ]); + log_1.enabled_mapping = new Map([ + [LogCategory.CHANNEL, true], + [LogCategory.CHANNEL_PROPERTIES, false], + [LogCategory.CLIENT, true], + [LogCategory.SERVER, true], + [LogCategory.BOOKMARKS, true], + [LogCategory.PERMISSIONS, true], + [LogCategory.GENERAL, true], + [LogCategory.NETWORKING, true], + [LogCategory.VOICE, true], + [LogCategory.AUDIO, true], + [LogCategory.CHAT, true], + [LogCategory.I18N, false], + [LogCategory.IDENTITIES, true], + [LogCategory.IPC, true], + [LogCategory.STATISTICS, true], + [LogCategory.DNS, true] + ]); + //Values will be overridden by initialize() + log_1.level_mapping = new Map([ + [LogType.TRACE, true], + [LogType.DEBUG, true], + [LogType.INFO, true], + [LogType.WARNING, true], + [LogType.ERROR, true] + ]); + let GroupMode; + (function (GroupMode) { + GroupMode[GroupMode["NATIVE"] = 0] = "NATIVE"; + GroupMode[GroupMode["PREFIX"] = 1] = "PREFIX"; + })(GroupMode || (GroupMode = {})); + const group_mode = GroupMode.PREFIX; + //Category Example: ?log.i18n.enabled=0 + //Level Example A: ?log.level.trace.enabled=0 + //Level Example B: ?log.level=0 + function initialize(default_level) { + for (const category of Object.keys(LogCategory).map(e => parseInt(e))) { + if (isNaN(category)) + continue; + const category_name = LogCategory[category].toLowerCase(); + log_1.enabled_mapping.set(category, settings.static_global("log." + category_name.toLowerCase() + ".enabled", log_1.enabled_mapping.get(category))); + } + const base_level = settings.static_global("log.level", default_level); + for (const level of Object.keys(LogType).map(e => parseInt(e))) { + if (isNaN(level)) + continue; + const level_name = LogType[level].toLowerCase(); + log_1.level_mapping.set(level, settings.static_global("log." + level_name + ".enabled", level >= base_level)); + } + } + log_1.initialize = initialize; + function logDirect(type, message, ...optionalParams) { + if (!log_1.level_mapping.get(type)) + return; + switch (type) { + case LogType.TRACE: + case LogType.DEBUG: + console.debug(message, ...optionalParams); + break; + case LogType.INFO: + console.log(message, ...optionalParams); + break; + case LogType.WARNING: + console.warn(message, ...optionalParams); + break; + case LogType.ERROR: + console.error(message, ...optionalParams); + break; + } + } + function log(type, category, message, ...optionalParams) { + if (!log_1.enabled_mapping.get(category)) + return; + optionalParams.unshift(category_mapping.get(category)); + message = "[%s] " + message; + logDirect(type, message, ...optionalParams); + } + log_1.log = log; + function trace(category, message, ...optionalParams) { + log(LogType.TRACE, category, message, ...optionalParams); + } + log_1.trace = trace; + function debug(category, message, ...optionalParams) { + log(LogType.DEBUG, category, message, ...optionalParams); + } + log_1.debug = debug; + function info(category, message, ...optionalParams) { + log(LogType.INFO, category, message, ...optionalParams); + } + log_1.info = info; + function warn(category, message, ...optionalParams) { + log(LogType.WARNING, category, message, ...optionalParams); + } + log_1.warn = warn; + function error(category, message, ...optionalParams) { + log(LogType.ERROR, category, message, ...optionalParams); + } + log_1.error = error; + function group(level, category, name, ...optionalParams) { + name = "[%s] " + name; + optionalParams.unshift(category_mapping.get(category)); + return new Group(group_mode, level, category, name, optionalParams); + } + log_1.group = group; + function table(level, category, title, arguments) { + if (group_mode == GroupMode.NATIVE) { + console.groupCollapsed(title); + console.table(arguments); + console.groupEnd(); + } + else { + if (!log_1.enabled_mapping.get(category) || !log_1.level_mapping.get(level)) + return; + logDirect(level, _translations.Z7deKCo1 || (_translations.Z7deKCo1 = tr("Snipped table \"%s\"")), title); + } + } + log_1.table = table; + class Group { + constructor(mode, level, category, name, optionalParams, owner = undefined) { + this.owner = undefined; + this._collapsed = false; + this.initialized = false; + this.level = level; + this.mode = mode; + this.category = category; + this.name = name; + this.optionalParams = optionalParams; + this.enabled = log_1.enabled_mapping.get(category); + } + group(level, name, ...optionalParams) { + return new Group(this.mode, level, this.category, name, optionalParams, this); + } + collapsed(flag = true) { + this._collapsed = flag; + return this; + } + log(message, ...optionalParams) { + if (!this.enabled) + return this; + if (!this.initialized) { + if (this.mode == GroupMode.NATIVE) { + if (this._collapsed && console.groupCollapsed) + console.groupCollapsed(this.name, ...this.optionalParams); + else + console.group(this.name, ...this.optionalParams); + } + else { + this._log_prefix = " "; + let parent = this.owner; + while (parent) { + if (parent.mode == GroupMode.PREFIX) + this._log_prefix = this._log_prefix + parent._log_prefix; + else + break; + } + } + this.initialized = true; + } + if (this.mode == GroupMode.NATIVE) + logDirect(this.level, message, ...optionalParams); + else { + logDirect(this.level, "[%s] " + this._log_prefix + message, category_mapping.get(this.category), ...optionalParams); + } + return this; + } + end() { + if (this.initialized) { + if (this.mode == GroupMode.NATIVE) + console.groupEnd(); + } + } + get prefix() { + return this._log_prefix; + } + set prefix(prefix) { + this._log_prefix = prefix; + } + } + log_1.Group = Group; +})(log || (log = {})); +var LogType = log.LogType; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["20f9ceefb8afb92246cf15502902caa1a99609daaa67d6f61840b5171d8bbc8d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["20f9ceefb8afb92246cf15502902caa1a99609daaa67d6f61840b5171d8bbc8d"] = "20f9ceefb8afb92246cf15502902caa1a99609daaa67d6f61840b5171d8bbc8d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "zYWR4o3Z", path: "D:/TeaSpeak/web/shared/js/proto.ts (65,31)" }, { name: "iE5caXLV", path: "D:/TeaSpeak/web/shared/js/proto.ts (69,31)" }, { name: "TjsDTpLS", path: "D:/TeaSpeak/web/shared/js/proto.ts (91,26)" }, { name: "gwdhfC2P", path: "D:/TeaSpeak/web/shared/js/proto.ts (244,33)" }, { name: "owW67Cw3", path: "D:/TeaSpeak/web/shared/js/proto.ts (246,32)" }, { name: "CdWCrwjn", path: "D:/TeaSpeak/web/shared/js/proto.ts (248,33)" }, { name: "HTRvaP1x", path: "D:/TeaSpeak/web/shared/js/proto.ts (250,35)" }, { name: "bgcJDudo", path: "D:/TeaSpeak/web/shared/js/proto.ts (252,35)" }, { name: "q1rcyqp9", path: "D:/TeaSpeak/web/shared/js/proto.ts (254,18)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +if (!JSON.map_to) { + JSON.map_to = function (object, json, variables, validator, variable_direction) { + if (!validator) + validator = (a, b) => true; + if (!variables) { + variables = []; + if (!variable_direction || variable_direction == 0) { + for (let field in json) + variables.push(field); + } + else if (variable_direction == 1) { + for (let field in object) + variables.push(field); + } + } + else if (!Array.isArray(variables)) { + variables = [variables]; + } + let updates = 0; + for (let field of variables) { + if (typeof json[field] === "undefined") { + console.trace(_translations.zYWR4o3Z || (_translations.zYWR4o3Z = tr("Json does not contains %s")), field); + continue; + } + if (!validator(field, json[field])) { + console.trace(_translations.iE5caXLV || (_translations.iE5caXLV = tr("Validator results in false for %s")), field); + continue; + } + if (JSON.map_field_to(object, json[field], field)) + updates++; + } + return updates; + }; +} +if (!JSON.map_field_to) { + JSON.map_field_to = function (object, value, field) { + let field_type = typeof (object[field]); + let new_object; + if (field_type == "string" || field_type == "object" || field_type == "undefined") + new_object = value; + else if (field_type == "number") + new_object = parseFloat(value); + else if (field_type == "boolean") + new_object = value == "1" || value == "true"; + else { + console.warn(_translations.TjsDTpLS || (_translations.TjsDTpLS = tr("Invalid object type %s for entry %s")), field_type, field); + return false; + } + if (new_object === object[field]) + return false; + object[field] = new_object; + return true; + }; +} +if (!Array.prototype.remove) { + Array.prototype.remove = function (elem) { + const index = this.indexOf(elem, 0); + if (index > -1) { + this.splice(index, 1); + return true; + } + return false; + }; +} +if (!Array.prototype.pop_front) { + Array.prototype.pop_front = function () { + if (this.length == 0) + return undefined; + return this.splice(0, 1)[0]; + }; +} +if (!Array.prototype.last) { + Array.prototype.last = function () { + if (this.length == 0) + return undefined; + return this[this.length - 1]; + }; +} +if (typeof ($) !== "undefined") { + if (!$.spawn) { + $.spawn = function (tagName) { + return $(document.createElement(tagName)); + }; + } + if (!$.fn.renderTag) { + $.fn.renderTag = function (values) { + let result; + if (this.render) { + result = $(this.render(values)); + } + else { + const template = window.jsrender.render[this.attr("id")]; + if (!template) { + console.error("Tried to render template %o, but template is not available!", this.attr("id")); + throw "missing template " + this.attr("id"); + } + /* + result = window.jsrender.templates("tmpl_permission_entry", $("#tmpl_permission_entry").html()); + result = window.jsrender.templates("xxx", this.html()); + */ + result = template(values); + result = $(result); + } + result.find("node").each((index, element) => { + $(element).replaceWith(values[$(element).attr("key")] || (values[0] || [])[$(element).attr("key")]); + }); + return result; + }; + } + if (!$.fn.hasScrollBar) + $.fn.hasScrollBar = function (direction) { + if (this.length <= 0) + return false; + const scroll_height = this.get(0).scrollHeight > this.height(); + const scroll_width = this.get(0).scrollWidth > this.width(); + if (typeof (direction) === "string") { + if (direction === "height") + return scroll_height; + if (direction === "width") + return scroll_width; + } + return scroll_width || scroll_height; + }; + if (!$.fn.visible_height) + $.fn.visible_height = function () { + const original_style = this.attr("style"); + this.css({ + position: 'absolute!important', + visibility: 'hidden!important', + display: 'block!important' + }); + const result = this.height(); + this.attr("style", original_style || ""); + return result; + }; + if (!$.fn.visible_width) + $.fn.visible_width = function () { + const original_style = this.attr("style"); + this.css({ + position: 'absolute!important', + visibility: 'hidden!important', + display: 'block!important' + }); + const result = this.width(); + this.attr("style", original_style || ""); + return result; + }; + if (!$.fn.firstParent) + $.fn.firstParent = function (selector) { + if (this.is(selector)) + return this; + return this.parent(selector); + }; +} +if (!String.prototype.format) { + String.prototype.format = function () { + const args = arguments; + let array = args.length == 1 && $.isArray(args[0]); + return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) { + if (m == "{{") { + return "{"; + } + if (m == "}}") { + return "}"; + } + return array ? args[0][n] : args[n]; + }); + }; +} +function concatenate(resultConstructor, ...arrays) { + let totalLength = 0; + for (const arr of arrays) { + totalLength += arr.length; + } + const result = new resultConstructor(totalLength); + let offset = 0; + for (const arr of arrays) { + result.set(arr, offset); + offset += arr.length; + } + return result; +} +function formatDate(secs) { + let years = Math.floor(secs / (60 * 60 * 24 * 365)); + let days = Math.floor(secs / (60 * 60 * 24)) % 365; + let hours = Math.floor(secs / (60 * 60)) % 24; + let minutes = Math.floor(secs / 60) % 60; + let seconds = Math.floor(secs % 60); + let result = ""; + if (years > 0) + result += years + " " + (_translations.gwdhfC2P || (_translations.gwdhfC2P = tr("years"))) + " "; + if (years > 0 || days > 0) + result += days + " " + (_translations.owW67Cw3 || (_translations.owW67Cw3 = tr("days"))) + " "; + if (years > 0 || days > 0 || hours > 0) + result += hours + " " + (_translations.CdWCrwjn || (_translations.CdWCrwjn = tr("hours"))) + " "; + if (years > 0 || days > 0 || hours > 0 || minutes > 0) + result += minutes + " " + (_translations.HTRvaP1x || (_translations.HTRvaP1x = tr("minutes"))) + " "; + if (years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) + result += seconds + " " + (_translations.bgcJDudo || (_translations.bgcJDudo = tr("seconds"))) + " "; + else + result = (_translations.q1rcyqp9 || (_translations.q1rcyqp9 = tr("now"))) + " "; + return result.substr(0, result.length - 1); +} +function calculate_width(text) { + let element = $.spawn("div"); + element.text(text) + .css("display", "none") + .css("margin", 0); + $("body").append(element); + let size = element.width(); + element.detach(); + return size; +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["da3cc8c2045432c24c8da99176efcb06d5865aeca6fdc4cbb6208410d361cae2"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["da3cc8c2045432c24c8da99176efcb06d5865aeca6fdc4cbb6208410d361cae2"] = "da3cc8c2045432c24c8da99176efcb06d5865aeca6fdc4cbb6208410d361cae2"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/* +interface Window { + TextEncoder: any; +} +*/ +var sha; +(function (sha) { + /* + * [js-sha1]{@link https://github.com/emn178/js-sha1} + * + * @version 0.6.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2017 + * @license MIT + */ + /*jslint bitwise: true */ + (function () { + 'use strict'; + let root = typeof window === 'object' ? window : {}; + let NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } + let COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports; + let AMD = typeof define === 'function' && define.amd; + let HEX_CHARS = '0123456789abcdef'.split(''); + let EXTRA = [-2147483648, 8388608, 32768, 128]; + let SHIFT = [24, 16, 8, 0]; + let OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + let blocks = []; + let createOutputMethod = function (outputType) { + return function (message) { + return new Sha1(true).update(message)[outputType](); + }; + }; + let createMethod = function () { + let method = createOutputMethod('hex'); + if (NODE_JS) { + method = nodeWrap(method); + } + method.create = function () { + return new Sha1(); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type); + } + return method; + }; + var nodeWrap = function (method) { + var crypto = eval("require('crypto')"); + var Buffer = eval("require('buffer').Buffer"); + var nodeMethod = function (message) { + if (typeof message === 'string') { + return crypto.createHash('sha1').update(message, 'utf8').digest('hex'); + } + else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + else if (message.length === undefined) { + return method(message); + } + return crypto.createHash('sha1').update(new Buffer(message)).digest('hex'); + }; + return nodeMethod; + }; + function Sha1(sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } + else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + this.h0 = 0x67452301; + this.h1 = 0xEFCDAB89; + this.h2 = 0x98BADCFE; + this.h3 = 0x10325476; + this.h4 = 0xC3D2E1F0; + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + } + Sha1.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString = typeof (message) !== 'string'; + if (notString && message.constructor === root.ArrayBuffer) { + message = new Uint8Array(message); + } + var code, index = 0, i, length = message.length || 0, blocks = this.blocks; + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; + } + } + else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >> 2] |= code << SHIFT[i++ & 3]; + } + else if (code < 0x800) { + blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + else if (code < 0xd800 || code >= 0xe000) { + blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } + else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; + Sha1.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + var blocks = this.blocks, i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + Sha1.prototype.hash = function () { + var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4; + var f, j, t, blocks = this.blocks; + for (j = 16; j < 80; ++j) { + t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; + blocks[j] = (t << 1) | (t >>> 31); + } + for (j = 0; j < 20; j += 5) { + f = (b & c) | ((~b) & d); + t = (a << 5) | (a >>> 27); + e = t + f + e + 1518500249 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = (a & b) | ((~a) & c); + t = (e << 5) | (e >>> 27); + d = t + f + d + 1518500249 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = (e & a) | ((~e) & b); + t = (d << 5) | (d >>> 27); + c = t + f + c + 1518500249 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = (d & e) | ((~d) & a); + t = (c << 5) | (c >>> 27); + b = t + f + b + 1518500249 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = (c & d) | ((~c) & e); + t = (b << 5) | (b >>> 27); + a = t + f + a + 1518500249 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 40; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e + 1859775393 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d + 1859775393 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c + 1859775393 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b + 1859775393 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a + 1859775393 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 60; j += 5) { + f = (b & c) | (b & d) | (c & d); + t = (a << 5) | (a >>> 27); + e = t + f + e - 1894007588 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = (a & b) | (a & c) | (b & c); + t = (e << 5) | (e >>> 27); + d = t + f + d - 1894007588 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = (e & a) | (e & b) | (a & b); + t = (d << 5) | (d >>> 27); + c = t + f + c - 1894007588 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = (d & e) | (d & a) | (e & a); + t = (c << 5) | (c >>> 27); + b = t + f + b - 1894007588 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = (c & d) | (c & e) | (d & e); + t = (b << 5) | (b >>> 27); + a = t + f + a - 1894007588 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + for (; j < 80; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e - 899497514 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d - 899497514 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c - 899497514 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b - 899497514 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a - 899497514 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + }; + Sha1.prototype.hex = function () { + this.finalize(); + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F]; + }; + Sha1.prototype.toString = Sha1.prototype.hex; + Sha1.prototype.digest = function () { + this.finalize(); + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + return [ + (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, + (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, + (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, + (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, + (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF + ]; + }; + Sha1.prototype.array = Sha1.prototype.digest; + Sha1.prototype.arrayBuffer = function () { + this.finalize(); + var buffer = new ArrayBuffer(20); + var dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + return buffer; + }; + var exports = createMethod(); + if (COMMON_JS) { + module.exports = exports; + } + else { + root._sha1 = exports; + if (AMD) { + define(function () { + return exports; + }); + } + } + })(); + function encode_text(buffer) { + if (window.TextEncoder) { + return new TextEncoder().encode(buffer).buffer; + } + let utf8 = unescape(encodeURIComponent(buffer)); + let result = new Uint8Array(utf8.length); + for (let i = 0; i < utf8.length; i++) { + result[i] = utf8.charCodeAt(i); + } + return result.buffer; + } + sha.encode_text = encode_text; + function sha1(message) { + if (!(typeof (message) === "string" || message instanceof ArrayBuffer)) + throw "Invalid type!"; + let buffer = message instanceof ArrayBuffer ? message : encode_text(message); + if (!crypto || !crypto.subtle || !crypto.subtle.digest || /Edge/.test(navigator.userAgent)) + return new Promise(resolve => { + resolve(_sha1.arrayBuffer(buffer)); + }); + else + return crypto.subtle.digest("SHA-1", buffer); + } + sha.sha1 = sha1; +})(sha || (sha = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["935e0da2013d91ea7f19563d4f99c3afd9a64fbd92648d10f12100847a7e2706"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["935e0da2013d91ea7f19563d4f99c3afd9a64fbd92648d10f12100847a7e2706"] = "935e0da2013d91ea7f19563d4f99c3afd9a64fbd92648d10f12100847a7e2706"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "vp3qN7Lv", path: "D:/TeaSpeak/web/shared/js/utils/helpers.ts (64,17)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var helpers; +(function (helpers) { + function hashPassword(password) { + return new Promise((resolve, reject) => { + sha.sha1(password).then(result => { + resolve(btoa(String.fromCharCode.apply(null, new Uint8Array(result)))); + }); + }); + } + helpers.hashPassword = hashPassword; +})(helpers || (helpers = {})); +class LaterPromise extends Promise { + constructor() { + super((resolve, reject) => { }); + this._handle = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + this._time = Date.now(); + } + resolved(object) { + this._resolve(object); + } + rejected(reason) { + this._reject(reason); + } + function_rejected() { + return error => this.rejected(error); + } + time() { return this._time; } + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled, onrejected) { + return this._handle.then(onfulfilled, onrejected); + } + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected) { + return this._handle.then(onrejected); + } +} +const copy_to_clipboard = str => { + console.log(_translations.vp3qN7Lv || (_translations.vp3qN7Lv = tr("Copy text to clipboard: %s")), str); + const el = document.createElement('textarea'); + el.value = str; + el.setAttribute('readonly', ''); + el.style.position = 'absolute'; + el.style.left = '-9999px'; + document.body.appendChild(el); + const selected = document.getSelection().rangeCount > 0 + ? document.getSelection().getRangeAt(0) + : false; + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); + if (selected) { + document.getSelection().removeAllRanges(); + document.getSelection().addRange(selected); + } +}; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["ea470d3765848757dcda6d47b98223fcb9ac6df163ab3f988099385249d28f40"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["ea470d3765848757dcda6d47b98223fcb9ac6df163ab3f988099385249d28f40"] = "ea470d3765848757dcda6d47b98223fcb9ac6df163ab3f988099385249d28f40"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "hhpJHMRa", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (279,36)" }, { name: "knrsc2gO", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (286,36)" }, { name: "xE5tdDeb", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (293,36)" }, { name: "QB55gRZX", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (300,36)" }, { name: "MbfBEncM", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (307,36)" }, { name: "Vsb7qSul", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (410,44)" }, { name: "ue16j2uQ", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (509,28)" }, { name: "uA6gN1CT", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (514,28)" }, { name: "Q5gkqCHu", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (525,23)" }, { name: "ZbnjV_nC", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (540,40)" }, { name: "_NrpAgeh", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (547,40)" }, { name: "QmKAmMQq", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (554,40)" }, { name: "K_D5E5Qr", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (565,23)" }, { name: "SE5yjugN", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (572,59)" }, { name: "xQ2nlySI", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (599,23)" }, { name: "Z8BBjPN6", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (611,23)" }, { name: "pWVzIrse", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (614,41)" }, { name: "gDrg7wuy", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (614,73)" }, { name: "N3R6GVVn", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (620,42)" }, { name: "JmxXwa_z", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (620,98)" }, { name: "TWGmtLXk", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (628,23)" }, { name: "i2ZLxHht", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (634,23)" }, { name: "lytbqzbt", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (712,82)" }, { name: "UH_cWHFT", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (835,30)" }, { name: "LeX07FON", path: "D:/TeaSpeak/web/shared/js/ui/channel.ts (835,54)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +var ChannelType; +(function (ChannelType) { + ChannelType[ChannelType["PERMANENT"] = 0] = "PERMANENT"; + ChannelType[ChannelType["SEMI_PERMANENT"] = 1] = "SEMI_PERMANENT"; + ChannelType[ChannelType["TEMPORARY"] = 2] = "TEMPORARY"; +})(ChannelType || (ChannelType = {})); +(function (ChannelType) { + function normalize(mode) { + let value = ChannelType[mode]; + value = value.toLowerCase(); + return value[0].toUpperCase() + value.substr(1); + } + ChannelType.normalize = normalize; +})(ChannelType || (ChannelType = {})); +var ChannelSubscribeMode; +(function (ChannelSubscribeMode) { + ChannelSubscribeMode[ChannelSubscribeMode["SUBSCRIBED"] = 0] = "SUBSCRIBED"; + ChannelSubscribeMode[ChannelSubscribeMode["UNSUBSCRIBED"] = 1] = "UNSUBSCRIBED"; + ChannelSubscribeMode[ChannelSubscribeMode["INHERITED"] = 2] = "INHERITED"; +})(ChannelSubscribeMode || (ChannelSubscribeMode = {})); +class ChannelProperties { + constructor() { + this.channel_order = 0; + this.channel_name = ""; + this.channel_name_phonetic = ""; + this.channel_topic = ""; + this.channel_password = ""; + this.channel_codec = 4; + this.channel_codec_quality = 0; + this.channel_codec_is_unencrypted = false; + this.channel_maxclients = -1; + this.channel_maxfamilyclients = -1; + this.channel_needed_talk_power = 1; + this.channel_flag_permanent = false; + this.channel_flag_semi_permanent = false; + this.channel_flag_default = false; + this.channel_flag_password = false; + this.channel_flag_maxclients_unlimited = false; + this.channel_flag_maxfamilyclients_inherited = false; + this.channel_flag_maxfamilyclients_unlimited = false; + this.channel_icon_id = 0; + this.channel_delete_delay = 0; + //Only after request + this.channel_description = ""; + this.channel_flag_conversation_private = true; /* TeamSpeak mode */ + this.channel_conversation_history_length = -1; + } +} +class ChannelEntry { + constructor(channelId, channelName, parent = null) { + this.properties = new ChannelProperties(); + this._channel_name_alignment = undefined; + this._channel_name_formatted = undefined; + this._family_index = 0; + this._destroyed = false; + this._cached_channel_description = undefined; + this._cached_channel_description_promise = undefined; + this._cached_channel_description_promise_resolve = undefined; + this._cached_channel_description_promise_reject = undefined; + this.properties = new ChannelProperties(); + this.channelId = channelId; + this.properties.channel_name = channelName; + this.parent = parent; + this.channelTree = null; + this.initializeTag(); + this.__updateChannelName(); + } + destroy() { + this._destroyed = true; + if (this._tag_root) { + this._tag_root.remove(); /* removes also all other tags */ + this._tag_root = undefined; + } + this._tag_siblings = undefined; + this._tag_channel = undefined; + this._tag_clients = undefined; + this._cached_channel_description_promise = undefined; + this._cached_channel_description_promise_resolve = undefined; + this._cached_channel_description_promise_reject = undefined; + this.channel_previous = undefined; + this.parent = undefined; + this.channel_next = undefined; + this.channelTree = undefined; + } + channelName() { + return this.properties.channel_name; + } + formattedChannelName() { + return this._channel_name_formatted || this.properties.channel_name; + } + getChannelDescription() { + if (this._cached_channel_description) + return new Promise(resolve => resolve(this._cached_channel_description)); + if (this._cached_channel_description_promise) + return this._cached_channel_description_promise; + this.channelTree.client.serverConnection.send_command("channelgetdescription", { cid: this.channelId }).catch(error => { + this._cached_channel_description_promise_reject(error); + }); + return this._cached_channel_description_promise = new Promise((resolve, reject) => { + this._cached_channel_description_promise_resolve = resolve; + this._cached_channel_description_promise_reject = reject; + }); + } + parent_channel() { return this.parent; } + hasParent() { return this.parent != null; } + getChannelId() { return this.channelId; } + children(deep = false) { + const result = []; + if (this.channelTree == null) + return []; + const self = this; + this.channelTree.channels.forEach(function (entry) { + let current = entry; + if (deep) { + while (current) { + if (current.parent_channel() == self) { + result.push(entry); + break; + } + current = current.parent_channel(); + } + } + else if (current.parent_channel() == self) + result.push(entry); + }); + return result; + } + clients(deep = false) { + const result = []; + if (this.channelTree == null) + return []; + const self = this; + this.channelTree.clients.forEach(function (entry) { + let current = entry.currentChannel(); + if (deep) { + while (current) { + if (current == self) { + result.push(entry); + break; + } + current = current.parent_channel(); + } + } + else if (current == self) + result.push(entry); + }); + return result; + } + clients_ordered() { + const clients = this.clients(false); + clients.sort((a, b) => { + if (a.properties.client_talk_power < b.properties.client_talk_power) + return 1; + if (a.properties.client_talk_power > b.properties.client_talk_power) + return -1; + if (a.properties.client_nickname > b.properties.client_nickname) + return 1; + if (a.properties.client_nickname < b.properties.client_nickname) + return -1; + return 0; + }); + return clients; + } + update_family_index(enforce) { + const current_index = this._family_index; + const new_index = this.calculate_family_index(true); + if (current_index == new_index && !enforce) + return; + this._tag_channel.css("z-index", this._family_index); + this._tag_channel.css("padding-left", ((this._family_index + 1) * 16 + 10) + "px"); + } + calculate_family_index(enforce_recalculate = false) { + if (this._family_index !== undefined && !enforce_recalculate) + return this._family_index; + this._family_index = 0; + let channel = this.parent_channel(); + while (channel) { + this._family_index++; + channel = channel.parent_channel(); + } + return this._family_index; + } + initializeTag() { + const tag_channel = $.spawn("div").addClass("tree-entry channel"); + { + const container_entry = $.spawn("div").addClass("container-channel"); + container_entry.attr("channel-id", this.channelId); + container_entry.addClass(this._channel_name_alignment); + /* unread marker */ + { + container_entry.append($.spawn("div") + .addClass("marker-text-unread hidden") + .attr("conversation", this.channelId)); + } + /* channel icon (type) */ + { + container_entry.append($.spawn("div") + .addClass("show-channel-normal-only channel-type icon client-channel_green_subscribed")); + } + /* channel name */ + { + container_entry.append($.spawn("div") + .addClass("container-channel-name") + .append($.spawn("a") + .addClass("channel-name") + .text(this.channelName()))); + } + /* all icons (last element) */ + { + //Icons + let container_icons = $.spawn("span").addClass("icons"); + //Default icon (5) + container_icons.append($.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_default icon client-channel_default") + .attr("title", _translations.hhpJHMRa || (_translations.hhpJHMRa = tr("Default channel")))); + //Password icon (4) + container_icons.append($.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_password icon client-register") + .attr("title", _translations.knrsc2gO || (_translations.knrsc2gO = tr("The channel is password protected")))); + //Music icon (3) + container_icons.append($.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_music icon client-music") + .attr("title", _translations.xE5tdDeb || (_translations.xE5tdDeb = tr("Music quality")))); + //Channel moderated (2) + container_icons.append($.spawn("div") + .addClass("show-channel-normal-only icon_entry icon_moderated icon client-moderated") + .attr("title", _translations.QB55gRZX || (_translations.QB55gRZX = tr("Channel is moderated")))); + //Channel Icon (1) + container_icons.append($.spawn("div") + .addClass("show-channel-normal-only icon_entry channel_icon") + .attr("title", _translations.MbfBEncM || (_translations.MbfBEncM = tr("Channel icon")))); + //Default no sound (0) + let container = $.spawn("div") + .css("position", "relative") + .addClass("icon_no_sound"); + let noSound = $.spawn("div") + .addClass("icon_entry icon client-conflict-icon") + .attr("title", "You don't support the channel codec"); + let bg = $.spawn("div") + .width(10) + .height(14) + .css("background", "red") + .css("position", "absolute") + .css("top", "1px") + .css("left", "3px") + .css("z-index", "-1"); + bg.appendTo(container); + noSound.appendTo(container); + container_icons.append(container); + container_icons.appendTo(container_entry); + } + tag_channel.append(this._tag_channel = container_entry); + this.update_family_index(true); + } + { + const container_client = $.spawn("div").addClass("container-clients"); + tag_channel.append(this._tag_clients = container_client); + } + { + const container_children = $.spawn("div").addClass("container-children"); + tag_channel.append(this._tag_siblings = container_children); + } + /* + setInterval(() => { + let color = (Math.random() * 10000000).toString(16).substr(0, 6); + tag_channel.css("background", "#" + color); + }, 150); + */ + this._tag_root = tag_channel; + } + rootTag() { + return this._tag_root; + } + channelTag() { + return this._tag_channel; + } + siblingTag() { + return this._tag_siblings; + } + clientTag() { + return this._tag_clients; + } + reorderClients(sync) { + if (this._reorder_timer) { + if (!sync) + return; + clearTimeout(this._reorder_timer); + this._reorder_timer = undefined; + } + else if (!sync) { + this._reorder_timer = setTimeout(() => { + this._reorder_timer = undefined; + this.reorderClients(true); + }, 5); + return; + } + let clients = this.clients(); + if (clients.length > 1) { + clients.sort((a, b) => { + if (a.properties.client_talk_power < b.properties.client_talk_power) + return 1; + if (a.properties.client_talk_power > b.properties.client_talk_power) + return -1; + if (a.properties.client_nickname > b.properties.client_nickname) + return 1; + if (a.properties.client_nickname < b.properties.client_nickname) + return -1; + return 0; + }); + clients.reverse(); + for (let index = 0; index + 1 < clients.length; index++) + clients[index].tag.before(clients[index + 1].tag); + log.debug(LogCategory.CHANNEL, _translations.Vsb7qSul || (_translations.Vsb7qSul = tr("Reordered channel clients: %d")), clients.length); + for (let client of clients) { + log.debug(LogCategory.CHANNEL, "- %i %s", client.properties.client_talk_power, client.properties.client_nickname); + } + } + } + initializeListener() { + const tag_channel = this.channelTag(); + tag_channel.on('click', () => this.channelTree.onSelect(this)); + tag_channel.on('dblclick', () => { + if ($.isArray(this.channelTree.currently_selected)) { //Multiselect + return; + } + this.joinChannel(); + }); + let last_touch = 0; + let touch_start = 0; + tag_channel.on('touchend', event => { + /* if over 250ms then its not a click its more a drag */ + if (Date.now() - touch_start > 250) { + touch_start = 0; + return; + } + if (Date.now() - last_touch > 750) { + last_touch = Date.now(); + return; + } + last_touch = Date.now(); + /* double touch */ + tag_channel.trigger('dblclick'); + }); + tag_channel.on('touchstart', event => { + touch_start = Date.now(); + }); + if (!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) { + this.channelTag().on("contextmenu", (event) => { + event.preventDefault(); + if ($.isArray(this.channelTree.currently_selected)) { //Multiselect + (this.channelTree.currently_selected_context_callback || ((_) => null))(event); + return; + } + this.channelTree.onSelect(this, true); + this.showContextMenu(event.pageX, event.pageY, () => { + this.channelTree.onSelect(undefined, true); + }); + }); + } + } + showContextMenu(x, y, on_close = undefined) { + let channelCreate = !![ + PermissionType.B_CHANNEL_CREATE_TEMPORARY, + PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT, + PermissionType.B_CHANNEL_CREATE_PERMANENT + ].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1)); + let channelModify = !![ + PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT, + PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT, + PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT, + PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY, + PermissionType.B_CHANNEL_MODIFY_NAME, + PermissionType.B_CHANNEL_MODIFY_TOPIC, + PermissionType.B_CHANNEL_MODIFY_DESCRIPTION, + PermissionType.B_CHANNEL_MODIFY_PASSWORD, + PermissionType.B_CHANNEL_MODIFY_CODEC, + PermissionType.B_CHANNEL_MODIFY_CODEC_QUALITY, + PermissionType.B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR, + PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS, + PermissionType.B_CHANNEL_MODIFY_MAXFAMILYCLIENTS, + PermissionType.B_CHANNEL_MODIFY_SORTORDER, + PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER, + PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED, + PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY, + PermissionType.B_ICON_MANAGE + ].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1)); + let flagDelete = true; + if (this.clients(true).length > 0) + flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_FLAG_FORCE).granted(1); + if (flagDelete) { + if (this.properties.channel_flag_permanent) + flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_PERMANENT).granted(1); + else if (this.properties.channel_flag_semi_permanent) + flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_PERMANENT).granted(1); + else + flagDelete = this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_DELETE_TEMPORARY).granted(1); + } + let trigger_close = true; + const bold = text => contextmenu.get_provider().html_format_enabled() ? "" + text + "" : text; + contextmenu.spawn_context_menu(x, y, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_switch", + name: bold(_translations.ue16j2uQ || (_translations.ue16j2uQ = tr("Switch to channel"))), + callback: () => this.joinChannel() + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_switch", + name: bold(_translations.uA6gN1CT || (_translations.uA6gN1CT = tr("Join text channel"))), + callback: () => { + this.channelTree.client.side_bar.channel_conversations().set_current_channel(this.getChannelId()); + this.channelTree.client.side_bar.show_channel_conversations(); + }, + visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT) + }, { + type: contextmenu.MenuEntryType.HR, + name: '' + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.Q5gkqCHu || (_translations.Q5gkqCHu = tr("Show channel info")), + callback: () => { + trigger_close = false; + Modals.openChannelInfo(this); + }, + icon_class: "client-about" + }, ...(() => { + const local_client = this.channelTree.client.getClient(); + if (!local_client || local_client.currentChannel() !== this) + return [ + contextmenu.Entry.HR(), + { + type: contextmenu.MenuEntryType.ENTRY, + icon: "client-subscribe_to_channel", + name: bold(_translations.ZbnjV_nC || (_translations.ZbnjV_nC = tr("Subscribe to channel"))), + callback: () => this.subscribe(), + visible: !this.flag_subscribed + }, + { + type: contextmenu.MenuEntryType.ENTRY, + icon: "client-channel_unsubscribed", + name: bold(_translations._NrpAgeh || (_translations._NrpAgeh = tr("Unsubscribe from channel"))), + callback: () => this.unsubscribe(), + visible: this.flag_subscribed + }, + { + type: contextmenu.MenuEntryType.ENTRY, + icon: "client-subscribe_mode", + name: bold(_translations.QmKAmMQq || (_translations.QmKAmMQq = tr("Use inherited subscribe mode"))), + callback: () => this.unsubscribe(true), + visible: this.subscribe_mode != ChannelSubscribeMode.INHERITED + } + ]; + return []; + })(), contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_edit", + name: _translations.K_D5E5Qr || (_translations.K_D5E5Qr = tr("Edit channel")), + invalidPermission: !channelModify, + callback: () => { + Modals.createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes, permissions) => { + if (changes) { + changes["cid"] = this.channelId; + this.channelTree.client.serverConnection.send_command("channeledit", changes); + log.info(LogCategory.CHANNEL, _translations.SE5yjugN || (_translations.SE5yjugN = tr("Changed channel properties of channel %s: %o")), this.channelName(), changes); + } + if (permissions && permissions.length > 0) { + let perms = []; + for (let perm of permissions) { + perms.push({ + permvalue: perm.value, + permnegated: false, + permskip: false, + permid: perm.type.id + }); + } + perms[0]["cid"] = this.channelId; + this.channelTree.client.serverConnection.send_command("channeladdperm", perms, { + flagset: ["continueonerror"] + }).then(() => { + this.channelTree.client.sound.play(Sound.CHANNEL_EDITED_SELF); + }); + } + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_delete", + name: _translations.xQ2nlySI || (_translations.xQ2nlySI = tr("Delete channel")), + invalidPermission: !flagDelete, + callback: () => { + this.channelTree.client.serverConnection.send_command("channeldelete", { cid: this.channelId }).then(() => { + this.channelTree.client.sound.play(Sound.CHANNEL_DELETED); + }); + } + }, contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-addon-collection", + name: _translations.Z8BBjPN6 || (_translations.Z8BBjPN6 = tr("Create music bot")), + callback: () => { + this.channelTree.client.serverConnection.send_command("musicbotcreate", { cid: this.channelId }).then(() => { + createInfoModal(_translations.pWVzIrse || (_translations.pWVzIrse = tr("Bot successfully created")), _translations.gDrg7wuy || (_translations.gDrg7wuy = tr("Bot has been successfully created."))).open(); + }).catch(error => { + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.N3R6GVVn || (_translations.N3R6GVVn = tr("Failed to create bot")), MessageHelper.formatMessage(_translations.JmxXwa_z || (_translations.JmxXwa_z = tr("Failed to create the music bot:
{0}")), error)).open(); + }); + } + }, contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_create_sub", + name: _translations.TWGmtLXk || (_translations.TWGmtLXk = tr("Create sub channel")), + invalidPermission: !(channelCreate && this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_CHILD).granted(1)), + callback: () => this.channelTree.spawnCreateChannel(this) + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_create", + name: _translations.i2ZLxHht || (_translations.i2ZLxHht = tr("Create channel")), + invalidPermission: !channelCreate, + callback: () => this.channelTree.spawnCreateChannel() + }, contextmenu.Entry.CLOSE(() => trigger_close ? on_close() : {})); + } + handle_frame_resized() { + if (this._channel_name_formatted === "align-repetitive") + this.__updateChannelName(); + } + __updateChannelName() { + this._channel_name_formatted = undefined; + parse_type: if (this.parent_channel() == null && this.properties.channel_name.charAt(0) == '[') { + let end = this.properties.channel_name.indexOf(']'); + if (end == -1) + break parse_type; + let options = this.properties.channel_name.substr(1, end - 1); + if (options.indexOf("spacer") == -1) + break parse_type; + options = options.substr(0, options.indexOf("spacer")); + if (options.length == 0) + options = "l"; + else if (options.length > 1) + options = options[0]; + switch (options) { + case "r": + this._channel_name_alignment = "align-right"; + break; + case "l": + this._channel_name_alignment = "align-left"; + break; + case "c": + this._channel_name_alignment = "align-center"; + break; + case "*": + this._channel_name_alignment = "align-repetitive"; + break; + default: + this._channel_name_alignment = undefined; + break parse_type; + } + this._channel_name_formatted = this.properties.channel_name.substr(end + 1) || ""; + } + this._tag_channel.find(".show-channel-normal-only").toggleClass("channel-normal", this._channel_name_formatted === undefined); + const tag_container_name = this._tag_channel.find(".container-channel-name"); + tag_container_name.removeClass(ChannelEntry.NAME_ALIGNMENTS.join(" ")); + const tag_name = tag_container_name.find(".channel-name"); + let text = this._channel_name_formatted === undefined ? this.properties.channel_name : this._channel_name_formatted; + if (this._channel_name_formatted !== undefined) { + tag_container_name.addClass(this._channel_name_alignment); + if (this._channel_name_alignment == "align-repetitive" && text.length > 0) { + while (text.length < 1024 * 8) + text += text; + } + } + tag_name.text(text); + } + recalculate_repetitive_name() { + if (this._channel_name_alignment == "align-repetitive") + this.__updateChannelName(); + } + updateVariables(...variables) { + let group = log.group(log.LogType.DEBUG, LogCategory.CHANNEL_PROPERTIES, _translations.lytbqzbt || (_translations.lytbqzbt = tr("Update properties (%i) of %s (%i)")), variables.length, this.channelName(), this.getChannelId()); + { + const entries = []; + for (const variable of variables) + entries.push({ + key: variable.key, + value: variable.value, + type: typeof (this.properties[variable.key]) + }); + log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Clannel update properties", entries); + } + let info_update = false; + for (let variable of variables) { + let key = variable.key; + let value = variable.value; + JSON.map_field_to(this.properties, value, variable.key); + if (key == "channel_name") { + this.__updateChannelName(); + info_update = true; + } + else if (key == "channel_order") { + let order = this.channelTree.findChannel(this.properties.channel_order); + this.channelTree.moveChannel(this, order, this.parent); + } + else if (key == "channel_icon_id") { + /* For more detail lookup client::updateVariables and client_icon_id! + * ATTENTION: This is required! + */ + this.properties.channel_icon_id = variable.value >>> 0; + let tag = this.channelTag().find(".icons .channel_icon"); + (this.properties.channel_icon_id > 0 ? $.fn.show : $.fn.hide).apply(tag); + if (this.properties.channel_icon_id > 0) { + tag.children().detach(); + this.channelTree.client.fileManager.icons.generateTag(this.properties.channel_icon_id).appendTo(tag); + } + info_update = true; + } + else if (key == "channel_codec") { + (this.properties.channel_codec == 5 || this.properties.channel_codec == 3 ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_music")); + this.channelTag().find(".icons .icon_no_sound").toggle(!(this.channelTree.client.serverConnection.support_voice() && + this.channelTree.client.serverConnection.voice_connection().decoding_supported(this.properties.channel_codec))); + } + else if (key == "channel_flag_default") { + (this.properties.channel_flag_default ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_default")); + } + else if (key == "channel_flag_password") + (this.properties.channel_flag_password ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_password")); + else if (key == "channel_needed_talk_power") + (this.properties.channel_needed_talk_power > 0 ? $.fn.show : $.fn.hide).apply(this.channelTag().find(".icons .icon_moderated")); + else if (key == "channel_description") { + this._cached_channel_description = undefined; + if (this._cached_channel_description_promise_resolve) + this._cached_channel_description_promise_resolve(value); + this._cached_channel_description_promise = undefined; + this._cached_channel_description_promise_resolve = undefined; + this._cached_channel_description_promise_reject = undefined; + } + if (key == "channel_maxclients" || key == "channel_maxfamilyclients" || key == "channel_flag_private" || key == "channel_flag_password") { + this.updateChannelTypeIcon(); + info_update = true; + } + if (key == "channel_flag_conversation_private") { + const conversations = this.channelTree.client.side_bar.channel_conversations(); + const conversation = conversations.conversation(this.channelId, false); + if (conversation) + conversation.set_flag_private(this.properties.channel_flag_conversation_private); + } + } + group.end(); + if (info_update) { + const _client = this.channelTree.client.getClient(); + if (_client.currentChannel() === this) + this.channelTree.client.side_bar.info_frame().update_channel_talk(); + //TODO chat channel! + } + } + updateChannelTypeIcon() { + let tag = this.channelTag().find(".channel-type"); + tag.removeAttr('class'); + tag.addClass("show-channel-normal-only channel-type icon"); + if (this._channel_name_formatted === undefined) + tag.addClass("channel-normal"); + let type; + if (this.properties.channel_flag_password == true && !this._cachedPassword) + type = "yellow"; + else if ((!this.properties.channel_flag_maxclients_unlimited && this.clients().length >= this.properties.channel_maxclients) || + (!this.properties.channel_flag_maxfamilyclients_unlimited && this.properties.channel_maxfamilyclients >= 0 && this.clients(true).length >= this.properties.channel_maxfamilyclients)) + type = "red"; + else + type = "green"; + tag.addClass("client-channel_" + type + (this._flag_subscribed ? "_subscribed" : "")); + } + generate_bbcode() { + return "[url=channel://" + this.channelId + "/" + encodeURIComponent(this.properties.channel_name) + "]" + this.formattedChannelName() + "[/url]"; + } + generate_tag(braces = false) { + return $(htmltags.generate_channel({ + channel_name: this.properties.channel_name, + channel_id: this.channelId, + add_braces: braces + })); + } + channelType() { + if (this.properties.channel_flag_permanent == true) + return ChannelType.PERMANENT; + if (this.properties.channel_flag_semi_permanent == true) + return ChannelType.SEMI_PERMANENT; + return ChannelType.TEMPORARY; + } + joinChannel() { + if (this.properties.channel_flag_password == true && + !this._cachedPassword && + !this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_JOIN_IGNORE_PASSWORD).granted(1)) { + createInputModal(_translations.UH_cWHFT || (_translations.UH_cWHFT = tr("Channel password")), _translations.LeX07FON || (_translations.LeX07FON = tr("Channel password:")), () => true, text => { + if (typeof (text) == typeof (true)) + return; + helpers.hashPassword(text).then(result => { + this._cachedPassword = result; + this.joinChannel(); + this.updateChannelTypeIcon(); + }); + }).open(); + } + else if (this.channelTree.client.getClient().currentChannel() != this) + this.channelTree.client.getServerConnection().command_helper.joinChannel(this, this._cachedPassword).then(() => { + this.channelTree.client.sound.play(Sound.CHANNEL_JOINED); + }).catch(error => { + if (error instanceof CommandResult) { + if (error.id == 781) { //Invalid password + this._cachedPassword = undefined; + this.updateChannelTypeIcon(); + } + } + }); + } + cached_password() { return this._cachedPassword; } + subscribe() { + return __awaiter(this, void 0, void 0, function* () { + if (this.subscribe_mode == ChannelSubscribeMode.SUBSCRIBED) + return; + this.subscribe_mode = ChannelSubscribeMode.SUBSCRIBED; + const connection = this.channelTree.client.getServerConnection(); + if (!this.flag_subscribed && connection) + yield connection.send_command('channelsubscribe', { + 'cid': this.getChannelId() + }); + else + this.flag_subscribed = false; + }); + } + unsubscribe(inherited_subscription_mode) { + return __awaiter(this, void 0, void 0, function* () { + const connection = this.channelTree.client.getServerConnection(); + let unsubscribe; + if (inherited_subscription_mode) { + this.subscribe_mode = ChannelSubscribeMode.INHERITED; + unsubscribe = this.flag_subscribed && !this.channelTree.client.client_status.channel_subscribe_all; + } + else { + this.subscribe_mode = ChannelSubscribeMode.UNSUBSCRIBED; + unsubscribe = this.flag_subscribed; + } + if (unsubscribe) { + if (connection) + yield connection.send_command('channelunsubscribe', { + 'cid': this.getChannelId() + }); + else + this.flag_subscribed = false; + for (const client of this.clients(false)) + this.channelTree.deleteClient(client, false); + } + }); + } + get flag_subscribed() { + return this._flag_subscribed; + } + set flag_subscribed(flag) { + if (this._flag_subscribed == flag) + return; + this._flag_subscribed = flag; + this.updateChannelTypeIcon(); + } + get subscribe_mode() { + return typeof (this._subscribe_mode) !== 'undefined' ? this._subscribe_mode : (this._subscribe_mode = this.channelTree.client.settings.server(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), ChannelSubscribeMode.INHERITED)); + } + set subscribe_mode(mode) { + if (this.subscribe_mode == mode) + return; + this._subscribe_mode = mode; + this.channelTree.client.settings.changeServer(Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE(this.channelId), mode); + } + set flag_text_unread(flag) { + this._tag_channel.find(".marker-text-unread").toggleClass("hidden", !flag); + } + log_data() { + return { + channel_name: this.channelName(), + channel_id: this.channelId + }; + } +} +ChannelEntry.NAME_ALIGNMENTS = ["align-left", "align-center", "align-right", "align-repetitive"]; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3a726f47d44c83f2e45a85cc387e80ebef9b1d8512dfcad3ffb69d77002c410e"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3a726f47d44c83f2e45a85cc387e80ebef9b1d8512dfcad3ffb69d77002c410e"] = "3a726f47d44c83f2e45a85cc387e80ebef9b1d8512dfcad3ffb69d77002c410e"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "jlXiTChR", path: "D:/TeaSpeak/web/shared/js/PPTListener.ts (161,31)" }, { name: "nn_CMtx4", path: "D:/TeaSpeak/web/shared/js/PPTListener.ts (163,31)" }, { name: "ivWvObQX", path: "D:/TeaSpeak/web/shared/js/PPTListener.ts (165,31)" }, { name: "NjOPG_IO", path: "D:/TeaSpeak/web/shared/js/PPTListener.ts (167,31)" }, { name: "UUiQqGoU", path: "D:/TeaSpeak/web/shared/js/PPTListener.ts (170,20)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var KeyCode; +(function (KeyCode) { + KeyCode[KeyCode["KEY_CANCEL"] = 3] = "KEY_CANCEL"; + KeyCode[KeyCode["KEY_HELP"] = 6] = "KEY_HELP"; + KeyCode[KeyCode["KEY_BACK_SPACE"] = 8] = "KEY_BACK_SPACE"; + KeyCode[KeyCode["KEY_TAB"] = 9] = "KEY_TAB"; + KeyCode[KeyCode["KEY_CLEAR"] = 12] = "KEY_CLEAR"; + KeyCode[KeyCode["KEY_RETURN"] = 13] = "KEY_RETURN"; + KeyCode[KeyCode["KEY_ENTER"] = 14] = "KEY_ENTER"; + KeyCode[KeyCode["KEY_SHIFT"] = 16] = "KEY_SHIFT"; + KeyCode[KeyCode["KEY_CONTROL"] = 17] = "KEY_CONTROL"; + KeyCode[KeyCode["KEY_ALT"] = 18] = "KEY_ALT"; + KeyCode[KeyCode["KEY_PAUSE"] = 19] = "KEY_PAUSE"; + KeyCode[KeyCode["KEY_CAPS_LOCK"] = 20] = "KEY_CAPS_LOCK"; + KeyCode[KeyCode["KEY_ESCAPE"] = 27] = "KEY_ESCAPE"; + KeyCode[KeyCode["KEY_SPACE"] = 32] = "KEY_SPACE"; + KeyCode[KeyCode["KEY_PAGE_UP"] = 33] = "KEY_PAGE_UP"; + KeyCode[KeyCode["KEY_PAGE_DOWN"] = 34] = "KEY_PAGE_DOWN"; + KeyCode[KeyCode["KEY_END"] = 35] = "KEY_END"; + KeyCode[KeyCode["KEY_HOME"] = 36] = "KEY_HOME"; + KeyCode[KeyCode["KEY_LEFT"] = 37] = "KEY_LEFT"; + KeyCode[KeyCode["KEY_UP"] = 38] = "KEY_UP"; + KeyCode[KeyCode["KEY_RIGHT"] = 39] = "KEY_RIGHT"; + KeyCode[KeyCode["KEY_DOWN"] = 40] = "KEY_DOWN"; + KeyCode[KeyCode["KEY_PRINTSCREEN"] = 44] = "KEY_PRINTSCREEN"; + KeyCode[KeyCode["KEY_INSERT"] = 45] = "KEY_INSERT"; + KeyCode[KeyCode["KEY_DELETE"] = 46] = "KEY_DELETE"; + KeyCode[KeyCode["KEY_0"] = 48] = "KEY_0"; + KeyCode[KeyCode["KEY_1"] = 49] = "KEY_1"; + KeyCode[KeyCode["KEY_2"] = 50] = "KEY_2"; + KeyCode[KeyCode["KEY_3"] = 51] = "KEY_3"; + KeyCode[KeyCode["KEY_4"] = 52] = "KEY_4"; + KeyCode[KeyCode["KEY_5"] = 53] = "KEY_5"; + KeyCode[KeyCode["KEY_6"] = 54] = "KEY_6"; + KeyCode[KeyCode["KEY_7"] = 55] = "KEY_7"; + KeyCode[KeyCode["KEY_8"] = 56] = "KEY_8"; + KeyCode[KeyCode["KEY_9"] = 57] = "KEY_9"; + KeyCode[KeyCode["KEY_SEMICOLON"] = 59] = "KEY_SEMICOLON"; + KeyCode[KeyCode["KEY_EQUALS"] = 61] = "KEY_EQUALS"; + KeyCode[KeyCode["KEY_A"] = 65] = "KEY_A"; + KeyCode[KeyCode["KEY_B"] = 66] = "KEY_B"; + KeyCode[KeyCode["KEY_C"] = 67] = "KEY_C"; + KeyCode[KeyCode["KEY_D"] = 68] = "KEY_D"; + KeyCode[KeyCode["KEY_E"] = 69] = "KEY_E"; + KeyCode[KeyCode["KEY_F"] = 70] = "KEY_F"; + KeyCode[KeyCode["KEY_G"] = 71] = "KEY_G"; + KeyCode[KeyCode["KEY_H"] = 72] = "KEY_H"; + KeyCode[KeyCode["KEY_I"] = 73] = "KEY_I"; + KeyCode[KeyCode["KEY_J"] = 74] = "KEY_J"; + KeyCode[KeyCode["KEY_K"] = 75] = "KEY_K"; + KeyCode[KeyCode["KEY_L"] = 76] = "KEY_L"; + KeyCode[KeyCode["KEY_M"] = 77] = "KEY_M"; + KeyCode[KeyCode["KEY_N"] = 78] = "KEY_N"; + KeyCode[KeyCode["KEY_O"] = 79] = "KEY_O"; + KeyCode[KeyCode["KEY_P"] = 80] = "KEY_P"; + KeyCode[KeyCode["KEY_Q"] = 81] = "KEY_Q"; + KeyCode[KeyCode["KEY_R"] = 82] = "KEY_R"; + KeyCode[KeyCode["KEY_S"] = 83] = "KEY_S"; + KeyCode[KeyCode["KEY_T"] = 84] = "KEY_T"; + KeyCode[KeyCode["KEY_U"] = 85] = "KEY_U"; + KeyCode[KeyCode["KEY_V"] = 86] = "KEY_V"; + KeyCode[KeyCode["KEY_W"] = 87] = "KEY_W"; + KeyCode[KeyCode["KEY_X"] = 88] = "KEY_X"; + KeyCode[KeyCode["KEY_Y"] = 89] = "KEY_Y"; + KeyCode[KeyCode["KEY_Z"] = 90] = "KEY_Z"; + KeyCode[KeyCode["KEY_LEFT_CMD"] = 91] = "KEY_LEFT_CMD"; + KeyCode[KeyCode["KEY_RIGHT_CMD"] = 93] = "KEY_RIGHT_CMD"; + KeyCode[KeyCode["KEY_CONTEXT_MENU"] = 93] = "KEY_CONTEXT_MENU"; + KeyCode[KeyCode["KEY_NUMPAD0"] = 96] = "KEY_NUMPAD0"; + KeyCode[KeyCode["KEY_NUMPAD1"] = 97] = "KEY_NUMPAD1"; + KeyCode[KeyCode["KEY_NUMPAD2"] = 98] = "KEY_NUMPAD2"; + KeyCode[KeyCode["KEY_NUMPAD3"] = 99] = "KEY_NUMPAD3"; + KeyCode[KeyCode["KEY_NUMPAD4"] = 100] = "KEY_NUMPAD4"; + KeyCode[KeyCode["KEY_NUMPAD5"] = 101] = "KEY_NUMPAD5"; + KeyCode[KeyCode["KEY_NUMPAD6"] = 102] = "KEY_NUMPAD6"; + KeyCode[KeyCode["KEY_NUMPAD7"] = 103] = "KEY_NUMPAD7"; + KeyCode[KeyCode["KEY_NUMPAD8"] = 104] = "KEY_NUMPAD8"; + KeyCode[KeyCode["KEY_NUMPAD9"] = 105] = "KEY_NUMPAD9"; + KeyCode[KeyCode["KEY_MULTIPLY"] = 106] = "KEY_MULTIPLY"; + KeyCode[KeyCode["KEY_ADD"] = 107] = "KEY_ADD"; + KeyCode[KeyCode["KEY_SEPARATOR"] = 108] = "KEY_SEPARATOR"; + KeyCode[KeyCode["KEY_SUBTRACT"] = 109] = "KEY_SUBTRACT"; + KeyCode[KeyCode["KEY_DECIMAL"] = 110] = "KEY_DECIMAL"; + KeyCode[KeyCode["KEY_DIVIDE"] = 111] = "KEY_DIVIDE"; + KeyCode[KeyCode["KEY_F1"] = 112] = "KEY_F1"; + KeyCode[KeyCode["KEY_F2"] = 113] = "KEY_F2"; + KeyCode[KeyCode["KEY_F3"] = 114] = "KEY_F3"; + KeyCode[KeyCode["KEY_F4"] = 115] = "KEY_F4"; + KeyCode[KeyCode["KEY_F5"] = 116] = "KEY_F5"; + KeyCode[KeyCode["KEY_F6"] = 117] = "KEY_F6"; + KeyCode[KeyCode["KEY_F7"] = 118] = "KEY_F7"; + KeyCode[KeyCode["KEY_F8"] = 119] = "KEY_F8"; + KeyCode[KeyCode["KEY_F9"] = 120] = "KEY_F9"; + KeyCode[KeyCode["KEY_F10"] = 121] = "KEY_F10"; + KeyCode[KeyCode["KEY_F11"] = 122] = "KEY_F11"; + KeyCode[KeyCode["KEY_F12"] = 123] = "KEY_F12"; + KeyCode[KeyCode["KEY_F13"] = 124] = "KEY_F13"; + KeyCode[KeyCode["KEY_F14"] = 125] = "KEY_F14"; + KeyCode[KeyCode["KEY_F15"] = 126] = "KEY_F15"; + KeyCode[KeyCode["KEY_F16"] = 127] = "KEY_F16"; + KeyCode[KeyCode["KEY_F17"] = 128] = "KEY_F17"; + KeyCode[KeyCode["KEY_F18"] = 129] = "KEY_F18"; + KeyCode[KeyCode["KEY_F19"] = 130] = "KEY_F19"; + KeyCode[KeyCode["KEY_F20"] = 131] = "KEY_F20"; + KeyCode[KeyCode["KEY_F21"] = 132] = "KEY_F21"; + KeyCode[KeyCode["KEY_F22"] = 133] = "KEY_F22"; + KeyCode[KeyCode["KEY_F23"] = 134] = "KEY_F23"; + KeyCode[KeyCode["KEY_F24"] = 135] = "KEY_F24"; + KeyCode[KeyCode["KEY_NUM_LOCK"] = 144] = "KEY_NUM_LOCK"; + KeyCode[KeyCode["KEY_SCROLL_LOCK"] = 145] = "KEY_SCROLL_LOCK"; + KeyCode[KeyCode["KEY_COMMA"] = 188] = "KEY_COMMA"; + KeyCode[KeyCode["KEY_PERIOD"] = 190] = "KEY_PERIOD"; + KeyCode[KeyCode["KEY_SLASH"] = 191] = "KEY_SLASH"; + KeyCode[KeyCode["KEY_BACK_QUOTE"] = 192] = "KEY_BACK_QUOTE"; + KeyCode[KeyCode["KEY_OPEN_BRACKET"] = 219] = "KEY_OPEN_BRACKET"; + KeyCode[KeyCode["KEY_BACK_SLASH"] = 220] = "KEY_BACK_SLASH"; + KeyCode[KeyCode["KEY_CLOSE_BRACKET"] = 221] = "KEY_CLOSE_BRACKET"; + KeyCode[KeyCode["KEY_QUOTE"] = 222] = "KEY_QUOTE"; + KeyCode[KeyCode["KEY_META"] = 224] = "KEY_META"; +})(KeyCode || (KeyCode = {})); +var ppt; +(function (ppt) { + let EventType; + (function (EventType) { + EventType[EventType["KEY_PRESS"] = 0] = "KEY_PRESS"; + EventType[EventType["KEY_RELEASE"] = 1] = "KEY_RELEASE"; + EventType[EventType["KEY_TYPED"] = 2] = "KEY_TYPED"; + })(EventType = ppt.EventType || (ppt.EventType = {})); + let SpecialKey; + (function (SpecialKey) { + SpecialKey[SpecialKey["CTRL"] = 0] = "CTRL"; + SpecialKey[SpecialKey["WINDOWS"] = 1] = "WINDOWS"; + SpecialKey[SpecialKey["SHIFT"] = 2] = "SHIFT"; + SpecialKey[SpecialKey["ALT"] = 3] = "ALT"; + })(SpecialKey = ppt.SpecialKey || (ppt.SpecialKey = {})); + function key_description(key) { + let result = ""; + if (key.key_shift) + result += " + " + (_translations.jlXiTChR || (_translations.jlXiTChR = tr("Shift"))); + if (key.key_alt) + result += " + " + (_translations.nn_CMtx4 || (_translations.nn_CMtx4 = tr("Alt"))); + if (key.key_ctrl) + result += " + " + (_translations.ivWvObQX || (_translations.ivWvObQX = tr("CTRL"))); + if (key.key_windows) + result += " + " + (_translations.NjOPG_IO || (_translations.NjOPG_IO = tr("Win"))); + if (!result && !key.key_code) + return _translations.UUiQqGoU || (_translations.UUiQqGoU = tr("unset")); + if (key.key_code) + result += " + " + key.key_code; + return result.substr(3); + } + ppt.key_description = key_description; +})(ppt || (ppt = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e85c12b4ba32fa88e1cf62cff6c27ebb393473443cb8a69f01892fe496e32840"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e85c12b4ba32fa88e1cf62cff6c27ebb393473443cb8a69f01892fe496e32840"] = "e85c12b4ba32fa88e1cf62cff6c27ebb393473443cb8a69f01892fe496e32840"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var ElementType; +(function (ElementType) { + ElementType[ElementType["HEADER"] = 0] = "HEADER"; + ElementType[ElementType["BODY"] = 1] = "BODY"; + ElementType[ElementType["FOOTER"] = 2] = "FOOTER"; +})(ElementType || (ElementType = {})); +const ModalFunctions = { + divify: function (val) { + if (val.length > 1) + return $.spawn("div").append(val); + return val; + }, + jqueriefy: function (val, type) { + if (typeof (val) === "function") + val = val(); + if (val instanceof jQuery) + return val; + if (Array.isArray(val)) { + if (val.length == 0) + return undefined; + return val.map(e => this.jqueriefy(e)); + } + switch (typeof val) { + case "string": + if (type == ElementType.HEADER) + return $.spawn("div").addClass("modal-title").text(val); + return $("
" + val + "
"); + case "object": return val; + case "undefined": + return undefined; + default: + console.error(("Invalid type %o"), typeof val); + return $(); + } + }, + warpProperties(data) { + if (data instanceof ModalProperties) { + return data; + } + else { + const props = new ModalProperties(); + for (const key of Object.keys(data)) + props[key] = data[key]; + return props; + } + } +}; +class ModalProperties { + constructor() { + this.header = () => "HEADER"; + this.body = () => "BODY"; + this.footer = () => "FOOTER"; + this.closeListener = () => { }; + this.height = "auto"; + this.closeable = true; + this.template_properties = {}; + this.trigger_tab = true; + this.full_size = false; + } + registerCloseListener(listener) { + if (this.closeListener) { + if ($.isArray(this.closeListener)) + this.closeListener.push(listener); + else + this.closeListener = [this.closeListener, listener]; + } + else + this.closeListener = listener; + return this; + } + triggerClose() { + if ($.isArray(this.closeListener)) + for (let listener of this.closeListener) + listener(); + else + this.closeListener(); + } +} +var modal; +(function (modal) { + function initialize_modals() { + register_global_events(); + } + modal.initialize_modals = initialize_modals; + const scrollSize = 18; + function scroll_bar_clicked(event) { + const x = event.pageX, y = event.pageY, e = $(event.target); + if (e.hasScrollBar("height")) { + const top = e.offset().top; + const right = e.offset().left + e.width(); + const bottom = top + e.height(); + const left = right - scrollSize; + if ((y >= top && y <= bottom) && (x >= left && x <= right)) + return true; + } + if (e.hasScrollBar("width")) { + const bottom = e.offset().top + e.height(); + const top = bottom - scrollSize; + const left = e.offset().left; + const right = left + e.width(); + if ((y >= top && y <= bottom) && (x >= left && x <= right)) + return true; + } + return false; + } + function register_global_events() { + $(document).on('mousedown', (event) => { + /* pageX or pageY are undefined if this is an event executed via .trigger('click'); */ + if (_global_modal_count == 0 || typeof (event.pageX) === "undefined" || typeof (event.pageY) === "undefined") + return; + let element = event.target; + const original = element; + do { + if (element.classList.contains('modal-content')) + break; + if (!element.classList.contains('modal')) + continue; + if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) + break; + if (element === original && scroll_bar_clicked(event)) { + _global_modal_last_time = Date.now(); + break; + } + $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); + break; + } while ((element = element.parentElement)); + }); + $(document).on('keyup', (event) => { + if (_global_modal_count == 0 || typeof (event.target) === "undefined") + return; + if (event.key !== "Escape") + return; + let element = event.target; + if (element.nodeName == "HTMLInputElement" || element.nodeName == "HTMLSelectElement" || element.nodeName == "HTMLTextAreaElement") + return; + do { + if (element.classList.contains('modal-content')) + break; + if (!element.classList.contains('modal')) + continue; + if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) + break; + $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); + break; + } while ((element = element.parentElement)); + }); + } +})(modal || (modal = {})); +modal.initialize_modals(); +let _global_modal_count = 0; +let _global_modal_last; +let _global_modal_last_time; +class Modal { + constructor(props) { + this.open_listener = []; + this.close_listener = []; + this.properties = props; + this.shown = false; + } + get htmlTag() { + if (!this._htmlTag) + this._create(); + return this._htmlTag; + } + _create() { + const header = ModalFunctions.jqueriefy(this.properties.header, ElementType.HEADER); + const body = ModalFunctions.jqueriefy(this.properties.body, ElementType.BODY); + const footer = ModalFunctions.jqueriefy(this.properties.footer, ElementType.FOOTER); + //FIXME: cache template + const template = $(this.properties.template || "#tmpl_modal"); + const properties = { + modal_header: header, + modal_body: body, + modal_footer: footer, + closeable: this.properties.closeable, + full_size: this.properties.full_size + }; + if (this.properties.template_properties) + Object.assign(properties, this.properties.template_properties); + const tag = template.renderTag(properties); + if (typeof (this.properties.width) !== "undefined" && typeof (this.properties.min_width) !== "undefined") + tag.find(".modal-content") + .css("min-width", this.properties.min_width) + .css("width", this.properties.width); + else if (typeof (this.properties.width) !== "undefined") //Legacy support + tag.find(".modal-content").css("min-width", this.properties.width); + else if (typeof (this.properties.min_width) !== "undefined") + tag.find(".modal-content").css("min-width", this.properties.min_width); + this.close_elements = tag.find(".button-modal-close"); + this.close_elements.toggle(this.properties.closeable).on('click', event => { + if (this.properties.closeable) + this.close(); + }); + this._htmlTag = tag; + this._htmlTag.find("input").on('change', event => { + $(event.target).parents(".form-group").toggleClass('is-filled', !!event.target.value); + }); + //TODO: After the animation! + this._htmlTag.on('hide.bs.modal', event => !this.properties.closeable || this.close()); + this._htmlTag.on('hidden.bs.modal', event => this._htmlTag.remove()); + } + open() { + if (this.shown) + return; + _global_modal_last_time = Date.now(); + _global_modal_last = this.htmlTag[0]; + this.shown = true; + this.htmlTag.appendTo($("body")); + _global_modal_count++; + this.htmlTag.show(); + setTimeout(() => this.htmlTag.addClass('shown'), 0); + setTimeout(() => { + for (const listener of this.open_listener) + listener(); + this.htmlTag.find(".tab").trigger('tab.resize'); + }, 300); + } + close() { + if (!this.shown) + return; + _global_modal_count--; + if (_global_modal_last === this.htmlTag[0]) + _global_modal_last = undefined; + this.shown = false; + this.htmlTag.removeClass('shown'); + setTimeout(() => { + this.htmlTag.remove(); + this._htmlTag = undefined; + }, 300); + this.properties.triggerClose(); + for (const listener of this.close_listener) + listener(); + } + set_closeable(flag) { + if (flag === this.properties.closeable) + return; + this.properties.closeable = flag; + this.close_elements.toggle(flag); + } +} +function createModal(data) { + return new Modal(ModalFunctions.warpProperties(data)); +} +class InputModalProperties extends ModalProperties { +} +function createInputModal(headMessage, question, validator, callback, props = {}) { + props = ModalFunctions.warpProperties(props); + props.template_properties || (props.template_properties = {}); + props.template_properties.field_title = props.field_title; + props.template_properties.field_label = props.field_label; + props.template_properties.field_placeholder = props.field_placeholder; + props.template_properties.error_message = props.error_message; + props.template = "#tmpl_modal_input"; + props.header = headMessage; + props.template_properties.question = ModalFunctions.jqueriefy(question); + const modal = createModal(props); + const input = modal.htmlTag.find(".container-value input"); + const button_cancel = modal.htmlTag.find(".button-cancel"); + const button_submit = modal.htmlTag.find(".button-submit"); + let submited = false; + input.on('keyup change', event => { + const str = input.val(); + const valid = str !== undefined && validator(str); + input.attr("pattern", valid ? null : "^[a]{1000}$").toggleClass("is-invalid", !valid); + button_submit.prop("disabled", !valid); + }); + input.on('keydown', event => { + if (event.keyCode !== KeyCode.KEY_RETURN || event.shiftKey) + return; + if (button_submit.prop("disabled")) + return; + button_submit.trigger('click'); + }); + button_submit.on('click', event => { + if (!submited) { + submited = true; + const str = input.val(); + if (str !== undefined && validator(str)) + callback(str); + else + callback(false); + } + modal.close(); + }).prop("disabled", !validator("")); /* disabled if empty input isn't allowed */ + button_cancel.on('click', event => { + if (!submited) { + submited = true; + callback(false); + } + modal.close(); + }); + modal.open_listener.push(() => input.focus()); + modal.close_listener.push(() => button_cancel.trigger('click')); + return modal; +} +function createErrorModal(header, message, props = { footer: undefined }) { + props = ModalFunctions.warpProperties(props); + (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; + props.header = header; + props.body = message; + const modal = createModal(props); + modal.htmlTag.find(".modal-body").addClass("modal-error"); + return modal; +} +function createInfoModal(header, message, props = { footer: undefined }) { + props = ModalFunctions.warpProperties(props); + (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; + props.header = header; + props.body = message; + const modal = createModal(props); + modal.htmlTag.find(".modal-body").addClass("modal-info"); + return modal; +} +$.fn.modalize = function (entry_callback, properties) { + properties = properties || {}; + entry_callback = entry_callback || ((a, b, c) => undefined); + let tag_modal = this[0].tagName.toLowerCase() == "modal" ? this : undefined; /* TODO may throw exception? */ + let tag_head = tag_modal ? tag_modal.find("modal-header") : ModalFunctions.jqueriefy(properties.header); + let tag_body = tag_modal ? tag_modal.find("modal-body") : this; + let tag_footer = tag_modal ? tag_modal.find("modal-footer") : ModalFunctions.jqueriefy(properties.footer); + const result = entry_callback(tag_head, tag_body, tag_footer) || {}; + properties.header = result.header || tag_head; + properties.body = result.body || tag_body; + properties.footer = result.footer || tag_footer; + return createModal(properties); +}; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["daf6447bfb4db78245da21e8a415df1fbffd01dea2eead594ce099db160614b4"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["daf6447bfb4db78245da21e8a415df1fbffd01dea2eead594ce099db160614b4"] = "daf6447bfb4db78245da21e8a415df1fbffd01dea2eead594ce099db160614b4"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "WyjTP88B", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChangeVolume.ts (14,29)" }, { name: "Ajoev9cN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChangeVolume.ts (14,57)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + //TODO: Use the max limit! + let modal; + function spawnChangeVolume(client, local, current, max, callback) { + if (modal) + modal.close(); + let new_value; + modal = createModal({ + header: local ? _translations.WyjTP88B || (_translations.WyjTP88B = tr("Change local volume")) : _translations.Ajoev9cN || (_translations.Ajoev9cN = tr("Change remote volume")), + body: function () { + let tag = $("#tmpl_change_volume").renderTag({ + client: htmltags.generate_client_object({ + add_braces: false, + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier, + client_id: client.clientId() + }), + local: local + }); + const container_value = tag.find(".info .value"); + const set_value = value => { + const number = value > 100 ? value - 100 : 100 - value; + container_value.html((value == 100 ? "±" : value > 100 ? "+" : "-") + number + "%"); + new_value = value / 100; + if (local) + callback(new_value); + }; + set_value(current * 100); + const slider_tag = tag.find(".container-slider"); + const slider = sliderfy(slider_tag, { + initial_value: current * 100, + step: 1, + max_value: 200, + min_value: 0, + unit: '%' + }); + slider_tag.on('change', event => set_value(parseInt(slider_tag.attr("value")))); + tag.find(".button-save").on('click', event => { + if (typeof (new_value) !== "undefined") + callback(new_value); + modal.close(); + }); + tag.find(".button-cancel").on('click', event => { + callback(current); + modal.close(); + }); + tag.find(".button-reset").on('click', event => { + slider.value(100); + }); + tag.find(".button-apply").on('click', event => { + callback(new_value); + new_value = undefined; + }); + return tag.children(); + }, + footer: null, + width: 600 + }); + modal.close_listener.push(() => modal = undefined); + modal.open(); + modal.htmlTag.find(".modal-body").addClass("modal-volume"); + } + Modals.spawnChangeVolume = spawnChangeVolume; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["8826f72b6f1d2cf9a2064e176a47bf3754cd63eaf3c1a19fc6ed100e765e2fe8"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["8826f72b6f1d2cf9a2064e176a47bf3754cd63eaf3c1a19fc6ed100e765e2fe8"] = "8826f72b6f1d2cf9a2064e176a47bf3754cd63eaf3c1a19fc6ed100e765e2fe8"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "KEIZ2p_a", path: "D:/TeaSpeak/web/shared/js/ui/client_move.ts (53,40)" }, { name: "crLZ2vSF", path: "D:/TeaSpeak/web/shared/js/ui/client_move.ts (115,40)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +class ClientMover { + constructor(tree) { + this.enabled = true; + this._active = false; + this.origin_point = undefined; + this.channel_tree = tree; + } + is_active() { return this._active; } + hover_text() { + if ($.isArray(this.selected_client)) { + return this.selected_client.filter(client => !!client).map(client => client.clientNickName()).join(", "); + } + else if (this.selected_client) { + return this.selected_client.clientNickName(); + } + else + return ""; + } + bbcode_text() { + if ($.isArray(this.selected_client)) { + return this.selected_client.filter(client => !!client).map(client => client.create_bbcode()).join(", "); + } + else if (this.selected_client) { + return this.selected_client.create_bbcode(); + } + else + return ""; + } + activate(client, callback, event) { + this.finish_listener(undefined); + if (!this.enabled) + return false; + this.selected_client = client; + this.callback = callback; + log.debug(LogCategory.GENERAL, _translations.KEIZ2p_a || (_translations.KEIZ2p_a = tr("Starting mouse move"))); + ClientMover.listener_root.on('mouseup', this._bound_finish = this.finish_listener.bind(this)).on('mousemove', this._bound_move = this.move_listener.bind(this)); + { + const content = ClientMover.move_element.find(".container"); + content.empty(); + content.append($.spawn("a").text(this.hover_text())); + } + this.move_listener(event); + } + move_listener(event) { + if (!this.enabled) + return; + //console.log("Mouse move: " + event.pageX + " - " + event.pageY); + if (!event.pageX || !event.pageY) + return; + if (!this.origin_point) + this.origin_point = { x: event.pageX, y: event.pageY }; + ClientMover.move_element.css({ + "top": (event.pageY - 1) + "px", + "left": (event.pageX + 10) + "px" + }); + if (!this._active) { + const d_x = this.origin_point.x - event.pageX; + const d_y = this.origin_point.y - event.pageY; + this._active = Math.sqrt(d_x * d_x + d_y * d_y) > 5 * 5; + if (this._active) { + if ($.isArray(this.selected_client)) { + this.channel_tree.onSelect(this.selected_client[0], true); + for (const client of this.selected_client.slice(1)) + this.channel_tree.onSelect(client, false, true); + } + else { + this.channel_tree.onSelect(this.selected_client, true); + } + ClientMover.move_element.show(); + } + } + const elements = document.elementsFromPoint(event.pageX, event.pageY); + while (elements.length > 0) { + if (elements[0].classList.contains("container-channel")) + break; + elements.pop_front(); + } + if (this.hovered_channel) { + this.hovered_channel.classList.remove("move-selected"); + this.hovered_channel = undefined; + } + if (elements.length > 0) { + elements[0].classList.add("move-selected"); + this.hovered_channel = elements[0]; + } + } + finish_listener(event) { + ClientMover.move_element.hide(); + log.debug(LogCategory.GENERAL, _translations.crLZ2vSF || (_translations.crLZ2vSF = tr("Finishing mouse move"))); + const channel_id = this.hovered_channel ? parseInt(this.hovered_channel.getAttribute("channel-id")) : 0; + ClientMover.listener_root.unbind('mouseleave', this._bound_finish); + ClientMover.listener_root.unbind('mouseup', this._bound_finish); + ClientMover.listener_root.unbind('mousemove', this._bound_move); + if (this.hovered_channel) { + this.hovered_channel.classList.remove("move-selected"); + this.hovered_channel = undefined; + } + this.origin_point = undefined; + if (!this._active) { + this.selected_client = undefined; + this.callback = undefined; + return; + } + this._active = false; + if (this.callback) { + if (!channel_id) + this.callback(undefined); + else { + this.callback(this.channel_tree.findChannel(channel_id)); + } + this.callback = undefined; + } + /* test for the chat box */ + { + const elements = document.elementsFromPoint(event.pageX, event.pageY); + console.error(elements); + while (elements.length > 0) { + if (elements[0].classList.contains("client-chat-box-field")) + break; + elements.pop_front(); + } + if (elements.length > 0) { + const element = $(elements[0]); + element.val((element.val() || "") + this.bbcode_text()); + } + } + } + deactivate() { + this.callback = undefined; + this.finish_listener(undefined); + } +} +ClientMover.listener_root = $(document); +ClientMover.move_element = $("#mouse-move"); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["4246282d77c925162d2c06841ca915232cba6ac6d2a1d1085c05fb0ff54920bb"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["4246282d77c925162d2c06841ca915232cba6ac6d2a1d1085c05fb0ff54920bb"] = "4246282d77c925162d2c06841ca915232cba6ac6d2a1d1085c05fb0ff54920bb"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "imtMC86F", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (154,41)" }, { name: "aVeKeOqo", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (158,45)" }, { name: "SkYDkDbr", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (174,45)" }, { name: "K9iUYVHs", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (323,87)" }, { name: "MKsTvtiO", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (323,109)" }, { name: "sKUcAWYo", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (394,19)" }, { name: "NkRVbrEL", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (408,19)" }, { name: "DQmH806h", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (415,19)" }, { name: "FBafbYH1", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (420,27)" }, { name: "QONLfGGw", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (426,27)" }, { name: "XQqk0hQm", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (482,25)" }, { name: "Wamj6lZp", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (492,23)" }, { name: "NAP7iNGX", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (499,23)" }, { name: "wle1DPL6", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (501,38)" }, { name: "IhKp1U6b", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (501,57)" }, { name: "wfJ0cnuF", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (516,23)" }, { name: "SkDvmbF4", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (518,38)" }, { name: "UUmz0RUR", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (518,71)" }, { name: "IqCkK5LB", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (536,23)" }, { name: "mgE4Z1Js", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (546,23)" }, { name: "Gj78RvOz", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (548,38)" }, { name: "xhyckLyH", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (548,70)" }, { name: "ZCjZNTae", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (564,23)" }, { name: "PXTiLlh1", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (566,38)" }, { name: "U9VbSk_j", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (566,69)" }, { name: "t1_5DwD9", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (582,23)" }, { name: "_SOYYOpq", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (623,23)" }, { name: "LGRXJbyk", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (636,23)" }, { name: "HXDWIypp", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (649,23)" }, { name: "qPADwJeS", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (655,23)" }, { name: "mwXcnRVj", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (820,70)" }, { name: "BwKYcCsM", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (891,47)" }, { name: "TWJRawAz", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1143,25)" }, { name: "QXkvf75y", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1149,23)" }, { name: "eo0BTEUD", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1152,38)" }, { name: "cGSxzOs8", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1152,68)" }, { name: "KltoFGs6", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1154,41)" }, { name: "XbLxNlO8", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1319,23)" }, { name: "pmicCstj", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1324,38)" }, { name: "vqukZhk9", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1324,72)" }, { name: "KwxFZl6L", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1336,23)" }, { name: "mYzzfhcQ", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1340,38)" }, { name: "ZTCZj4qo", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1340,75)" }, { name: "JLASYUDm", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1362,23)" }, { name: "ecR9BnOb", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1373,42)" }, { name: "bjVFcLvv", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1373,69)" }, { name: "cCQHXsEN", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1375,42)" }, { name: "z0TAFloZ", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1375,75)" }, { name: "v3a_2VKL", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1381,23)" }, { name: "qP0cnomf", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1385,38)" }, { name: "yQiADIEs", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1385,66)" }, { name: "LAv5uiVz", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1396,50)" }, { name: "y7NwSbFu", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1408,23)" }, { name: "l4FBtA8t", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1418,23)" }, { name: "I7AyThLI", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1420,38)" }, { name: "Xd1D_Zzu", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1420,70)" }, { name: "mWrcl55N", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1422,41)" }, { name: "GjpMhrnj", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1436,23)" }, { name: "xbWCjjmW", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1447,23)" }, { name: "eRh0JpMP", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1468,23)" }, { name: "J7K_FqfX", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1481,23)" }, { name: "RjNsfIH5", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1485,83)" }, { name: "UryMTw7o", path: "D:/TeaSpeak/web/shared/js/ui/client.ts (1486,39)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var ClientType; +(function (ClientType) { + ClientType[ClientType["CLIENT_VOICE"] = 0] = "CLIENT_VOICE"; + ClientType[ClientType["CLIENT_QUERY"] = 1] = "CLIENT_QUERY"; + ClientType[ClientType["CLIENT_INTERNAL"] = 2] = "CLIENT_INTERNAL"; + ClientType[ClientType["CLIENT_WEB"] = 3] = "CLIENT_WEB"; + ClientType[ClientType["CLIENT_MUSIC"] = 4] = "CLIENT_MUSIC"; + ClientType[ClientType["CLIENT_UNDEFINED"] = 5] = "CLIENT_UNDEFINED"; +})(ClientType || (ClientType = {})); +class ClientProperties { + constructor() { + this.client_type = ClientType.CLIENT_VOICE; //TeamSpeaks type + this.client_type_exact = ClientType.CLIENT_VOICE; + this.client_database_id = 0; + this.client_version = ""; + this.client_platform = ""; + this.client_nickname = "unknown"; + this.client_unique_identifier = "unknown"; + this.client_description = ""; + this.client_servergroups = ""; + this.client_channel_group_id = 0; + this.client_lastconnected = 0; + this.client_created = 0; + this.client_totalconnections = 0; + this.client_flag_avatar = ""; + this.client_icon_id = 0; + this.client_away_message = ""; + this.client_away = false; + this.client_country = ""; + this.client_input_hardware = false; + this.client_output_hardware = false; + this.client_input_muted = false; + this.client_output_muted = false; + this.client_is_channel_commander = false; + this.client_teaforo_id = 0; + this.client_teaforo_name = ""; + this.client_teaforo_flags = 0; /* 0x01 := Banned | 0x02 := Stuff | 0x04 := Premium */ + /* not updated in view! */ + this.client_month_bytes_uploaded = 0; + this.client_month_bytes_downloaded = 0; + this.client_total_bytes_uploaded = 0; + this.client_total_bytes_downloaded = 0; + this.client_talk_power = 0; + this.client_is_priority_speaker = false; + } +} +class ClientConnectionInfo { + constructor() { + this.connection_bandwidth_received_last_minute_control = -1; + this.connection_bandwidth_received_last_minute_keepalive = -1; + this.connection_bandwidth_received_last_minute_speech = -1; + this.connection_bandwidth_received_last_second_control = -1; + this.connection_bandwidth_received_last_second_keepalive = -1; + this.connection_bandwidth_received_last_second_speech = -1; + this.connection_bandwidth_sent_last_minute_control = -1; + this.connection_bandwidth_sent_last_minute_keepalive = -1; + this.connection_bandwidth_sent_last_minute_speech = -1; + this.connection_bandwidth_sent_last_second_control = -1; + this.connection_bandwidth_sent_last_second_keepalive = -1; + this.connection_bandwidth_sent_last_second_speech = -1; + this.connection_bytes_received_control = -1; + this.connection_bytes_received_keepalive = -1; + this.connection_bytes_received_speech = -1; + this.connection_bytes_sent_control = -1; + this.connection_bytes_sent_keepalive = -1; + this.connection_bytes_sent_speech = -1; + this.connection_packets_received_control = -1; + this.connection_packets_received_keepalive = -1; + this.connection_packets_received_speech = -1; + this.connection_packets_sent_control = -1; + this.connection_packets_sent_keepalive = -1; + this.connection_packets_sent_speech = -1; + this.connection_ping = -1; + this.connection_ping_deviation = -1; + this.connection_server2client_packetloss_control = -1; + this.connection_server2client_packetloss_keepalive = -1; + this.connection_server2client_packetloss_speech = -1; + this.connection_server2client_packetloss_total = -1; + this.connection_client2server_packetloss_speech = -1; + this.connection_client2server_packetloss_keepalive = -1; + this.connection_client2server_packetloss_control = -1; + this.connection_client2server_packetloss_total = -1; + this.connection_filetransfer_bandwidth_sent = -1; + this.connection_filetransfer_bandwidth_received = -1; + this.connection_connected_time = -1; + this.connection_idle_time = -1; + this.connection_client_port = -1; + } +} +class ClientEntry { + constructor(clientId, clientName, properties = new ClientProperties()) { + this.lastVariableUpdate = 0; + this.events = new events.Registry(); + this._properties = properties; + this._properties.client_nickname = clientName; + this._clientId = clientId; + this.channelTree = null; + this._channel = null; + } + destroy() { + if (this._tag) { + this._tag.remove(); + this._tag = undefined; + } + if (this._audio_handle) { + log.warn(LogCategory.AUDIO, _translations.imtMC86F || (_translations.imtMC86F = tr("Destroying client with an active audio handle. This could cause memory leaks!"))); + try { + this._audio_handle.abort_replay(); + } + catch (error) { + log.warn(LogCategory.AUDIO, _translations.aVeKeOqo || (_translations.aVeKeOqo = tr("Failed to abort replay: %o")), error); + } + this._audio_handle.callback_playback = undefined; + this._audio_handle.callback_stopped = undefined; + this._audio_handle = undefined; + } + this._channel = undefined; + } + tree_unregistered() { + this.channelTree = undefined; + if (this._audio_handle) { + try { + this._audio_handle.abort_replay(); + } + catch (error) { + log.warn(LogCategory.AUDIO, _translations.SkYDkDbr || (_translations.SkYDkDbr = tr("Failed to abort replay: %o")), error); + } + this._audio_handle.callback_playback = undefined; + this._audio_handle.callback_stopped = undefined; + this._audio_handle = undefined; + } + this._channel = undefined; + } + set_audio_handle(handle) { + if (this._audio_handle === handle) + return; + if (this._audio_handle) { + this._audio_handle.callback_playback = undefined; + this._audio_handle.callback_stopped = undefined; + } + //TODO may ensure that the id is the same? + this._audio_handle = handle; + if (!handle) { + this.speaking = false; + return; + } + handle.callback_playback = () => this.speaking = true; + handle.callback_stopped = () => this.speaking = false; + } + get_audio_handle() { + return this._audio_handle; + } + get properties() { + return this._properties; + } + currentChannel() { return this._channel; } + clientNickName() { return this.properties.client_nickname; } + clientUid() { return this.properties.client_unique_identifier; } + clientId() { return this._clientId; } + is_muted() { return !!this._audio_muted; } + set_muted(flag, update_icon, force) { + if (this._audio_muted === flag && !force) + return; + if (flag) { + this.channelTree.client.serverConnection.send_command('clientmute', { + clid: this.clientId() + }); + } + else if (this._audio_muted) { + this.channelTree.client.serverConnection.send_command('clientunmute', { + clid: this.clientId() + }); + } + this._audio_muted = flag; + this.channelTree.client.settings.changeServer("mute_client_" + this.clientUid(), flag); + if (this._audio_handle) { + if (flag) { + this._audio_handle.set_volume(0); + } + else { + this._audio_handle.set_volume(this._audio_volume); + } + } + if (update_icon) + this.updateClientSpeakIcon(); + for (const client of this.channelTree.clients) { + if (client === this || client.properties.client_unique_identifier != this.properties.client_unique_identifier) + continue; + client.set_muted(flag, true); + } + } + initializeListener() { + if (this._listener_initialized) + return; + this._listener_initialized = true; + this.tag.on('mouseup', event => { + if (!this.channelTree.client_mover.is_active()) { + this.channelTree.onSelect(this); + } + }); + if (!(this instanceof LocalClientEntry) && !(this instanceof MusicClientEntry)) + this.tag.dblclick(event => { + if ($.isArray(this.channelTree.currently_selected)) { //Multiselect + return; + } + this.open_text_chat(); + }); + if (!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) { + this.tag.on("contextmenu", (event) => { + event.preventDefault(); + if ($.isArray(this.channelTree.currently_selected)) { //Multiselect + (this.channelTree.currently_selected_context_callback || ((_) => null))(event); + return; + } + this.channelTree.onSelect(this, true); + this.showContextMenu(event.pageX, event.pageY, () => { }); + return false; + }); + } + this.tag.on('mousedown', event => { + if (event.which != 1) + return; //Only the left button + let clients = this.channelTree.currently_selected; + if (ppt.key_pressed(ppt.SpecialKey.SHIFT)) { + if (clients != this && !($.isArray(clients) && clients.indexOf(this) != -1)) + clients = $.isArray(clients) ? [...clients, this] : [clients, this]; + } + else { + clients = this; + } + this.channelTree.client_mover.activate(clients, target => { + if (!target) + return; + for (const client of $.isArray(clients) ? clients : [clients]) { + if (target == client._channel) + continue; + const source = client._channel; + const self = this.channelTree.client.getClient(); + this.channelTree.client.serverConnection.send_command("clientmove", { + clid: client.clientId(), + cid: target.getChannelId() + }).then(event => { + if (client.clientId() == this.channelTree.client.clientId) + this.channelTree.client.sound.play(Sound.CHANNEL_JOINED); + else if (target !== source && target != self.currentChannel()) + this.channelTree.client.sound.play(Sound.USER_MOVED); + }); + } + this.channelTree.onSelect(); + }, event); + }); + } + contextmenu_info() { + return [ + { + type: contextmenu.MenuEntryType.ENTRY, + name: this.properties.client_type_exact === ClientType.CLIENT_MUSIC ? _translations.K9iUYVHs || (_translations.K9iUYVHs = tr("Show bot info")) : _translations.MKsTvtiO || (_translations.MKsTvtiO = tr("Show client info")), + callback: () => { + this.channelTree.client.side_bar.show_client_info(this); + }, + icon_class: "client-about", + visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT) + }, { + callback: () => { }, + type: contextmenu.MenuEntryType.HR, + name: "", + visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT) + } + ]; + } + assignment_context() { + let server_groups = []; + for (let group of this.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { + if (group.type != GroupType.NORMAL) + continue; + let entry = {}; + //TODO: May add the server group icon? + entry.checkbox_checked = this.groupAssigned(group); + entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; + if (this.groupAssigned(group)) { + entry.callback = () => { + this.channelTree.client.serverConnection.send_command("servergroupdelclient", { + sgid: group.id, + cldbid: this.properties.client_database_id + }); + }; + entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); + } + else { + entry.callback = () => { + this.channelTree.client.serverConnection.send_command("servergroupaddclient", { + sgid: group.id, + cldbid: this.properties.client_database_id + }); + }; + entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberAddPower); + } + entry.type = contextmenu.MenuEntryType.CHECKBOX; + server_groups.push(entry); + } + let channel_groups = []; + for (let group of this.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { + if (group.type != GroupType.NORMAL) + continue; + let entry = {}; + //TODO: May add the channel group icon? + entry.checkbox_checked = this.assignedChannelGroup() == group.id; + entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; + entry.callback = () => { + this.channelTree.client.serverConnection.send_command("setclientchannelgroup", { + cldbid: this.properties.client_database_id, + cgid: group.id, + cid: this.currentChannel().channelId + }); + }; + entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); + entry.type = contextmenu.MenuEntryType.CHECKBOX; + channel_groups.push(entry); + } + return [{ + type: contextmenu.MenuEntryType.SUB_MENU, + icon_class: "client-permission_server_groups", + name: _translations.sKUcAWYo || (_translations.sKUcAWYo = tr("Set server group")), + sub_menu: [ + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-permission_server_groups", + name: "Server groups dialog", + callback: () => this.open_assignment_modal() + }, + contextmenu.Entry.HR(), + ...server_groups + ] + }, { + type: contextmenu.MenuEntryType.SUB_MENU, + icon_class: "client-permission_channel", + name: _translations.NkRVbrEL || (_translations.NkRVbrEL = tr("Set channel group")), + sub_menu: [ + ...channel_groups + ] + }, { + type: contextmenu.MenuEntryType.SUB_MENU, + icon_class: "client-permission_client", + name: _translations.DQmH806h || (_translations.DQmH806h = tr("Permissions")), + sub_menu: [ + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-permission_client", + name: _translations.FBafbYH1 || (_translations.FBafbYH1 = tr("Client permissions")), + callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clp", { unique_id: this.clientUid() }).open() + }, + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-permission_client", + name: _translations.QONLfGGw || (_translations.QONLfGGw = tr("Client channel permissions")), + callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clchp", { unique_id: this.clientUid(), channel_id: this._channel ? this._channel.channelId : undefined }).open() + } + ] + }]; + } + open_assignment_modal() { + Modals.createServerGroupAssignmentModal(this, (groups, flag) => { + if (groups.length == 0) + return Promise.resolve(true); + if (groups.length == 1) { + if (flag) { + return this.channelTree.client.serverConnection.send_command("servergroupaddclient", { + sgid: groups[0], + cldbid: this.properties.client_database_id + }).then(result => true); + } + else + return this.channelTree.client.serverConnection.send_command("servergroupdelclient", { + sgid: groups[0], + cldbid: this.properties.client_database_id + }).then(result => true); + } + else { + const data = groups.map(e => { return { sgid: e }; }); + data[0]["cldbid"] = this.properties.client_database_id; + if (flag) { + return this.channelTree.client.serverConnection.send_command("clientaddservergroup", data, { flagset: ["continueonerror"] }).then(result => true); + } + else + return this.channelTree.client.serverConnection.send_command("clientdelservergroup", data, { flagset: ["continueonerror"] }).then(result => true); + } + }); + } + open_text_chat() { + const chat = this.channelTree.client.side_bar; + const conversation = chat.private_conversations().find_conversation({ + name: this.clientNickName(), + client_id: this.clientId(), + unique_id: this.clientUid() + }, { + attach: true, + create: true + }); + chat.private_conversations().set_selected_conversation(conversation); + chat.show_private_conversations(); + chat.private_conversations().try_input_focus(); + } + showContextMenu(x, y, on_close = undefined) { + let trigger_close = true; + contextmenu.spawn_context_menu(x, y, ...this.contextmenu_info(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-change_nickname", + name: (contextmenu.get_provider().html_format_enabled() ? "" : "") + (_translations.XQqk0hQm || (_translations.XQqk0hQm = tr("Open text chat"))) + + (contextmenu.get_provider().html_format_enabled() ? "" : ""), + callback: () => { + this.open_text_chat(); + } + }, contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-about", + name: _translations.Wamj6lZp || (_translations.Wamj6lZp = tr("Show client info")), + callback: () => Modals.openClientInfo(this) + }, contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-poke", + name: _translations.NAP7iNGX || (_translations.NAP7iNGX = tr("Poke client")), + callback: () => { + createInputModal(_translations.wle1DPL6 || (_translations.wle1DPL6 = tr("Poke client")), _translations.IhKp1U6b || (_translations.IhKp1U6b = tr("Poke message:
")), text => true, result => { + if (typeof (result) === "string") { + //TODO tr + console.log("Poking client " + this.clientNickName() + " with message " + result); + this.channelTree.client.serverConnection.send_command("clientpoke", { + clid: this.clientId(), + msg: result + }); + } + }, { width: 400, maxLength: 512 }).open(); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-edit", + name: _translations.wfJ0cnuF || (_translations.wfJ0cnuF = tr("Change description")), + callback: () => { + createInputModal(_translations.SkDvmbF4 || (_translations.SkDvmbF4 = tr("Change client description")), _translations.UUmz0RUR || (_translations.UUmz0RUR = tr("New description:
")), text => true, result => { + if (typeof (result) === "string") { + //TODO tr + console.log("Changing " + this.clientNickName() + "'s description to " + result); + this.channelTree.client.serverConnection.send_command("clientedit", { + clid: this.clientId(), + client_description: result + }); + } + }, { width: 400, maxLength: 1024 }).open(); + } + }, contextmenu.Entry.HR(), ...this.assignment_context(), contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-move_client_to_own_channel", + name: _translations.IqCkK5LB || (_translations.IqCkK5LB = tr("Move client to your channel")), + callback: () => { + this.channelTree.client.serverConnection.send_command("clientmove", { + clid: this.clientId(), + cid: this.channelTree.client.getClient().currentChannel().getChannelId() + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-kick_channel", + name: _translations.mgE4Z1Js || (_translations.mgE4Z1Js = tr("Kick client from channel")), + callback: () => { + createInputModal(_translations.Gj78RvOz || (_translations.Gj78RvOz = tr("Kick client from channel")), _translations.xhyckLyH || (_translations.xhyckLyH = tr("Kick reason:
")), text => true, result => { + if (typeof (result) !== 'boolean' || result) { + //TODO tr + console.log("Kicking client " + this.clientNickName() + " from channel with reason " + result); + this.channelTree.client.serverConnection.send_command("clientkick", { + clid: this.clientId(), + reasonid: ViewReasonId.VREASON_CHANNEL_KICK, + reasonmsg: result + }); + } + }, { width: 400, maxLength: 255 }).open(); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-kick_server", + name: _translations.ZCjZNTae || (_translations.ZCjZNTae = tr("Kick client fom server")), + callback: () => { + createInputModal(_translations.PXTiLlh1 || (_translations.PXTiLlh1 = tr("Kick client from server")), _translations.U9VbSk_j || (_translations.U9VbSk_j = tr("Kick reason:
")), text => true, result => { + if (typeof (result) !== 'boolean' || result) { + //TODO tr + console.log("Kicking client " + this.clientNickName() + " from server with reason " + result); + this.channelTree.client.serverConnection.send_command("clientkick", { + clid: this.clientId(), + reasonid: ViewReasonId.VREASON_SERVER_KICK, + reasonmsg: result + }); + } + }, { width: 400, maxLength: 255 }).open(); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-ban_client", + name: _translations.t1_5DwD9 || (_translations.t1_5DwD9 = tr("Ban client")), + invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), + callback: () => { + Modals.spawnBanClient(this.channelTree.client, [{ + name: this.properties.client_nickname, + unique_id: this.properties.client_unique_identifier + }], (data) => { + this.channelTree.client.serverConnection.send_command("banclient", { + uid: this.properties.client_unique_identifier, + banreason: data.reason, + time: data.length + }, { + flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""] + }).then(() => { + this.channelTree.client.sound.play(Sound.USER_BANNED); + }); + }); + } + }, contextmenu.Entry.HR(), + /* + { + type: MenuEntryType.ENTRY, + icon: "client-kick_server", + name: "Add group to client", + invalidPermission: true, //!this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), + callback: () => { + Modals.spawnBanClient(this.properties.client_nickname, (duration, reason) => { + this.channelTree.client.serverConnection.send_command("banclient", { + uid: this.properties.client_unique_identifier, + banreason: reason, + time: duration + }); + }); + } + }, + MenuEntry.HR(), + */ + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-volume", + name: _translations._SOYYOpq || (_translations._SOYYOpq = tr("Change Volume")), + callback: () => { + Modals.spawnChangeVolume(this, true, this._audio_volume, undefined, volume => { + this._audio_volume = volume; + this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume); + if (this._audio_handle) + this._audio_handle.set_volume(volume); + //TODO: Update in info + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.LGRXJbyk || (_translations.LGRXJbyk = tr("Change playback latency")), + callback: () => { + Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { + this._audio_handle.reset_latency_settings(); + return this._audio_handle.latency_settings(); + }, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => { + this._audio_handle.flush(); + } : undefined); + }, + visible: this._audio_handle && this._audio_handle.support_latency_settings() + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-input_muted_local", + name: _translations.HXDWIypp || (_translations.HXDWIypp = tr("Mute client")), + visible: !this._audio_muted, + callback: () => this.set_muted(true, true) + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-input_muted_local", + name: _translations.qPADwJeS || (_translations.qPADwJeS = tr("Unmute client")), + visible: this._audio_muted, + callback: () => this.set_muted(false, true) + }, contextmenu.Entry.CLOSE(() => trigger_close && on_close ? on_close() : {})); + } + get tag() { + if (this._tag) + return this._tag; + let container_client = $.spawn("div") + .addClass("tree-entry client") + .attr("client-id", this.clientId()); + /* unread marker */ + { + container_client.append($.spawn("div") + .addClass("marker-text-unread hidden") + .attr("private-conversation", this._clientId)); + } + container_client.append($.spawn("div") + .addClass("icon_client_state") + .attr("title", "Client state")); + container_client.append($.spawn("div") + .addClass("group-prefix") + .attr("title", "Server groups prefixes") + .hide()); + container_client.append($.spawn("div") + .addClass("client-name") + .text(this.clientNickName())); + container_client.append($.spawn("div") + .addClass("group-suffix") + .attr("title", "Server groups suffix") + .hide()); + container_client.append($.spawn("div") + .addClass("client-away-message") + .text(this.clientNickName())); + let container_icons = $.spawn("div").addClass("container-icons"); + container_icons.append($.spawn("div") + .addClass("icon icon_talk_power client-input_muted") + .hide()); + container_icons.append($.spawn("div") + .addClass("container-icons-group")); + container_icons.append($.spawn("div") + .addClass("container-icon-client")); + container_client.append(container_icons); + this._tag = container_client; + this.initializeListener(); + return this._tag; + } + static bbcodeTag(id, name, uid) { + return "[url=client://" + id + "/" + uid + "~" + encodeURIComponent(name) + "]" + name + "[/url]"; + } + static chatTag(id, name, uid, braces = false) { + return $(htmltags.generate_client({ + client_name: name, + client_id: id, + client_unique_id: uid, + add_braces: braces + })); + } + create_bbcode() { + return ClientEntry.bbcodeTag(this.clientId(), this.clientNickName(), this.clientUid()); + } + createChatTag(braces = false) { + return ClientEntry.chatTag(this.clientId(), this.clientNickName(), this.clientUid(), braces); + } + set speaking(flag) { + if (flag === this._speaking) + return; + this._speaking = flag; + this.updateClientSpeakIcon(); + } + updateClientStatusIcons() { + let talk_power = this.properties.client_talk_power >= this._channel.properties.channel_needed_talk_power; + if (talk_power) + this.tag.find(".icon_talk_power").hide(); + else + this.tag.find(".icon_talk_power").show(); + } + updateClientSpeakIcon() { + let icon = ""; + let clicon = ""; + if (this.properties.client_type_exact == ClientType.CLIENT_QUERY) { + icon = "client-server_query"; + console.log("Server query!"); + } + else { + if (this.properties.client_away) { + icon = "client-away"; + } + else if (this._audio_muted && !(this instanceof LocalClientEntry)) { + icon = "client-input_muted_local"; + } + else if (!this.properties.client_output_hardware) { + icon = "client-hardware_output_muted"; + } + else if (this.properties.client_output_muted) { + icon = "client-output_muted"; + } + else if (!this.properties.client_input_hardware) { + icon = "client-hardware_input_muted"; + } + else if (this.properties.client_input_muted) { + icon = "client-input_muted"; + } + else { + if (this._speaking) { + if (this.properties.client_is_channel_commander) + clicon = "client_cc_talk"; + else + clicon = "client_talk"; + } + else { + if (this.properties.client_is_channel_commander) + clicon = "client_cc_idle"; + else + clicon = "client_idle"; + } + } + } + if (clicon.length > 0) + this.tag.find(".icon_client_state").attr('class', 'icon_client_state clicon ' + clicon); + else if (icon.length > 0) + this.tag.find(".icon_client_state").attr('class', 'icon_client_state icon ' + icon); + else + this.tag.find(".icon_client_state").attr('class', 'icon_client_state icon_empty'); + } + updateAwayMessage() { + let tag = this.tag.find(".client-away-message"); + if (this.properties.client_away == true && this.properties.client_away_message) { + tag.text("[" + this.properties.client_away_message + "]"); + tag.show(); + } + else { + tag.hide(); + } + } + updateVariables(...variables) { + let group = log.group(log.LogType.DEBUG, LogCategory.CLIENT, _translations.mwXcnRVj || (_translations.mwXcnRVj = tr("Update properties (%i) of %s (%i)")), variables.length, this.clientNickName(), this.clientId()); + let update_icon_status = false; + let update_icon_speech = false; + let update_away = false; + let reorder_channel = false; + let update_avatar = false; + { + const entries = []; + for (const variable of variables) + entries.push({ + key: variable.key, + value: variable.value, + type: typeof (this.properties[variable.key]) + }); + log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Client update properties", entries); + } + for (const variable of variables) { + const old_value = this._properties[variable.key]; + JSON.map_field_to(this._properties, variable.value, variable.key); + if (variable.key == "client_nickname") { + if (variable.value !== old_value && typeof (old_value) === "string") { + if (!(this instanceof LocalClientEntry)) { /* own changes will be logged somewhere else */ + this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, { + own_client: false, + client: this.log_data(), + new_name: variable.value, + old_name: old_value + }); + } + } + this.tag.find(".client-name").text(variable.value); + const chat = this.channelTree.client.side_bar; + const conversation = chat.private_conversations().find_conversation({ + name: this.clientNickName(), + client_id: this.clientId(), + unique_id: this.clientUid() + }, { + attach: false, + create: false + }); + if (conversation) + conversation.set_client_name(variable.value); + reorder_channel = true; + } + if (variable.key == "client_away" || + variable.key == "client_input_hardware" || + variable.key == "client_output_hardware" || + variable.key == "client_output_muted" || + variable.key == "client_input_muted" || + variable.key == "client_is_channel_commander") { + update_icon_speech = true; + } + if (variable.key == "client_away_message" || variable.key == "client_away") { + update_away = true; + } + if (variable.key == "client_unique_identifier") { + this._audio_volume = parseFloat(this.channelTree.client.settings.server("volume_client_" + this.clientUid(), "1")); + const mute_status = this.channelTree.client.settings.server("mute_client_" + this.clientUid(), false); + this.set_muted(mute_status, false, mute_status); /* force only needed when we want to mute the client */ + if (this._audio_handle) + this._audio_handle.set_volume(this._audio_muted ? 0 : this._audio_volume); + update_icon_speech = true; + log.debug(LogCategory.CLIENT, _translations.BwKYcCsM || (_translations.BwKYcCsM = tr("Loaded client (%s) server specific properties. Volume: %o Muted: %o.")), this.clientUid(), this._audio_volume, this._audio_muted); + } + if (variable.key == "client_talk_power") { + reorder_channel = true; + update_icon_status = true; + } + if (variable.key == "client_icon_id") { + /* yeah we like javascript. Due to JS wiered integer behaviour parsing for example fails for 18446744073409829863. + * parseInt("18446744073409829863") evaluates to 18446744073409829000. + * In opposite "18446744073409829863" >>> 0 evaluates to 3995244544, which is the icon id :) + */ + this.properties.client_icon_id = variable.value >>> 0; + this.updateClientIcon(); + } + if (variable.key == "client_channel_group_id" || variable.key == "client_servergroups") + this.update_displayed_client_groups(); + else if (variable.key == "client_flag_avatar") + update_avatar = true; + } + /* process updates after variables have been set */ + if (this._channel && reorder_channel) + this._channel.reorderClients(); + if (update_icon_speech) + this.updateClientSpeakIcon(); + if (update_icon_status) + this.updateClientStatusIcons(); + if (update_away) + this.updateAwayMessage(); + const side_bar = this.channelTree.client.side_bar; + { + const client_info = side_bar.client_info(); + if (client_info.current_client() === this) + client_info.set_current_client(this, true); /* force an update */ + } + if (update_avatar) { + this.channelTree.client.fileManager.avatars.update_cache(this.avatarId(), this.properties.client_flag_avatar); + const conversations = side_bar.private_conversations(); + const conversation = conversations.find_conversation({ name: this.clientNickName(), unique_id: this.clientUid(), client_id: this.clientId() }, { create: false, attach: false }); + if (conversation) + conversation.update_avatar(); + } + group.end(); + this.events.fire("property_update", { + properties: variables.map(e => e.key) + }); + } + update_displayed_client_groups() { + this.tag.find(".container-icons-group").children().remove(); + for (let id of this.assignedServerGroupIds()) + this.updateGroupIcon(this.channelTree.client.groups.serverGroup(id)); + this.update_group_icon_order(); + this.updateGroupIcon(this.channelTree.client.groups.channelGroup(this.properties.client_channel_group_id)); + let prefix_groups = []; + let suffix_groups = []; + for (const group_id of this.assignedServerGroupIds()) { + const group = this.channelTree.client.groups.serverGroup(group_id); + if (!group) + continue; + if (group.properties.namemode == 1) + prefix_groups.push(group.name); + else if (group.properties.namemode == 2) + suffix_groups.push(group.name); + } + const tag_group_prefix = this.tag.find(".group-prefix"); + const tag_group_suffix = this.tag.find(".group-suffix"); + if (prefix_groups.length > 0) { + tag_group_prefix.text("[" + prefix_groups.join("][") + "]").show(); + } + else { + tag_group_prefix.hide(); + } + if (suffix_groups.length > 0) { + tag_group_suffix.text("[" + suffix_groups.join("][") + "]").show(); + } + else { + tag_group_suffix.hide(); + } + } + updateClientVariables(force_update) { + if (Date.now() - 10 * 60 * 1000 < this._info_variables_promise_timestamp && this._info_variables_promise && (typeof (force_update) !== "boolean" || force_update)) + return this._info_variables_promise; + this._info_variables_promise_timestamp = Date.now(); + return (this._info_variables_promise = new Promise((resolve, reject) => { + this.channelTree.client.serverConnection.send_command("clientgetvariables", { clid: this.clientId() }).then(() => resolve()).catch(error => { + this._info_connection_promise_timestamp = 0; /* not succeeded */ + reject(error); + }); + })); + } + updateClientIcon() { + this.tag.find(".container-icon-client").children().remove(); + if (this.properties.client_icon_id > 0) { + this.channelTree.client.fileManager.icons.generateTag(this.properties.client_icon_id).attr("title", "Client icon") + .appendTo(this.tag.find(".container-icon-client")); + } + } + updateGroupIcon(group) { + if (!group) + return; + const container = this.tag.find(".container-icons-group"); + container.find(".icon_group_" + group.id).remove(); + if (group.properties.iconid > 0) { + container.append($.spawn("div").attr('group-power', group.properties.sortid) + .addClass("container-group-icon icon_group_" + group.id) + .append(this.channelTree.client.fileManager.icons.generateTag(group.properties.iconid)).attr("title", group.name)); + } + } + update_group_icon_order() { + const container = this.tag.find(".container-icons-group"); + container.append(...[...container.children()].sort((a, b) => parseInt(a.getAttribute("group-power")) - parseInt(b.getAttribute("group-power")))); + } + assignedServerGroupIds() { + let result = []; + for (let id of this.properties.client_servergroups.split(",")) { + if (id.length == 0) + continue; + result.push(Number.parseInt(id)); + } + return result; + } + assignedChannelGroup() { + return this.properties.client_channel_group_id; + } + groupAssigned(group) { + if (group.target == GroupTarget.SERVER) { + for (let id of this.assignedServerGroupIds()) + if (id == group.id) + return true; + return false; + } + else + return group.id == this.assignedChannelGroup(); + } + onDelete() { } + calculateOnlineTime() { + return Date.now() / 1000 - this.properties.client_lastconnected; + } + avatarId() { + function str2ab(str) { + let buf = new ArrayBuffer(str.length); // 2 bytes for each char + let bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; + } + try { + let raw = atob(this.properties.client_unique_identifier); + let input = hex.encode(str2ab(raw)); + let result = ""; + for (let index = 0; index < input.length; index++) { + let c = input.charAt(index); + let offset = 0; + if (c >= '0' && c <= '9') + offset = c.charCodeAt(0) - '0'.charCodeAt(0); + else if (c >= 'A' && c <= 'F') + offset = c.charCodeAt(0) - 'A'.charCodeAt(0) + 0x0A; + else if (c >= 'a' && c <= 'f') + offset = c.charCodeAt(0) - 'a'.charCodeAt(0) + 0x0A; + result += String.fromCharCode('a'.charCodeAt(0) + offset); + } + return result; + } + catch (e) { //invalid base 64 (like music bot etc) + return undefined; + } + } + update_family_index() { + if (!this._channel) + return; + const index = this._channel.calculate_family_index(); + this.tag.css('padding-left', (5 + (index + 2) * 16) + "px"); + } + log_data() { + return { + client_unique_id: this.properties.client_unique_identifier, + client_name: this.clientNickName(), + client_id: this._clientId + }; + } + /* max 1s ago, so we could update every second */ + request_connection_info() { + if (Date.now() - 900 < this._info_connection_promise_timestamp && this._info_connection_promise) + return this._info_connection_promise; + if (this._info_connection_promise_reject) + this._info_connection_promise_resolve("timeout"); + let _local_reject; /* to ensure we're using the right resolve! */ + this._info_connection_promise = new Promise((resolve, reject) => { + this._info_connection_promise_resolve = resolve; + this._info_connection_promise_reject = reject; + _local_reject = reject; + }); + this._info_connection_promise_timestamp = Date.now(); + this.channelTree.client.serverConnection.send_command("getconnectioninfo", { clid: this._clientId }).catch(error => _local_reject(error)); + return this._info_connection_promise; + } + set_connection_info(info) { + if (!this._info_connection_promise_resolve) + return; + this._info_connection_promise_resolve(info); + this._info_connection_promise_resolve = undefined; + this._info_connection_promise_reject = undefined; + } + set flag_text_unread(flag) { + this._tag.find(".marker-text-unread").toggleClass("hidden", !flag); + } +} +class LocalClientEntry extends ClientEntry { + constructor(handle) { + super(0, "local client"); + this.handle = handle; + } + showContextMenu(x, y, on_close = undefined) { + const _self = this; + contextmenu.spawn_context_menu(x, y, ...this.contextmenu_info(), { + name: (contextmenu.get_provider().html_format_enabled() ? "" : "") + (_translations.TWJRawAz || (_translations.TWJRawAz = tr("Change name"))) + + (contextmenu.get_provider().html_format_enabled() ? "" : ""), + icon_class: "client-change_nickname", + callback: () => _self.openRename(), + type: contextmenu.MenuEntryType.ENTRY + }, { + name: _translations.QXkvf75y || (_translations.QXkvf75y = tr("Change description")), + icon_class: "client-edit", + callback: () => { + createInputModal(_translations.eo0BTEUD || (_translations.eo0BTEUD = tr("Change own description")), _translations.cGSxzOs8 || (_translations.cGSxzOs8 = tr("New description:
")), text => true, result => { + if (result) { + console.log(_translations.KltoFGs6 || (_translations.KltoFGs6 = tr("Changing own description to %s")), result); + _self.channelTree.client.serverConnection.send_command("clientedit", { + clid: _self.clientId(), + client_description: result + }); + } + }, { width: 400, maxLength: 1024 }).open(); + }, + type: contextmenu.MenuEntryType.ENTRY + }, contextmenu.Entry.HR(), ...this.assignment_context(), contextmenu.Entry.CLOSE(on_close)); + } + initializeListener() { + if (this._listener_initialized) + this.tag.off(); + this._listener_initialized = false; /* could there be a better system */ + super.initializeListener(); + this.tag.find(".client-name").addClass("client-name-own"); + this.tag.on('dblclick', () => { + if (Array.isArray(this.channelTree.currently_selected)) { //Multiselect + return; + } + this.openRename(); + }); + } + openRename() { + this.channelTree.client_mover.enabled = false; + const elm = this.tag.find(".client-name"); + elm.attr("contenteditable", "true"); + elm.removeClass("client-name-own"); + elm.css("background-color", "white"); + elm.focus(); + this.renaming = true; + elm.on('keypress', event => { + if (event.keyCode == KeyCode.KEY_RETURN) { + $(event.target).trigger("focusout"); + return false; + } + }); + elm.on('focusout', event => { + this.channelTree.client_mover.enabled = true; + if (!this.renaming) + return; + this.renaming = false; + elm.css("background-color", ""); + elm.removeAttr("contenteditable"); + elm.addClass("client-name-own"); + let text = elm.text().toString(); + if (this.clientNickName() == text) + return; + elm.text(this.clientNickName()); + const old_name = this.clientNickName(); + this.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => { + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, text); + this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, { + client: this.log_data(), + old_name: old_name, + new_name: text, + own_client: true + }); + }).catch((e) => { + this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGE_FAILED, { + reason: e.extra_message + }); + this.openRename(); + }); + }); + } +} +class MusicClientProperties extends ClientProperties { + constructor() { + super(...arguments); + this.player_state = 0; + this.player_volume = 0; + this.client_playlist_id = 0; + this.client_disabled = false; + this.client_flag_notify_song_change = false; + this.client_bot_type = 0; + this.client_uptime_mode = 0; + } +} +/* + * command[index]["song_id"] = element ? element->getSongId() : 0; + command[index]["song_url"] = element ? element->getUrl() : ""; + command[index]["song_invoker"] = element ? element->getInvoker() : 0; + command[index]["song_loaded"] = false; + + auto entry = dynamic_pointer_cast(element); + if(entry) { + auto data = entry->song_loaded_data(); + command[index]["song_loaded"] = entry->song_loaded() && data; + + if(entry->song_loaded() && data) { + command[index]["song_title"] = data->title; + command[index]["song_description"] = data->description; + command[index]["song_thumbnail"] = data->thumbnail; + command[index]["song_length"] = data->length.count(); + } + } + */ +class SongInfo { + constructor() { + this.song_id = 0; + this.song_url = ""; + this.song_invoker = 0; + this.song_loaded = false; + /* only if song_loaded = true */ + this.song_title = ""; + this.song_description = ""; + this.song_thumbnail = ""; + this.song_length = 0; + } +} +class MusicClientPlayerInfo extends SongInfo { + constructor() { + super(...arguments); + this.bot_id = 0; + this.player_state = 0; + this.player_buffered_index = 0; + this.player_replay_index = 0; + this.player_max_index = 0; + this.player_seekable = false; + this.player_title = ""; + this.player_description = ""; + } +} +class MusicClientEntry extends ClientEntry { + constructor(clientId, clientName) { + super(clientId, clientName, new MusicClientProperties()); + this._info_promise_age = 0; + } + destroy() { + super.destroy(); + this._info_promise = undefined; + this._info_promise_reject = undefined; + this._info_promise_resolve = undefined; + } + get properties() { + return this._properties; + } + showContextMenu(x, y, on_close = undefined) { + let trigger_close = true; + contextmenu.spawn_context_menu(x, y, ...this.contextmenu_info(), { + name: (contextmenu.get_provider().html_format_enabled() ? "" : "") + (_translations.XbLxNlO8 || (_translations.XbLxNlO8 = tr("Change bot name"))) + + (contextmenu.get_provider().html_format_enabled() ? "" : ""), + icon_class: "client-change_nickname", + disabled: false, + callback: () => { + createInputModal(_translations.pmicCstj || (_translations.pmicCstj = tr("Change music bots nickname")), _translations.vqukZhk9 || (_translations.vqukZhk9 = tr("New nickname:
")), text => text.length >= 3 && text.length <= 31, result => { + if (result) { + this.channelTree.client.serverConnection.send_command("clientedit", { + clid: this.clientId(), + client_nickname: result + }); + } + }, { width: "40em", min_width: "10em", maxLength: 255 }).open(); + }, + type: contextmenu.MenuEntryType.ENTRY + }, { + name: _translations.KwxFZl6L || (_translations.KwxFZl6L = tr("Change bot description")), + icon_class: "client-edit", + disabled: false, + callback: () => { + createInputModal(_translations.mYzzfhcQ || (_translations.mYzzfhcQ = tr("Change music bots description")), _translations.ZTCZj4qo || (_translations.ZTCZj4qo = tr("New description:
")), text => true, result => { + if (typeof (result) === 'string') { + this.channelTree.client.serverConnection.send_command("clientedit", { + clid: this.clientId(), + client_description: result + }); + } + }, { width: "60em", min_width: "10em", maxLength: 255 }).open(); + }, + type: contextmenu.MenuEntryType.ENTRY + }, + /* + { + name: tr("Open music panel"), + icon: "client-edit", + disabled: true, + callback: () => {}, + type: MenuEntryType.ENTRY + }, + */ + { + name: _translations.JLASYUDm || (_translations.JLASYUDm = tr("Open bot's playlist")), + icon_class: "client-edit", + disabled: false, + callback: () => { + this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => { + for (const entry of lists) { + if (entry.playlist_id == this.properties.client_playlist_id) { + Modals.spawnPlaylistEdit(this.channelTree.client, entry); + return; + } + } + createErrorModal(_translations.ecR9BnOb || (_translations.ecR9BnOb = tr("Invalid permissions")), _translations.bjVFcLvv || (_translations.bjVFcLvv = tr("You dont have to see the bots playlist."))).open(); + }).catch(error => { + createErrorModal(_translations.cCQHXsEN || (_translations.cCQHXsEN = tr("Failed to query playlist.")), _translations.z0TAFloZ || (_translations.z0TAFloZ = tr("Failed to query playlist info."))).open(); + }); + }, + type: contextmenu.MenuEntryType.ENTRY + }, { + name: _translations.v3a_2VKL || (_translations.v3a_2VKL = tr("Quick url replay")), + icon_class: "client-edit", + disabled: false, + callback: () => { + createInputModal(_translations.qP0cnomf || (_translations.qP0cnomf = tr("Please enter the URL")), _translations.yQiADIEs || (_translations.yQiADIEs = tr("URL:")), text => true, result => { + if (result) { + this.channelTree.client.serverConnection.send_command("musicbotqueueadd", { + bot_id: this.properties.client_database_id, + type: "yt", + url: result + }).catch(error => { + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + //TODO tr + createErrorModal(_translations.LAv5uiVz || (_translations.LAv5uiVz = tr("Failed to replay url")), "Failed to enqueue url:
" + error).open(); + }); + } + }, { width: 400, maxLength: 255 }).open(); + }, + type: contextmenu.MenuEntryType.ENTRY + }, contextmenu.Entry.HR(), ...super.assignment_context(), contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-move_client_to_own_channel", + name: _translations.y7NwSbFu || (_translations.y7NwSbFu = tr("Move client to your channel")), + callback: () => { + this.channelTree.client.serverConnection.send_command("clientmove", { + clid: this.clientId(), + cid: this.channelTree.client.getClient().currentChannel().getChannelId() + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-kick_channel", + name: _translations.l4FBtA8t || (_translations.l4FBtA8t = tr("Kick client from channel")), + callback: () => { + createInputModal(_translations.I7AyThLI || (_translations.I7AyThLI = tr("Kick client from channel")), _translations.Xd1D_Zzu || (_translations.Xd1D_Zzu = tr("Kick reason:
")), text => true, result => { + if (typeof (result) !== 'boolean' || result) { + console.log(_translations.mWrcl55N || (_translations.mWrcl55N = tr("Kicking client %o from channel with reason %o")), this.clientNickName(), result); + this.channelTree.client.serverConnection.send_command("clientkick", { + clid: this.clientId(), + reasonid: ViewReasonId.VREASON_CHANNEL_KICK, + reasonmsg: result + }); + } + }, { width: 400, maxLength: 255 }).open(); + } + }, contextmenu.Entry.HR(), { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-volume", + name: _translations.GjpMhrnj || (_translations.GjpMhrnj = tr("Change local volume")), + callback: () => { + Modals.spawnChangeVolume(this, true, this._audio_handle.get_volume(), undefined, volume => { + this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume); + this._audio_handle.set_volume(volume); + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-volume", + name: _translations.xbWCjjmW || (_translations.xbWCjjmW = tr("Change remote volume")), + callback: () => { + let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value; + if (max_volume < 0) + max_volume = 100; + Modals.spawnChangeVolume(this, false, this.properties.player_volume, max_volume / 100, value => { + if (typeof (value) !== "number") + return; + this.channelTree.client.serverConnection.send_command("clientedit", { + clid: this.clientId(), + player_volume: value, + }).then(() => { + //TODO: Update in info + }); + }); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.eRh0JpMP || (_translations.eRh0JpMP = tr("Change playback latency")), + callback: () => { + Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { + this._audio_handle.reset_latency_settings(); + return this._audio_handle.latency_settings(); + }, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => { + this._audio_handle.flush(); + } : undefined); + }, + visible: this._audio_handle && this._audio_handle.support_latency_settings() + }, contextmenu.Entry.HR(), { + name: _translations.J7K_FqfX || (_translations.J7K_FqfX = tr("Delete bot")), + icon_class: "client-delete", + disabled: false, + callback: () => { + const tag = $.spawn("div").append(MessageHelper.formatMessage(_translations.RjNsfIH5 || (_translations.RjNsfIH5 = tr("Do you really want to delete {0}")), this.createChatTag(false))); + Modals.spawnYesNo(_translations.UryMTw7o || (_translations.UryMTw7o = tr("Are you sure?")), $.spawn("div").append(tag), result => { + if (result) { + this.channelTree.client.serverConnection.send_command("musicbotdelete", { + bot_id: this.properties.client_database_id + }); + } + }); + }, + type: contextmenu.MenuEntryType.ENTRY + }, contextmenu.Entry.CLOSE(() => trigger_close && on_close ? on_close() : {})); + } + initializeListener() { + super.initializeListener(); + } + handlePlayerInfo(json) { + if (json) { + const info = new MusicClientPlayerInfo(); + JSON.map_to(info, json); + if (this._info_promise_resolve) + this._info_promise_resolve(info); + this._info_promise_reject = undefined; + this._info_promise_resolve = undefined; + } + } + requestPlayerInfo(max_age = 1000) { + if (this._info_promise !== undefined && this._info_promise_age > 0 && Date.now() - max_age <= this._info_promise_age) + return this._info_promise; + this._info_promise_age = Date.now(); + this._info_promise = new Promise((resolve, reject) => { + this._info_promise_reject = reject; + this._info_promise_resolve = resolve; + }); + this.channelTree.client.serverConnection.send_command("musicbotplayerinfo", { bot_id: this.properties.client_database_id }); + return this._info_promise; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["05ca7973a11a972f3d6b90eb9141fa51cfc36ba0897d1a7ec39adb5cbe0a6f99"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["05ca7973a11a972f3d6b90eb9141fa51cfc36ba0897d1a7ec39adb5cbe0a6f99"] = "05ca7973a11a972f3d6b90eb9141fa51cfc36ba0897d1a7ec39adb5cbe0a6f99"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "BJLONISG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerEdit.ts (35,21)" }, { name: "GqQnNwWS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerEdit.ts (211,29)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function createServerModal(server, callback) { + const properties = Object.assign({}, server.properties); + let _valid_states = { + general: false + }; + let _toggle_valid = (key, value) => { + if (typeof (key) === "string") { + _valid_states[key] = value; + } + let flag = true; + for (const key of Object.keys(_valid_states)) + if (!_valid_states[key]) { + flag = false; + break; + } + if (flag) { + flag = false; + for (const property_name of Object.keys(properties)) { + if (server.properties[property_name] !== properties[property_name]) { + flag = true; + break; + } + } + } + button_save.prop("disabled", !flag); + }; + const modal = createModal({ + header: _translations.BJLONISG || (_translations.BJLONISG = tr("Manage the Virtual Server")), + body: () => { + const template = $("#tmpl_server_edit").renderTag(Object.assign(Object.assign({}, server.properties), { + server_icon: server.channelTree.client.fileManager.icons.generateTag(server.properties.virtualserver_icon_id) + })); + /* the tab functionality */ + { + const container_tabs = template.find(".container-categories"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); + container_tabs.find(".entry").first().trigger('click'); + } + apply_general_listener(template.find(".container-general"), server, properties, _toggle_valid); + apply_host_listener(template.find(".container-host"), server, properties, _toggle_valid); + apply_network_listener(template.find(".container-network"), server, properties, _toggle_valid, modal); + apply_security_listener(template.find(".container-security"), server, properties, _toggle_valid); + apply_messages_listener(template.find(".container-messages"), server, properties, _toggle_valid); + apply_misc_listener(template.find(".container-misc"), server, properties, _toggle_valid); + return template.contents(); + }, + footer: null, + min_width: "35em" + }); + tooltip(modal.htmlTag); + const button_save = modal.htmlTag.find(".button-save"); + button_save.on('click', event => { + const changed = {}; + for (const property_name of Object.keys(properties)) + if (server.properties[property_name] !== properties[property_name]) + changed[property_name] = properties[property_name]; + callback(changed).then(() => { + _toggle_valid(undefined); + }); + }); + modal.htmlTag.find(".button-cancel").on('click', event => { + modal.close(); + callback(); + }); + _toggle_valid("general", true); + modal.htmlTag.find(".modal-body").addClass("modal-server-edit modal-blue"); + modal.open(); + } + Modals.createServerModal = createServerModal; + function apply_general_listener(tag, server, properties, callback_valid) { + /* name */ + { + const container = tag.find(".virtualserver_name"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1); + container.on('change', event => { + properties.virtualserver_name = container.val(); + const invalid = properties.virtualserver_name.length > 70 || properties.virtualserver_name.length < 1; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_name", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* icon */ + { + tag.find(".button-select-icon").on('click', event => { + Modals.spawnIconSelect(server.channelTree.client, id => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(server.channelTree.client.fileManager.icons.generateTag(id)); + console.log("Selected icon ID: %d", id); + properties.virtualserver_icon_id = id; + callback_valid(undefined); //Toggle save button update + }, properties.virtualserver_icon_id); + }); + tag.find(".button-icon-remove").on('click', event => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(server.channelTree.client.fileManager.icons.generateTag(0)); + console.log("Remove server icon"); + properties.virtualserver_icon_id = 0; + callback_valid(undefined); //Toggle save button update + }); + } + /* password */ + { + //TODO: On save let the user retype his password? + const container = tag.find(".virtualserver_password"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PASSWORD).granted(1); + container.on('change', event => { + const password = container.val(); + properties.virtualserver_flag_password = !!password; + if (properties.virtualserver_flag_password) { + helpers.hashPassword(password).then(pass => properties.virtualserver_password = pass); + } + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* slots */ + { + const container_max = tag.find(".virtualserver_maxclients"); + const container_reserved = tag.find(".virtualserver_reserved_slots"); + /* max users */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_MAXCLIENTS).granted(1); + container_max.on('change', event => { + properties.virtualserver_maxclients = parseInt(container_max.val()); + const invalid = properties.virtualserver_maxclients < 1 || properties.virtualserver_maxclients > 1024; + container_max.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_maxclients", !invalid); + container_reserved.trigger('change'); /* update the flag */ + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* reserved */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS).granted(1); + container_reserved.on('change', event => { + properties.virtualserver_reserved_slots = parseInt(container_reserved.val()); + const invalid = properties.virtualserver_reserved_slots > properties.virtualserver_maxclients; + container_reserved.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_reserved_slots", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + /* Welcome message */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE).granted(1); + const container = tag.find(".container-welcome-message"); + const input = container.find("textarea"); + const insert_tag = (open, close) => { + if (input.prop("disabled")) + return; + const node = input[0]; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; + } + else { + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; + } + input.focus().trigger('change'); + }; + input.on('change', event => { + console.log(_translations.GqQnNwWS || (_translations.GqQnNwWS = tr("Welcome message edited: %o")), input.val()); + properties.virtualserver_welcomemessage = input.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container.find(".button-color input").on('change', event => { + insert_tag('[color=' + event.target.value + ']', '[/color]'); + }); + } + } + function apply_network_listener(tag, server, properties, callback_valid, modal) { + /* binding */ + { + /* host */ + { + const container = tag.find(".virtualserver_host"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOST).granted(1); + container.on('change', event => { + properties.virtualserver_host = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_host)); + } + /* port */ + { + const container = tag.find(".virtualserver_port"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PORT).granted(1); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_port = value; + const valid = value >= 1 && value < 65536; + callback_valid("virtualserver_port", valid); + container.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_port)); + } + /* TeamSpeak server list */ + { + const container = tag.find(".virtualserver_weblist_enabled"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WEBLIST).granted(1); + container.on('change', event => { + properties.virtualserver_weblist_enabled = container.prop("checked"); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".checkbox").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.prop("checked", server.properties.virtualserver_weblist_enabled)); + } + } + /* file download */ + { + /* bandwidth */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); + const container = tag.find(".virtualserver_max_download_total_bandwidth"); + container.on('change', event => { + properties.virtualserver_max_download_total_bandwidth = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_max_download_total_bandwidth)); + } + /* Quota */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); + const container = tag.find(".virtualserver_download_quota"); + container.on('change', event => { + properties.virtualserver_download_quota = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_download_quota)); + } + } + /* file upload */ + { + /* bandwidth */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); + const container = tag.find(".virtualserver_max_upload_total_bandwidth"); + container.on('change', event => { + properties.virtualserver_max_upload_total_bandwidth = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_max_upload_total_bandwidth)); + } + /* Quota */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); + const container = tag.find(".virtualserver_upload_quota"); + container.on('change', event => { + properties.virtualserver_upload_quota = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_upload_quota)); + } + } + /* quota info */ + { + server.updateProperties().then(() => { + tag.find(".value.virtualserver_month_bytes_downloaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_month_bytes_downloaded)); + tag.find(".value.virtualserver_month_bytes_uploaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_month_bytes_uploaded)); + tag.find(".value.virtualserver_total_bytes_downloaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_total_bytes_downloaded)); + tag.find(".value.virtualserver_total_bytes_uploaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_total_bytes_uploaded)); + }); + } + /* quota update task */ + if (server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CONNECTIONINFO_VIEW).granted(1)) { + const month_bytes_downloaded = tag.find(".value.virtualserver_month_bytes_downloaded")[0]; + const month_bytes_uploaded = tag.find(".value.virtualserver_month_bytes_uploaded")[0]; + const total_bytes_downloaded = tag.find(".value.virtualserver_total_bytes_downloaded")[0]; + const total_bytes_uploaded = tag.find(".value.virtualserver_total_bytes_uploaded")[0]; + let id = setInterval(() => { + if (!modal.shown) { + clearInterval(id); + return; + } + server.request_connection_info().then(info => { + if (info.connection_filetransfer_bytes_sent_month && month_bytes_downloaded) + month_bytes_downloaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_sent_month); + if (info.connection_filetransfer_bytes_received_month && month_bytes_uploaded) + month_bytes_uploaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_received_month); + if (info.connection_filetransfer_bytes_sent_total && total_bytes_downloaded) + total_bytes_downloaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_sent_total); + if (info.connection_filetransfer_bytes_received_total && total_bytes_uploaded) + total_bytes_uploaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_received_total); + }); + }, 1000); + modal.close_listener.push(() => clearInterval(id)); + } + } + function apply_host_listener(tag, server, properties, callback_valid) { + /* host message */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1); + /* message */ + { + const container = tag.find(".virtualserver_hostmessage"); + container.on('change', event => { + properties.virtualserver_hostmessage = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* mode */ + { + const container = tag.find(".virtualserver_hostmessage_mode"); + container.on('change', event => { + properties.virtualserver_hostmessage_mode = Math.min(3, Math.max(0, parseInt(container.val()))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + /* host banner */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1); + /* URL */ + { + const container = tag.find(".virtualserver_hostbanner_url"); + container.on('change', event => { + properties.virtualserver_hostbanner_url = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* Image URL/Image Preview */ + { + const container = tag.find(".virtualserver_hostbanner_gfx_url"); + const container_preview = tag.find(".container-host-message .container-gfx-preview img"); + container.on('change', event => { + properties.virtualserver_hostbanner_gfx_url = container.val(); + container_preview.attr("src", properties.virtualserver_hostbanner_gfx_url).toggle(!!properties.virtualserver_hostbanner_gfx_url); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* Image Refresh */ + { + const container = tag.find(".virtualserver_hostbanner_gfx_interval"); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_hostbanner_gfx_interval = value; + const invalid = value < 60 && value != 0; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_hostbanner_gfx_interval", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* mode */ + { + const container = tag.find(".virtualserver_hostbanner_mode"); + container.on('change', event => { + properties.virtualserver_hostbanner_mode = Math.min(2, Math.max(0, parseInt(container.val()))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + /* host button */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1); + /* URL */ + { + const container = tag.find(".virtualserver_hostbutton_url"); + container.on('change', event => { + properties.virtualserver_hostbutton_url = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* Tooltip */ + { + const container = tag.find(".virtualserver_hostbutton_tooltip"); + container.on('change', event => { + properties.virtualserver_hostbutton_tooltip = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* Icon URL/Icon Preview */ + { + const container = tag.find(".virtualserver_hostbutton_gfx_url"); + const container_preview = tag.find(".container-host-button .container-gfx-preview img"); + container.on('change', event => { + properties.virtualserver_hostbutton_gfx_url = container.val(); + container_preview.attr("src", properties.virtualserver_hostbutton_gfx_url).toggle(!!properties.virtualserver_hostbutton_gfx_url); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + } + function apply_security_listener(tag, server, properties, callback_valid) { + /* Anti flood */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1); + /* reduce */ + { + const container = tag.find(".virtualserver_antiflood_points_tick_reduce"); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_antiflood_points_tick_reduce = value; + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_tick_reduce", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_tick_reduce)); + } + /* block commands */ + { + const container = tag.find(".virtualserver_antiflood_points_needed_command_block"); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_antiflood_points_needed_command_block = value; + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_needed_command_block", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_command_block)); + } + /* block ip */ + { + const container = tag.find(".virtualserver_antiflood_points_needed_ip_block"); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_antiflood_points_needed_ip_block = value; + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_needed_ip_block", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_ip_block)); + } + } + /* encryption */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE).granted(1); + const container = tag.find(".virtualserver_codec_encryption_mode"); + container.on('change', event => { + properties.virtualserver_codec_encryption_mode = Math.min(2, Math.max(0, parseInt(container.val()))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* security level */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL).granted(1); + const container = tag.find(".virtualserver_needed_identity_security_level"); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_needed_identity_security_level = value; + const invalid = value < 8 || value > 99; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_needed_identity_security_level", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_needed_identity_security_level)); + } + } + function apply_messages_listener(tag, server, properties, callback_valid) { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1); + /* channel topic */ + { + const container = tag.find(".virtualserver_default_channel_topic"); + container.on('change', event => { + properties.virtualserver_default_channel_topic = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* channel description */ + { + const container = tag.find(".virtualserver_default_channel_description"); + container.on('change', event => { + properties.virtualserver_default_channel_description = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_default_channel_description)); + } + /* client description */ + { + const container = tag.find(".virtualserver_default_client_description"); + container.on('change', event => { + properties.virtualserver_default_client_description = container.val(); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_default_client_description)); + } + } + function apply_misc_listener(tag, server, properties, callback_valid) { + /* default groups */ + { + /* Server Group */ + { + const container = tag.find(".virtualserver_default_server_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP).granted(1); + container.on('change', event => { + properties.virtualserver_default_server_group = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + for (const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { + if (group.type != 2) + continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if (group.id == server.properties.virtualserver_default_server_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); + } + } + /* Music Group */ + { + const container = tag.find(".virtualserver_default_music_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP).granted(1); + container.on('change', event => { + properties.virtualserver_default_music_group = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + for (const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { + if (group.type != 2) + continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if (group.id == server.properties.virtualserver_default_music_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); + } + } + /* Channel Admin Group */ + { + const container = tag.find(".virtualserver_default_channel_admin_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP).granted(1); + container.on('change', event => { + properties.virtualserver_default_channel_admin_group = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + for (const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { + if (group.type != 2) + continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if (group.id == server.properties.virtualserver_default_channel_admin_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); + } + } + /* Channel Guest Group */ + { + const container = tag.find(".virtualserver_default_channel_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP).granted(1); + container.on('change', event => { + properties.virtualserver_default_channel_group = parseInt(container.val()); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + for (const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { + if (group.type != 2) + continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if (group.id == server.properties.virtualserver_default_channel_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); + } + } + } + /* complains */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1); + /* ban threshold */ + { + const container = tag.find(".virtualserver_complain_autoban_count"); + container.on('change', event => { + properties.virtualserver_complain_autoban_count = parseInt(container.val()); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_count)); + } + /* ban time */ + { + const container = tag.find(".virtualserver_complain_autoban_time"); + container.on('change', event => { + properties.virtualserver_complain_autoban_time = parseInt(container.val()); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_time)); + } + /* auto remove time */ + { + const container = tag.find(".virtualserver_complain_remove_time"); + container.on('change', event => { + properties.virtualserver_complain_remove_time = parseInt(container.val()); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_remove_time)); + } + } + /* others */ + { + /* clients before silence */ + { + const container = tag.find(".virtualserver_min_clients_in_channel_before_forced_silence"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE).granted(1); + container.on('change', event => { + const value = parseInt(container.val()); + properties.virtualserver_min_clients_in_channel_before_forced_silence = value; + callback_valid("virtualserver_min_clients_in_channel_before_forced_silence", value > 1); + container.firstParent(".input-boxed").toggleClass("is-invalid", value <= 1); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + server.updateProperties().then(() => container.val(server.properties.virtualserver_min_clients_in_channel_before_forced_silence)); + } + /* priority speaker dim factor */ + { + const container = tag.find(".virtualserver_priority_speaker_dimm_modificator"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR).granted(1); + container.on('change', event => { + properties.virtualserver_priority_speaker_dimm_modificator = parseInt(container.val()); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + /* channel delete delay */ + { + const container = tag.find(".virtualserver_channel_temp_delete_delay_default"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT).granted(1); + container.on('change', event => { + properties.virtualserver_channel_temp_delete_delay_default = parseInt(container.val()); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["6079f752d3ed1818f65c23c2166c43f6d4e67d4994e5849c20b0297e41e704b6"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["6079f752d3ed1818f65c23c2166c43f6d4e67d4994e5849c20b0297e41e704b6"] = "6079f752d3ed1818f65c23c2166c43f6d4e67d4994e5849c20b0297e41e704b6"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "xl5h3fOx", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (209,23)" }, { name: "n60We1tw", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (218,23)" }, { name: "xTkdlACq", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (226,23)" }, { name: "dKIgGTUq", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (235,23)" }, { name: "zjiyeNdR", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (238,54)" }, { name: "simVGxCY", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (239,37)" }, { name: "MKb9CCNC", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (257,23)" }, { name: "aUKw1raS", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (262,23)" }, { name: "CDsTVwgK", path: "D:/TeaSpeak/web/shared/js/ui/server.ts (271,70)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +class ServerProperties { + constructor() { + this.virtualserver_host = ""; + this.virtualserver_port = 0; + this.virtualserver_name = ""; + this.virtualserver_name_phonetic = ""; + this.virtualserver_icon_id = 0; + this.virtualserver_version = "unknown"; + this.virtualserver_platform = "unknown"; + this.virtualserver_unique_identifier = ""; + this.virtualserver_clientsonline = 0; + this.virtualserver_queryclientsonline = 0; + this.virtualserver_channelsonline = 0; + this.virtualserver_uptime = 0; + this.virtualserver_created = 0; + this.virtualserver_maxclients = 0; + this.virtualserver_reserved_slots = 0; + this.virtualserver_password = ""; + this.virtualserver_flag_password = false; + this.virtualserver_ask_for_privilegekey = false; + this.virtualserver_welcomemessage = ""; + this.virtualserver_hostmessage = ""; + this.virtualserver_hostmessage_mode = 0; + this.virtualserver_hostbanner_url = ""; + this.virtualserver_hostbanner_gfx_url = ""; + this.virtualserver_hostbanner_gfx_interval = 0; + this.virtualserver_hostbanner_mode = 0; + this.virtualserver_hostbutton_tooltip = ""; + this.virtualserver_hostbutton_url = ""; + this.virtualserver_hostbutton_gfx_url = ""; + this.virtualserver_codec_encryption_mode = 0; + this.virtualserver_default_music_group = 0; + this.virtualserver_default_server_group = 0; + this.virtualserver_default_channel_group = 0; + this.virtualserver_default_channel_admin_group = 0; + //Special requested properties + this.virtualserver_default_client_description = ""; + this.virtualserver_default_channel_description = ""; + this.virtualserver_default_channel_topic = ""; + this.virtualserver_antiflood_points_tick_reduce = 0; + this.virtualserver_antiflood_points_needed_command_block = 0; + this.virtualserver_antiflood_points_needed_ip_block = 0; + this.virtualserver_country_code = "XX"; + this.virtualserver_complain_autoban_count = 0; + this.virtualserver_complain_autoban_time = 0; + this.virtualserver_complain_remove_time = 0; + this.virtualserver_needed_identity_security_level = 8; + this.virtualserver_weblist_enabled = false; + this.virtualserver_min_clients_in_channel_before_forced_silence = 0; + this.virtualserver_channel_temp_delete_delay_default = 60; + this.virtualserver_priority_speaker_dimm_modificator = -18; + this.virtualserver_max_upload_total_bandwidth = 0; + this.virtualserver_upload_quota = 0; + this.virtualserver_max_download_total_bandwidth = 0; + this.virtualserver_download_quota = 0; + this.virtualserver_month_bytes_downloaded = 0; + this.virtualserver_month_bytes_uploaded = 0; + this.virtualserver_total_bytes_downloaded = 0; + this.virtualserver_total_bytes_uploaded = 0; + } +} +class ServerEntry { + constructor(tree, name, address) { + this.info_request_promise = undefined; + this.info_request_promise_resolve = undefined; + this.info_request_promise_reject = undefined; + this.lastInfoRequest = 0; + this.nextInfoRequest = 0; + this._destroyed = false; + this.properties = new ServerProperties(); + this.channelTree = tree; + this.remote_address = Object.assign({}, address); /* close the address because it might get changed due to the DNS resolve */ + this.properties.virtualserver_name = name; + } + get htmlTag() { + if (this._destroyed) + throw "destoryed"; + if (this._htmlTag) + return this._htmlTag; + let tag = $.spawn("div").addClass("tree-entry server"); + /* unread marker */ + { + tag.append($.spawn("div") + .addClass("marker-text-unread hidden") + .attr("conversation", 0)); + } + tag.append($.spawn("div") + .addClass("server_type icon client-server_green")); + tag.append($.spawn("div") + .addClass("name") + .text(this.properties.virtualserver_name)); + tag.append($.spawn("div") + .addClass("icon_property icon_empty")); + return this._htmlTag = tag; + } + destroy() { + this._destroyed = true; + if (this._htmlTag) { + this._htmlTag.remove(); + this._htmlTag = undefined; + } + this.info_request_promise = undefined; + this.info_request_promise_resolve = undefined; + this.info_request_promise_reject = undefined; + this.channelTree = undefined; + this.remote_address = undefined; + } + initializeListener() { + this._htmlTag.on('click', () => { + this.channelTree.onSelect(this); + this.updateProperties(); /* just prepare to show some server info */ + }); + if (!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) { + this.htmlTag.on("contextmenu", (event) => { + event.preventDefault(); + if ($.isArray(this.channelTree.currently_selected)) { //Multiselect + (this.channelTree.currently_selected_context_callback || ((_) => null))(event); + return; + } + this.channelTree.onSelect(this, true); + this.spawnContextMenu(event.pageX, event.pageY, () => { this.channelTree.onSelect(undefined, true); }); + }); + } + } + spawnContextMenu(x, y, on_close = () => { }) { + let trigger_close = true; + contextmenu.spawn_context_menu(x, y, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.xl5h3fOx || (_translations.xl5h3fOx = tr("Show server info")), + callback: () => { + trigger_close = false; + Modals.openServerInfo(this); + }, + icon_class: "client-about" + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-invite_buddy", + name: _translations.n60We1tw || (_translations.n60We1tw = tr("Invite buddy")), + callback: () => Modals.spawnInviteEditor(this.channelTree.client) + }, { + type: contextmenu.MenuEntryType.HR, + name: '' + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_switch", + name: _translations.xTkdlACq || (_translations.xTkdlACq = tr("Join server text channel")), + callback: () => { + this.channelTree.client.side_bar.channel_conversations().set_current_channel(0); + this.channelTree.client.side_bar.show_channel_conversations(); + }, + visible: !settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT) + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-virtualserver_edit", + name: _translations.dKIgGTUq || (_translations.dKIgGTUq = tr("Edit")), + callback: () => { + Modals.createServerModal(this, properties => { + log.info(LogCategory.SERVER, _translations.zjiyeNdR || (_translations.zjiyeNdR = tr("Changing server properties %o")), properties); + console.log(_translations.simVGxCY || (_translations.simVGxCY = tr("Changed properties: %o")), properties); + if (properties) { + if (Object.keys(properties)) { + return this.channelTree.client.serverConnection.send_command("serveredit", properties).then(() => { + this.channelTree.client.sound.play(Sound.SERVER_EDITED_SELF); + }); + } + } + return Promise.resolve(); + }); + } + }, { + type: contextmenu.MenuEntryType.HR, + visible: true, + name: '' + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-iconviewer", + name: _translations.MKb9CCNC || (_translations.MKb9CCNC = tr("View icons")), + callback: () => Modals.spawnIconSelect(this.channelTree.client) + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: 'client-iconsview', + name: _translations.aUKw1raS || (_translations.aUKw1raS = tr("View avatars")), + visible: false, + callback: () => Modals.spawnAvatarList(this.channelTree.client) + }, contextmenu.Entry.CLOSE(() => trigger_close ? on_close() : {})); + } + updateVariables(is_self_notify, ...variables) { + let group = log.group(log.LogType.DEBUG, LogCategory.SERVER, _translations.CDsTVwgK || (_translations.CDsTVwgK = tr("Update properties (%i)")), variables.length); + { + const entries = []; + for (const variable of variables) + entries.push({ + key: variable.key, + value: variable.value, + type: typeof (this.properties[variable.key]) + }); + log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Server update properties", entries); + } + let update_bannner = false, update_button = false; + for (let variable of variables) { + JSON.map_field_to(this.properties, variable.value, variable.key); + if (variable.key == "virtualserver_name") { + this.htmlTag.find(".name").text(variable.value); + this.channelTree.client.tag_connection_handler.find(".server-name").text(variable.value); + server_connections.update_ui(); + } + else if (variable.key == "virtualserver_icon_id") { + /* For more detail lookup client::updateVariables and client_icon_id! + * ATTENTION: This is required! + */ + this.properties.virtualserver_icon_id = variable.value >>> 0; + const bmarks = bookmarks.bookmarks_flat() + .filter(e => e.server_properties.server_address === this.remote_address.host && e.server_properties.server_port == this.remote_address.port) + .filter(e => e.last_icon_id !== this.properties.virtualserver_icon_id); + if (bmarks.length > 0) { + bmarks.forEach(e => { + e.last_icon_id = this.properties.virtualserver_icon_id; + }); + bookmarks.save_bookmark(); + top_menu.rebuild_bookmarks(); + control_bar.update_bookmarks(); + } + if (this.channelTree.client.fileManager && this.channelTree.client.fileManager.icons) + this.htmlTag.find(".icon_property").replaceWith(this.channelTree.client.fileManager.icons.generateTag(this.properties.virtualserver_icon_id).addClass("icon_property")); + } + else if (variable.key.indexOf('hostbanner') != -1) { + update_bannner = true; + } + else if (variable.key.indexOf('hostbutton') != -1) { + update_button = true; + } + } + if (update_bannner) + this.channelTree.client.hostbanner.update(); + if (update_button) + if (control_bar.current_connection_handler() === this.channelTree.client) + control_bar.apply_server_hostbutton(); + group.end(); + if (is_self_notify && this.info_request_promise_resolve) { + this.info_request_promise_resolve(); + this.info_request_promise = undefined; + this.info_request_promise_reject = undefined; + this.info_request_promise_resolve = undefined; + } + connection_log.update_address_info({ + hostname: this.remote_address.host, + port: this.remote_address.port + }, { + clients_online: this.properties.virtualserver_clientsonline, + clients_total: this.properties.virtualserver_maxclients, + country: this.properties.virtualserver_country_code, + flag_password: this.properties.virtualserver_flag_password, + name: this.properties.virtualserver_name, + icon_id: this.properties.virtualserver_icon_id, + password_hash: undefined /* we've here no clue */ + }); + } + /* this result !must! be cached for at least a second */ + updateProperties() { + if (this.info_request_promise && Date.now() - this.lastInfoRequest < 1000) + return this.info_request_promise; + this.lastInfoRequest = Date.now(); + this.nextInfoRequest = this.lastInfoRequest + 10 * 1000; + this.channelTree.client.serverConnection.send_command("servergetvariables").catch(error => { + this.info_request_promise_reject(error); + this.info_request_promise = undefined; + this.info_request_promise_reject = undefined; + this.info_request_promise_resolve = undefined; + }); + return this.info_request_promise = new Promise((resolve, reject) => { + this.info_request_promise_reject = reject; + this.info_request_promise_resolve = resolve; + }); + } + /* max 1s ago, so we could update every second */ + request_connection_info() { + if (Date.now() - 900 < this._info_connection_promise_timestamp && this._info_connection_promise) + return this._info_connection_promise; + if (this._info_connection_promise_reject) + this._info_connection_promise_resolve("timeout"); + let _local_reject; /* to ensure we're using the right resolve! */ + this._info_connection_promise = new Promise((resolve, reject) => { + this._info_connection_promise_resolve = resolve; + this._info_connection_promise_reject = reject; + _local_reject = reject; + }); + this._info_connection_promise_timestamp = Date.now(); + this.channelTree.client.serverConnection.send_command("serverrequestconnectioninfo", {}, { process_result: false }).catch(error => _local_reject(error)); + return this._info_connection_promise; + } + set_connection_info(info) { + if (!this._info_connection_promise_resolve) + return; + this._info_connection_promise_resolve(info); + this._info_connection_promise_resolve = undefined; + this._info_connection_promise_reject = undefined; + } + shouldUpdateProperties() { + return this.nextInfoRequest < Date.now(); + } + calculateUptime() { + if (this.properties.virtualserver_uptime == 0 || this.lastInfoRequest == 0) + return this.properties.virtualserver_uptime; + return this.properties.virtualserver_uptime + (new Date().getTime() - this.lastInfoRequest) / 1000; + } + set flag_text_unread(flag) { + this._htmlTag.find(".marker-text-unread").toggleClass("hidden", !flag); + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5e6667919c371c71e6b16bc446ba82c5a2073d295e0e7274d5af19bf6357bc7c"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5e6667919c371c71e6b16bc446ba82c5a2073d295e0e7274d5af19bf6357bc7c"] = "5e6667919c371c71e6b16bc446ba82c5a2073d295e0e7274d5af19bf6357bc7c"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "m49fBygK", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (98,46)" }, { name: "wwys9akP", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (241,30)" }, { name: "i7HT3n9l", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (241,58)" }, { name: "CSYw4ecH", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (255,37)" }, { name: "OpoQAmLI", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (255,57)" }, { name: "pLzs3OvD", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (259,30)" }, { name: "I6qHm5Vw", path: "D:/TeaSpeak/web/shared/js/bookmarks.ts (259,62)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var bookmarks; +(function (bookmarks_1) { + function guid() { + function s4() { + return Math + .floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + } + bookmarks_1.boorkmak_connect = (mark, new_tab) => { + const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile(); + if (profile.valid()) { + const connection = (typeof (new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection_handler() : server_connections.spawn_server_connection_handler(); + server_connections.set_active_connection_handler(connection); + connection.startConnection(mark.server_properties.server_address + ":" + mark.server_properties.server_port, profile, true, { + nickname: mark.nickname === "Another TeaSpeak user" || !mark.nickname ? profile.connect_username() : mark.nickname, + password: mark.server_properties.server_password_hash ? { + password: mark.server_properties.server_password_hash, + hashed: true + } : mark.server_properties.server_password ? { + hashed: false, + password: mark.server_properties.server_password + } : undefined + }); + } + else { + Modals.spawnConnectModal({}, { + url: mark.server_properties.server_address + ":" + mark.server_properties.server_port, + enforce: true + }, { + profile: profile, + enforce: true + }); + } + }; + let BookmarkType; + (function (BookmarkType) { + BookmarkType[BookmarkType["ENTRY"] = 0] = "ENTRY"; + BookmarkType[BookmarkType["DIRECTORY"] = 1] = "DIRECTORY"; + })(BookmarkType = bookmarks_1.BookmarkType || (bookmarks_1.BookmarkType = {})); + let _bookmark_config; + function bookmark_config() { + if (_bookmark_config) + return _bookmark_config; + let bookmark_json = localStorage.getItem("bookmarks"); + let bookmarks; + try { + bookmarks = JSON.parse(bookmark_json) || {}; + } + catch (error) { + log.error(LogCategory.BOOKMARKS, _translations.m49fBygK || (_translations.m49fBygK = tr("Failed to load bookmarks: %o")), error); + bookmarks = {}; + } + _bookmark_config = bookmarks; + _bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY }; + if (!_bookmark_config.default_added) { + _bookmark_config.default_added = true; + create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, { + server_address: "ts.teaspeak.de", + server_port: 9987 + }, undefined); + save_config(); + } + const fix_parent = (parent, entry) => { + entry.parent = parent; + if (entry.type === BookmarkType.DIRECTORY) + for (const child of entry.content) + fix_parent(entry, child); + }; + for (const entry of _bookmark_config.root_bookmark.content) + fix_parent(_bookmark_config.root_bookmark, entry); + return _bookmark_config; + } + function save_config() { + localStorage.setItem("bookmarks", JSON.stringify(bookmark_config(), (key, value) => { + if (key === "parent") + return undefined; + return value; + })); + } + function bookmarks() { + return bookmark_config().root_bookmark; + } + bookmarks_1.bookmarks = bookmarks; + function bookmarks_flat() { + const result = []; + const _flat = (bookmark) => { + if (bookmark.type == BookmarkType.DIRECTORY) + for (const book of bookmark.content) + _flat(book); + else + result.push(bookmark); + }; + _flat(bookmark_config().root_bookmark); + return result; + } + bookmarks_1.bookmarks_flat = bookmarks_flat; + function find_bookmark_recursive(parent, uuid) { + for (const entry of parent.content) { + if (entry.unique_id == uuid) + return entry; + if (entry.type == BookmarkType.DIRECTORY) { + const result = find_bookmark_recursive(entry, uuid); + if (result) + return result; + } + } + return undefined; + } + function find_bookmark(uuid) { + return find_bookmark_recursive(bookmarks(), uuid); + } + bookmarks_1.find_bookmark = find_bookmark; + function parent_bookmark(bookmark) { + const books = [bookmarks()]; + while (!books.length) { + const directory = books.pop_front(); + if (directory.type == BookmarkType.DIRECTORY) { + const cast = directory; + if (cast.content.indexOf(bookmark) != -1) + return cast; + books.push(...cast.content); + } + } + return bookmarks(); + } + bookmarks_1.parent_bookmark = parent_bookmark; + function create_bookmark(display_name, directory, server_properties, nickname) { + const bookmark = { + display_name: display_name, + server_properties: server_properties, + nickname: nickname, + type: BookmarkType.ENTRY, + connect_profile: "default", + unique_id: guid(), + parent: directory + }; + directory.content.push(bookmark); + return bookmark; + } + bookmarks_1.create_bookmark = create_bookmark; + function create_bookmark_directory(parent, name) { + const bookmark = { + type: BookmarkType.DIRECTORY, + display_name: name, + content: [], + unique_id: guid(), + parent: parent + }; + parent.content.push(bookmark); + return bookmark; + } + bookmarks_1.create_bookmark_directory = create_bookmark_directory; + //TODO test if the new parent is within the old bookmark + function change_directory(parent, bookmark) { + delete_bookmark(bookmark); + parent.content.push(bookmark); + } + bookmarks_1.change_directory = change_directory; + function save_bookmark(bookmark) { + save_config(); /* nvm we dont give a fuck... saving everything */ + } + bookmarks_1.save_bookmark = save_bookmark; + function delete_bookmark_recursive(parent, bookmark) { + const index = parent.content.indexOf(bookmark); + if (index != -1) + parent.content.remove(bookmark); + else + for (const entry of parent.content) + if (entry.type == BookmarkType.DIRECTORY) + delete_bookmark_recursive(entry, bookmark); + } + function delete_bookmark(bookmark) { + delete_bookmark_recursive(bookmarks(), bookmark); + } + bookmarks_1.delete_bookmark = delete_bookmark; + function add_current_server() { + const ch = server_connections.active_connection_handler(); + if (ch && ch.connected) { + const ce = ch.getClient(); + const name = ce ? ce.clientNickName() : undefined; + createInputModal(_translations.wwys9akP || (_translations.wwys9akP = tr("Enter bookmarks name")), _translations.i7HT3n9l || (_translations.i7HT3n9l = tr("Please enter the bookmarks name:
")), text => text.length > 0, result => { + if (result) { + const bookmark = create_bookmark(result, bookmarks(), { + server_port: ch.serverConnection.remote_address().port, + server_address: ch.serverConnection.remote_address().host, + server_password: "", + server_password_hash: "" + }, name); + save_bookmark(bookmark); + control_bar.update_bookmarks(); + top_menu.rebuild_bookmarks(); + createInfoModal(_translations.CSYw4ecH || (_translations.CSYw4ecH = tr("Server added")), _translations.OpoQAmLI || (_translations.OpoQAmLI = tr("Server has been successfully added to your bookmarks."))).open(); + } + }).open(); + } + else { + createErrorModal(_translations.pLzs3OvD || (_translations.pLzs3OvD = tr("You have to be connected")), _translations.I6qHm5Vw || (_translations.I6qHm5Vw = tr("You have to be connected!"))).open(); + } + } + bookmarks_1.add_current_server = add_current_server; +})(bookmarks || (bookmarks = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["018fe975688693bb1725f14cbed0da456c9f39bf59179b3ef5c42e2f016eafb3"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["018fe975688693bb1725f14cbed0da456c9f39bf59179b3ef5c42e2f016eafb3"] = "018fe975688693bb1725f14cbed0da456c9f39bf59179b3ef5c42e2f016eafb3"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Ng2RgJmg", path: "D:/TeaSpeak/web/shared/js/ui/elements/context_menu.ts (58,27)" }, { name: "RN9mTViM", path: "D:/TeaSpeak/web/shared/js/ui/elements/context_menu.ts (94,31)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var contextmenu; +(function (contextmenu) { + let MenuEntryType; + (function (MenuEntryType) { + MenuEntryType[MenuEntryType["CLOSE"] = 0] = "CLOSE"; + MenuEntryType[MenuEntryType["ENTRY"] = 1] = "ENTRY"; + MenuEntryType[MenuEntryType["CHECKBOX"] = 2] = "CHECKBOX"; + MenuEntryType[MenuEntryType["HR"] = 3] = "HR"; + MenuEntryType[MenuEntryType["SUB_MENU"] = 4] = "SUB_MENU"; + })(MenuEntryType = contextmenu.MenuEntryType || (contextmenu.MenuEntryType = {})); + class Entry { + static HR() { + return { + callback: () => { }, + type: MenuEntryType.HR, + name: "", + icon: "" + }; + } + ; + static CLOSE(callback) { + return { + callback: callback, + type: MenuEntryType.CLOSE, + name: "", + icon: "" + }; + } + } + contextmenu.Entry = Entry; + let provider; + function spawn_context_menu(x, y, ...entries) { + if (!provider) { + console.error(_translations.Ng2RgJmg || (_translations.Ng2RgJmg = tr("Failed to spawn context menu! Missing provider!"))); + return; + } + provider.spawn_context_menu(x, y, ...entries); + } + contextmenu.spawn_context_menu = spawn_context_menu; + function despawn_context_menu() { + if (!provider) + return; + provider.despawn_context_menu(); + } + contextmenu.despawn_context_menu = despawn_context_menu; + function get_provider() { return provider; } + contextmenu.get_provider = get_provider; + function set_provider(_provider) { + provider = _provider; + provider.initialize(); + } + contextmenu.set_provider = set_provider; +})(contextmenu || (contextmenu = {})); +class HTMLContextMenuProvider { + constructor() { + this._close_callbacks = []; + this._visible = false; + } + despawn_context_menu() { + if (!this._visible) + return; + let menu = this._context_menu || (this._context_menu = $(".context-menu")); + menu.animate({ opacity: 0 }, 100, () => menu.css("display", "none")); + this._visible = false; + for (const callback of this._close_callbacks) { + if (typeof (callback) !== "function") { + console.error(_translations.RN9mTViM || (_translations.RN9mTViM = tr("Given close callback is not a function!. Callback: %o")), callback); + continue; + } + callback(); + } + this._close_callbacks = []; + } + finalize() { + $(document).unbind('click', this._global_click_listener); + } + initialize() { + this._global_click_listener = this.on_global_click.bind(this); + $(document).bind('click', this._global_click_listener); + } + on_global_click(event) { + //let menu = this._context_menu || (this._context_menu = $(".context-menu")); + if (!this._visible) + return; + if ($(event.target).parents(".context-menu").length == 0) { + this.despawn_context_menu(); + event.preventDefault(); + } + } + generate_tag(entry) { + if (entry.type == contextmenu.MenuEntryType.HR) { + return $.spawn("hr"); + } + else if (entry.type == contextmenu.MenuEntryType.ENTRY) { + let icon = entry.icon_class; + if (!icon || icon.length == 0) + icon = "icon_empty"; + else + icon = "icon " + icon; + let tag = $.spawn("div").addClass("entry"); + tag.append($.spawn("div").addClass(icon)); + tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name)); + if (entry.disabled || entry.invalidPermission) + tag.addClass("disabled"); + else { + tag.on('click', () => { + if ($.isFunction(entry.callback)) + entry.callback(); + entry.callback = undefined; /* for some reason despawn_context_menu() causes a second click event? */ + this.despawn_context_menu(); + }); + } + return tag; + } + else if (entry.type == contextmenu.MenuEntryType.CHECKBOX) { + let checkbox = $.spawn("label").addClass("ccheckbox"); + $.spawn("input").attr("type", "checkbox").prop("checked", !!entry.checkbox_checked).appendTo(checkbox); + $.spawn("span").addClass("checkmark").appendTo(checkbox); + let tag = $.spawn("div").addClass("entry"); + tag.append(checkbox); + tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name)); + if (entry.disabled || entry.invalidPermission) + tag.addClass("disabled"); + else { + tag.on('click', () => { + if ($.isFunction(entry.callback)) + entry.callback(); + entry.callback = undefined; /* for some reason despawn_context_menu() causes a second click event? */ + this.despawn_context_menu(); + }); + } + return tag; + } + else if (entry.type == contextmenu.MenuEntryType.SUB_MENU) { + let icon = entry.icon_class; + if (!icon || icon.length == 0) + icon = "icon_empty"; + else + icon = "icon " + icon; + let tag = $.spawn("div").addClass("entry").addClass("sub-container"); + tag.append($.spawn("div").addClass(icon)); + tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name)); + tag.append($.spawn("div").addClass("arrow right")); + if (entry.disabled || entry.invalidPermission) + tag.addClass("disabled"); + else { + let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container"); + for (const e of entry.sub_menu) { + if (typeof (entry.visible) === 'boolean' && !entry.visible) + continue; + menu.append(this.generate_tag(e)); + } + menu.appendTo(tag); + } + return tag; + } + return $.spawn("div").text("undefined"); + } + spawn_context_menu(x, y, ...entries) { + this._visible = true; + let menu_tag = this._context_menu || (this._context_menu = $(".context-menu")); + menu_tag.finish().empty().css("opacity", "0"); + const menu_container = $.spawn("div").addClass("context-menu-container"); + this._close_callbacks = []; + for (const entry of entries) { + if (typeof (entry.visible) === 'boolean' && !entry.visible) + continue; + if (entry.type == contextmenu.MenuEntryType.CLOSE) { + if (entry.callback) + this._close_callbacks.push(entry.callback); + } + else + menu_container.append(this.generate_tag(entry)); + } + menu_tag.append(menu_container); + menu_tag.animate({ opacity: 1 }, 100).css("display", "block"); + const width = menu_container.visible_width(); + if (x + width + 5 > window.innerWidth) + menu_container.addClass("left"); + // In the right position (the mouse) + menu_tag.css({ + "top": y + "px", + "left": x + "px" + }); + } + html_format_enabled() { + return true; + } +} +contextmenu.set_provider(new HTMLContextMenuProvider()); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["a1623e51d6c35dbe60874645b6eef1afcb564cc0ae6cd3c8ed09e710029bd1d4"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["a1623e51d6c35dbe60874645b6eef1afcb564cc0ae6cd3c8ed09e710029bd1d4"] = "a1623e51d6c35dbe60874645b6eef1afcb564cc0ae6cd3c8ed09e710029bd1d4"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "X2HpQ_9N", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (7,31)" }, { name: "RinYtSD0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (7,52)" }, { name: "M3qtba3_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (79,56)" }, { name: "XMi8o_cF", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (86,25)" }, { name: "JtIjuu6P", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (187,29)" }, { name: "QNuA03ZM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (423,33)" }, { name: "_AdLCqU0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (434,33)" }, { name: "F527rAOv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (467,33)" }, { name: "ljiyqr1d", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (480,33)" }, { name: "TKSMPptf", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (490,44)" }, { name: "I5FeAjFN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalCreateChannel.ts (506,56)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var Modals; +(function (Modals) { + function createChannelModal(connection, channel, parent, permissions, callback) { + let properties = {}; //The changes properties + const modal = createModal({ + header: channel ? _translations.X2HpQ_9N || (_translations.X2HpQ_9N = tr("Edit channel")) : _translations.RinYtSD0 || (_translations.RinYtSD0 = tr("Create channel")), + body: () => { + const render_properties = {}; + Object.assign(render_properties, channel ? channel.properties : { + channel_flag_maxfamilyclients_unlimited: true, + channel_flag_maxclients_unlimited: true, + }); + render_properties["channel_icon_tab"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); + render_properties["channel_icon_general"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); + render_properties["create"] = !channel; + let template = $("#tmpl_channel_edit").renderTag(render_properties); + /* the tab functionality */ + { + const container_tabs = template.find(".container-advanced"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); + container_tabs.find(".entry").first().trigger('click'); + } + /* Advanced/normal switch */ + { + const input = template.find(".input-advanced-mode"); + const container_mode = template.find(".mode-container"); + const container_advanced = container_mode.find(".container-advanced"); + const container_simple = container_mode.find(".container-simple"); + input.on('change', event => { + const advanced = input.prop("checked"); + settings.changeGlobal(Settings.KEY_CHANNEL_EDIT_ADVANCED, advanced); + container_mode.css("overflow", "hidden"); + container_advanced.show().toggleClass("hidden", !advanced); + container_simple.show().toggleClass("hidden", advanced); + setTimeout(() => { + container_advanced.toggle(advanced); + container_simple.toggle(!advanced); + container_mode.css("overflow", "visible"); + }, 300); + }).prop("checked", settings.static_global(Settings.KEY_CHANNEL_EDIT_ADVANCED)).trigger('change'); + } + return template.tabify().children(); /* the "render" div */ + }, + footer: null, + width: 500 + }); + modal.htmlTag.find(".modal-body").addClass("modal-channel modal-blue"); + applyGeneralListener(connection, properties, modal.htmlTag.find(".container-general"), modal.htmlTag.find(".button_ok"), channel); + applyStandardListener(connection, properties, modal.htmlTag.find(".container-standard"), modal.htmlTag.find(".container-simple"), parent, channel); + applyPermissionListener(connection, properties, modal.htmlTag.find(".container-permissions"), modal.htmlTag.find(".button_ok"), permissions, channel); + applyAudioListener(connection, properties, modal.htmlTag.find(".container-audio"), modal.htmlTag.find(".container-simple"), channel); + applyAdvancedListener(connection, properties, modal.htmlTag.find(".container-misc"), modal.htmlTag.find(".button_ok"), channel); + let updated = []; + modal.htmlTag.find(".button_ok").click(() => { + modal.htmlTag.find(".container-permissions").find("input[permission]").each((index, _element) => { + let element = $(_element); + if (element.val() == element.attr("original-value")) + return; + let permission = permissions.resolveInfo(element.attr("permission")); + if (!permission) { + log.error(LogCategory.PERMISSIONS, _translations.M3qtba3_ || (_translations.M3qtba3_ = tr("Failed to resolve channel permission for name %o")), element.attr("permission")); + element.prop("disabled", true); + return; + } + updated.push(new PermissionValue(permission, element.val())); + }); + console.log(_translations.XMi8o_cF || (_translations.XMi8o_cF = tr("Updated permissions %o")), updated); + }).click(() => { + modal.close(); + for (const key of Object.keys(channel ? channel.properties : {})) + if (channel.properties[key] == properties[key]) + delete properties[key]; + callback(properties, updated); //First may create the channel + }); + tooltip(modal.htmlTag); + modal.htmlTag.find(".button_cancel").click(() => { + modal.close(); + callback(); + }); + modal.open(); + if (!channel) + modal.htmlTag.find(".channel_name").focus(); + } + Modals.createChannelModal = createChannelModal; + function applyGeneralListener(connection, properties, tag, button, channel) { + let updateButton = () => { + const status = tag.find(".input_error").length != 0; + console.log("Disabled: %o", status); + button.prop("disabled", status); + }; + { + const channel_name = tag.find(".channel_name"); + tag.find(".channel_name").on('change keyup', function () { + properties.channel_name = this.value; + channel_name.toggleClass("input_error", this.value.length < 1 || this.value.length > 40); + updateButton(); + }).prop("disabled", channel && !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1)); + } + tag.find(".button-select-icon").on('click', event => { + Modals.spawnIconSelect(connection, id => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(connection.fileManager.icons.generateTag(id)); + console.log("Selected icon ID: %d", id); + properties.channel_icon_id = id; + }, channel ? channel.properties.channel_icon_id : 0); + }); + tag.find(".button-icon-remove").on('click', event => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(connection.fileManager.icons.generateTag(0)); + console.log("Remove channel icon"); + properties.channel_icon_id = 0; + }); + { + const channel_password = tag.find(".channel_password"); + tag.find(".channel_password").change(function () { + properties.channel_flag_password = this.value.length != 0; + if (properties.channel_flag_password) + helpers.hashPassword(this.value).then(pass => properties.channel_password = pass); + channel_password.removeClass("input_error"); + if (!properties.channel_flag_password) + if (connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1)) + channel_password.addClass("input_error"); + updateButton(); + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD : PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1)); + } + tag.find(".channel_topic").change(function () { + properties.channel_topic = this.value; + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_TOPIC : PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1)); + { + const container = tag.find(".container-description"); + const input = container.find("textarea"); + const insert_tag = (open, close) => { + if (input.prop("disabled")) + return; + const node = input[0]; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; + } + else { + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; + } + input.focus().trigger('change'); + }; + input.on('change', event => { + console.log(_translations.JtIjuu6P || (_translations.JtIjuu6P = tr("Channel description edited: %o")), input.val()); + properties.channel_description = input.val(); + }); + container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container.find(".button-color input").on('change', event => { + insert_tag('[color=' + event.target.value + ']', '[/color]'); + }); + } + tag.find(".channel_description").change(function () { + properties.channel_description = this.value; + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION : PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1)); + if (!channel) { + setTimeout(() => { + tag.find(".channel_name").trigger("change"); + tag.find(".channel_password").trigger('change'); + }, 0); + } + } + function applyStandardListener(connection, properties, tag, simple, parent, channel) { + /* Channel type */ + { + const input_advanced_type = tag.find("input[name='channel_type']"); + let _in_update = false; + const update_simple_type = () => { + if (_in_update) + return; + let type; + if (properties.channel_flag_default || (typeof (properties.channel_flag_default) === "undefined" && channel && channel.properties.channel_flag_default)) + type = "def"; + else if (properties.channel_flag_permanent || (typeof (properties.channel_flag_permanent) === "undefined" && channel && channel.properties.channel_flag_permanent)) + type = "perm"; + else if (properties.channel_flag_semi_permanent || (typeof (properties.channel_flag_semi_permanent) === "undefined" && channel && channel.properties.channel_flag_semi_permanent)) + type = "semi"; + else + type = "temp"; + simple.find("option[name='channel-type'][value='" + type + "']").prop("selected", true); + }; + input_advanced_type.on('change', event => { + const value = [...input_advanced_type].find(e => e.checked).value; + switch (value) { + case "semi": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = true; + break; + case "perm": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; + break; + default: + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = false; + break; + } + update_simple_type(); + }); + const permission_temp = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_TEMPORARY : PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1); + const permission_semi = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1); + const permission_perm = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1); + const permission_default = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) && + connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT : PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1); + /* advanced type listeners */ + const container_types = tag.find(".container-channel-type"); + const tag_type_temp = container_types.find(".type-temp"); + const tag_type_semi = container_types.find(".type-semi"); + const tag_type_perm = container_types.find(".type-perm"); + const select_default = tag.find(".input-flag-default"); + { + select_default.on('change', event => { + const node = select_default[0]; + properties.channel_flag_default = node.checked; + if (node.checked) + tag_type_perm.find("input").prop("checked", true); + tag_type_temp + .toggleClass("disabled", node.checked || !permission_temp) + .find("input").prop("disabled", node.checked || !permission_temp); + tag_type_semi + .toggleClass("disabled", node.checked || !permission_semi) + .find("input").prop("disabled", node.checked || !permission_semi); + tag_type_perm + .toggleClass("disabled", node.checked || !permission_perm) + .find("input").prop("disabled", node.checked || !permission_perm); + update_simple_type(); + }).prop("disabled", !permission_default).trigger('change').parent().toggleClass("disabled", !permission_default); + } + /* simple */ + { + simple.find("option[name='channel-type'][value='def']").prop("disabled", !permission_default); + simple.find("option[name='channel-type'][value='perm']").prop("disabled", !permission_perm); + simple.find("option[name='channel-type'][value='semi']").prop("disabled", !permission_semi); + simple.find("option[name='channel-type'][value='temp']").prop("disabled", !permission_temp); + simple.find("select[name='channel-type']").on('change', event => { + try { + _in_update = true; + switch (event.target.value) { + case "temp": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_temp.trigger('click'); + break; + case "semi": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = true; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_semi.trigger('click'); + break; + case "perm": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_perm.trigger('click'); + break; + case "def": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = true; + select_default.prop("checked", true).trigger('change'); + break; + } + } + finally { + _in_update = false; + /* We dont need to update the simple type because we changed the advanced part to the just changed simple part */ + //update_simple_type(); + } + }); + } + /* init */ + setTimeout(() => { + if (!channel) { + if (permission_perm) + tag_type_perm.find("input").trigger('click'); + else if (permission_semi) + tag_type_semi.find("input").trigger('click'); + else + tag_type_temp.find("input").trigger('click'); + } + else { + if (channel.properties.channel_flag_permanent) + tag_type_perm.find("input").trigger('click'); + else if (channel.properties.channel_flag_semi_permanent) + tag_type_semi.find("input").trigger('click'); + else + tag_type_temp.find("input").trigger('click'); + } + }, 0); + } + /* Talk power */ + { + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER : PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1); + const input_advanced = tag.find("input[name='talk_power']").prop("disabled", !permission); + const input_simple = simple.find("input[name='talk_power']").prop("disabled", !permission); + input_advanced.on('change', event => { + properties.channel_needed_talk_power = parseInt(input_advanced.val()); + input_simple.val(input_advanced.val()); + }); + input_simple.on('change', event => { + properties.channel_needed_talk_power = parseInt(input_simple.val()); + input_advanced.val(input_simple.val()); + }); + } + /* Channel order */ + { + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER : PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1); + const advanced_order_id = tag.find(".order_id").prop("disabled", !permission); + const simple_order_id = simple.find(".order_id").prop("disabled", !permission); + for (let previous_channel of (parent ? parent.children() : connection.channelTree.rootChannel())) { + let selected = channel && channel.properties.channel_order == previous_channel.channelId; + $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(advanced_order_id); + $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(simple_order_id); + } + advanced_order_id.on('change', event => { + simple_order_id[0].selectedIndex = advanced_order_id[0].selectedIndex; + const selected = $(advanced_order_id[0].options.item(advanced_order_id[0].selectedIndex)); + properties.channel_order = parseInt(selected.attr("channelId")); + }); + simple_order_id.on('change', event => { + advanced_order_id[0].selectedIndex = simple_order_id[0].selectedIndex; + const selected = $(simple_order_id[0].options.item(simple_order_id[0].selectedIndex)); + properties.channel_order = parseInt(selected.attr("channelId")); + }); + } + /* Advanced only */ + { + const container_max_users = tag.find(".container-max-users"); + const container_unlimited = container_max_users.find(".container-unlimited"); + const container_limited = container_max_users.find(".container-limited"); + const input_unlimited = container_unlimited.find("input[value='unlimited']"); + const input_limited = container_limited.find("input[value='limited']"); + const input_limit = container_limited.find(".channel_maxclients"); + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); + if (!permission) { + input_unlimited.prop("disabled", true); + input_limited.prop("disabled", true); + input_limit.prop("disabled", true); + container_limited.addClass("disabled"); + container_unlimited.addClass("disabled"); + } + else { + container_max_users.find("input[name='max_users']").on('change', event => { + const node = event.target; + console.log(_translations.QNuA03ZM || (_translations.QNuA03ZM = tr("Channel max user mode: %o")), node.value); + const flag = node.value === "unlimited"; + input_limit + .prop("disabled", flag) + .parent().toggleClass("disabled", flag); + properties.channel_flag_maxclients_unlimited = flag; + }); + input_limit.on('change', event => { + properties.channel_maxclients = parseInt(input_limit.val()); + console.log(_translations._AdLCqU0 || (_translations._AdLCqU0 = tr("Changed max user limit to %o")), properties.channel_maxclients); + }); + setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); + } + } + { + const container_max_users = tag.find(".container-max-family-users"); + const container_unlimited = container_max_users.find(".container-unlimited"); + const container_inherited = container_max_users.find(".container-inherited"); + const container_limited = container_max_users.find(".container-limited"); + const input_unlimited = container_unlimited.find("input[value='unlimited']"); + const input_inherited = container_inherited.find("input[value='inherited']"); + const input_limited = container_limited.find("input[value='limited']"); + const input_limit = container_limited.find(".channel_maxfamilyclients"); + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); + if (!permission) { + input_unlimited.prop("disabled", true); + input_inherited.prop("disabled", true); + input_limited.prop("disabled", true); + input_limit.prop("disabled", true); + container_limited.addClass("disabled"); + container_unlimited.addClass("disabled"); + container_inherited.addClass("disabled"); + } + else { + container_max_users.find("input[name='max_family_users']").on('change', event => { + const node = event.target; + console.log(_translations.F527rAOv || (_translations.F527rAOv = tr("Channel max family user mode: %o")), node.value); + const flag_unlimited = node.value === "unlimited"; + const flag_inherited = node.value === "inherited"; + input_limit + .prop("disabled", flag_unlimited || flag_inherited) + .parent().toggleClass("disabled", flag_unlimited || flag_inherited); + properties.channel_flag_maxfamilyclients_unlimited = flag_unlimited; + properties.channel_flag_maxfamilyclients_inherited = flag_inherited; + }); + input_limit.on('change', event => { + properties.channel_maxfamilyclients = parseInt(input_limit.val()); + console.log(_translations.ljiyqr1d || (_translations.ljiyqr1d = tr("Changed max family user limit to %o")), properties.channel_maxfamilyclients); + }); + setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); + } + } + } + function applyPermissionListener(connection, properties, tag, button, permissions, channel) { + let apply_permissions = (channel_permissions) => { + log.trace(LogCategory.CHANNEL, _translations.TKSMPptf || (_translations.TKSMPptf = tr("Received channel permissions: %o")), channel_permissions); + let required_power = -2; + for (let cperm of channel_permissions) + if (cperm.type.name == PermissionType.I_CHANNEL_NEEDED_MODIFY_POWER) { + required_power = cperm.value; + break; + } + tag.find("input[permission]").each((index, _element) => { + let element = $(_element); + element.attr("original-value", 0); + element.val(0); + let permission = permissions.resolveInfo(element.attr("permission")); + if (!permission) { + log.error(LogCategory.PERMISSIONS, _translations.I5FeAjFN || (_translations.I5FeAjFN = tr("Failed to resolve channel permission for name %o")), element.attr("permission")); + element.prop("disabled", true); + return; + } + for (let cperm of channel_permissions) + if (cperm.type == permission) { + element.val(cperm.value); + element.attr("original-value", cperm.value); + return; + } + }); + const permission = permissions.neededPermission(PermissionType.I_CHANNEL_PERMISSION_MODIFY_POWER).granted(required_power, false); + tag.find("input[permission]").prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); //No permissions + }; + if (channel) { + permissions.requestChannelPermissions(channel.getChannelId()).then(apply_permissions).catch((error) => { + tag.find("input[permission]").prop("disabled", true); + console.log("Failed to receive channel permissions (%o)", error); + }); + } + else + apply_permissions([]); + } + function applyAudioListener(connection, properties, tag, simple, channel) { + const bandwidth_mapping = [ + /* SPEEX narrow */ [2.49, 2.69, 2.93, 3.17, 3.17, 3.56, 3.56, 4.05, 4.05, 4.44, 5.22], + /* SPEEX wide */ [2.69, 2.93, 3.17, 3.42, 3.76, 4.25, 4.74, 5.13, 5.62, 6.40, 7.37], + /* SPEEX ultra */ [2.73, 3.12, 3.37, 3.61, 4.00, 4.49, 4.93, 5.32, 5.81, 6.59, 7.57], + /* CELT */ [6.10, 6.10, 7.08, 7.08, 7.08, 8.06, 8.06, 8.06, 8.06, 10.01, 13.92], + /* Opus Voice */ [2.73, 3.22, 3.71, 4.20, 4.74, 5.22, 5.71, 6.20, 6.74, 7.23, 7.71], + /* Opus Music */ [3.08, 3.96, 4.83, 5.71, 6.59, 7.47, 8.35, 9.23, 10.11, 10.99, 11.87] + ]; + let update_template = () => { + let codec = properties.channel_codec; + if (!codec && channel) + codec = channel.properties.channel_codec; + if (!codec) + return; + let quality = properties.channel_codec_quality; + if (!quality && channel) + quality = channel.properties.channel_codec_quality; + if (!quality) + return; + let template_name = "custom"; + { + if (codec == 4 && quality == 4) + template_name = "voice_mobile"; + else if (codec == 4 && quality == 6) + template_name = "voice_desktop"; + else if (codec == 5 && quality == 6) + template_name = "music"; + } + tag.find("input[name='voice_template'][value='" + template_name + "']").prop("checked", true); + simple.find("option[name='voice_template'][value='" + template_name + "']").prop("selected", true); + let bandwidth; + if (codec < 0 || codec > bandwidth_mapping.length) + bandwidth = 0; + else + bandwidth = bandwidth_mapping[codec][quality] || 0; /* OOB access results in undefined, but is allowed */ + tag.find(".container-needed-bandwidth").text(bandwidth.toFixed(2) + " KiB/s"); + }; + let change_codec = codec => { + if (properties.channel_codec == codec) + return; + tag.find(".voice_codec option").prop("selected", false).eq(codec).prop("selected", true); + properties.channel_codec = codec; + update_template(); + }; + const container_quality = tag.find(".container-quality"); + const slider_quality = sliderfy(container_quality.find(".container-slider"), { + initial_value: properties.channel_codec_quality || 6, + unit: "", + min_value: 1, + max_value: 10, + step: 1, + value_field: container_quality.find(".container-value") + }); + let change_quality = (quality) => { + if (properties.channel_codec_quality == quality) + return; + properties.channel_codec_quality = quality; + slider_quality.value(quality); + update_template(); + }; + container_quality.find(".container-slider").on('change', event => { + properties.channel_codec_quality = slider_quality.value(); + update_template(); + }); + tag.find("input[name='voice_template']").change(function () { + switch (this.value) { + case "custom": + break; + case "music": + change_codec(5); + change_quality(6); + break; + case "voice_desktop": + change_codec(4); + change_quality(6); + break; + case "voice_mobile": + change_codec(4); + change_quality(4); + break; + } + }); + simple.find("select[name='voice_template']").change(function () { + switch (this.value) { + case "custom": + break; + case "music": + change_codec(5); + change_quality(6); + break; + case "voice_desktop": + change_codec(4); + change_quality(6); + break; + case "voice_mobile": + change_codec(4); + change_quality(4); + break; + } + }); + /* disable not granted templates */ + { + tag.find("input[name='voice_template'][value='voice_mobile']") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + simple.find("option[name='voice_template'][value='voice_mobile']") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + tag.find("input[name='voice_template'][value=\"voice_desktop\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + simple.find("option[name='voice_template'][value=\"voice_desktop\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + tag.find("input[name='voice_template'][value=\"music\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + simple.find("option[name='voice_template'][value=\"music\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + } + let codecs = tag.find(".voice_codec option"); + codecs.eq(0).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8).granted(1)); + codecs.eq(1).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16).granted(1)); + codecs.eq(2).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32).granted(1)); + codecs.eq(3).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48).granted(1)); + codecs.eq(4).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + codecs.eq(5).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + tag.find(".voice_codec").change(function () { + if ($(this.item(this.selectedIndex)).prop("disabled")) + return false; + change_codec(this.selectedIndex); + }); + if (!channel) { + change_codec(4); + change_quality(6); + } + else { + change_codec(channel.properties.channel_codec); + change_quality(channel.properties.channel_codec_quality); + } + update_template(); + } + function applyAdvancedListener(connection, properties, tag, button, channel) { + tag.find(".channel_name_phonetic").change(function () { + properties.channel_topic = this.value; + }); + { + const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1); + tag.find(".channel_delete_delay").change(function () { + properties.channel_delete_delay = parseInt(this.value); + }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); + } + { + tag.find(".button-delete-max").on('click', event => { + const power = connection.permissions.neededPermission(PermissionType.I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY).value; + let value = power == -2 ? 0 : power == -1 ? (7 * 24 * 60 * 60) : power; + tag.find(".channel_delete_delay").val(value).trigger('change'); + }); + } + { + const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1); + tag.find(".channel_codec_is_unencrypted").change(function () { + properties.channel_codec_is_unencrypted = parseInt(this.value) == 0; + }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["d35da4bcda041ba972f8c87abdaa890518d6cde71d37e7f860e12e42688c97ce"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["d35da4bcda041ba972f8c87abdaa890518d6cde71d37e7f860e12e42688c97ce"] = "d35da4bcda041ba972f8c87abdaa890518d6cde71d37e7f860e12e42688c97ce"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "h0T6RPkj", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (129,23)" }, { name: "GFlIOGAL", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (264,27)" }, { name: "UCLC4ivJ", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (351,45)" }, { name: "ZrJ02URc", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (532,21)" }, { name: "HSzvJwwR", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (535,21)" }, { name: "Ody_8yeT", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (545,23)" }, { name: "ATXAwxWL", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (547,38)" }, { name: "vsK8laF1", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (547,58)" }, { name: "jMleGq7P", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (563,19)" }, { name: "fkTEx1Wh", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (578,23)" }, { name: "SlAO2UCb", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (580,38)" }, { name: "beWd2uCp", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (580,71)" }, { name: "Utc4LAwu", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (598,27)" }, { name: "hPN4f_xI", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (600,42)" }, { name: "jhZR81Qh", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (600,74)" }, { name: "Y10NXL7W", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (615,27)" }, { name: "CMX4tuF2", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (641,27)" }, { name: "u_LRSiVw", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (647,90)" }, { name: "O6jNMfCB", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (649,43)" }, { name: "yFR88s_P", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (712,44)" }, { name: "rAA5IByZ", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (716,52)" }, { name: "Fh5FtgZp", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (890,34)" }, { name: "pBoepRGH", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (894,26)" }, { name: "hSPoFqAs", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (911,34)" }, { name: "rbZFNFYt", path: "D:/TeaSpeak/web/shared/js/ui/view.ts (915,26)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/// +/// +/// +/// +/// +/// +class ChannelTree { + constructor(client) { + this.channels = []; + this.clients = []; + this.currently_selected = undefined; + this.currently_selected_context_callback = undefined; + this._tree_detached = false; + this._focused = false; + this._reorder_channels = new Set(); + this.client = client; + this._tag_container = $.spawn("div").addClass("channel-tree-container"); + this._tag_entries = $.spawn("div").addClass("channel-tree"); + this.client_mover = new ClientMover(this); + this.reset(); + if (!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) { + this._tag_container.on("contextmenu", (event) => { + if (event.isDefaultPrevented()) + return; + for (const element of document.elementsFromPoint(event.pageX, event.pageY)) + if (element.classList.contains("channelLine") || element.classList.contains("client")) + return; + event.preventDefault(); + if ($.isArray(this.currently_selected)) { //Multiselect + (this.currently_selected_context_callback || ((_) => null))(event); + } + else { + this.onSelect(undefined); + this.showContextMenu(event.pageX, event.pageY); + } + }); + } + this._tag_container.on('resize', this.handle_resized.bind(this)); + this._listener_document_key = event => this.handle_key_press(event); + this._listener_document_click = event => { + this._focused = false; + let element = event.target; + while (element) { + if (element === this._tag_container[0]) { + this._focused = true; + break; + } + element = element.parentNode; + } + }; + document.addEventListener('click', this._listener_document_click); + document.addEventListener('keydown', this._listener_document_key); + } + tag_tree() { + return this._tag_container; + } + destroy() { + this._listener_document_click && document.removeEventListener('click', this._listener_document_click); + this._listener_document_click = undefined; + this._listener_document_key && document.removeEventListener('keydown', this._listener_document_key); + this._listener_document_key = undefined; + if (this.server) { + this.server.destroy(); + this.server = undefined; + } + this.reset(); /* cleanup channel and clients */ + this.channel_first = undefined; + this.channel_last = undefined; + this._tag_container.remove(); + this.currently_selected = undefined; + this.currently_selected_context_callback = undefined; + } + hide_channel_tree() { + this._tag_entries.detach(); + this._tree_detached = true; + } + show_channel_tree() { + this._tree_detached = false; + this._tag_entries.appendTo(this._tag_container); + this.channels.forEach(e => { + e.recalculate_repetitive_name(); + e.reorderClients(); + }); + } + showContextMenu(x, y, on_close = undefined) { + let channelCreate = this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1) || + this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) || + this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1); + contextmenu.spawn_context_menu(x, y, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_create", + name: _translations.h0T6RPkj || (_translations.h0T6RPkj = tr("Create channel")), + invalidPermission: !channelCreate, + callback: () => this.spawnCreateChannel() + }, contextmenu.Entry.CLOSE(on_close)); + } + initialiseHead(serverName, address) { + if (this.server) { + this.server.destroy(); + this.server = undefined; + } + this.server = new ServerEntry(this, serverName, address); + this.server.htmlTag.appendTo(this._tag_entries); + this.server.initializeListener(); + } + __deleteAnimation(element) { + let tag = element instanceof ChannelEntry ? element.rootTag() : element.tag; + tag.fadeOut("slow", () => { + tag.detach(); + element.destroy(); + }); + } + rootChannel() { + return this.channels.filter(e => e.parent == undefined); + } + deleteChannel(channel) { + const _this = this; + for (let index = 0; index < this.channels.length; index++) { + let entry = this.channels[index]; + let currentEntry = this.channels[index]; + while (currentEntry != undefined && currentEntry != null) { + if (currentEntry == channel) { + _this.channels.remove(entry); + _this.__deleteAnimation(entry); + entry.channelTree = null; + index--; + break; + } + else + currentEntry = currentEntry.parent_channel(); + } + } + this.channels.remove(channel); + this.__deleteAnimation(channel); + channel.channelTree = null; + if (channel.channel_previous) + channel.channel_previous.channel_next = channel.channel_next; + if (channel.channel_next) + channel.channel_next.channel_previous = channel.channel_previous; + if (channel == this.channel_first) + this.channel_first = channel.channel_next; + if (channel == this.channel_last) + this.channel_last = channel.channel_previous; + } + insertChannel(channel) { + channel.channelTree = this; + this.channels.push(channel); + let elm = undefined; + let tag = this._tag_entries; + let previous_channel = null; + if (channel.hasParent()) { + let parent = channel.parent_channel(); + let siblings = parent.children(); + if (siblings.length == 0) { + elm = parent.rootTag(); + previous_channel = null; + } + else { + previous_channel = siblings.last(); + elm = previous_channel.tag; + } + tag = parent.siblingTag(); + } + else { + previous_channel = this.channel_last; + if (!this.channel_last) + this.channel_last = channel; + if (!this.channel_first) + this.channel_first = channel; + } + channel.channel_previous = previous_channel; + channel.channel_next = undefined; + if (previous_channel) { + channel.channel_next = previous_channel.channel_next; + previous_channel.channel_next = channel; + if (channel.channel_next) + channel.channel_next.channel_previous = channel; + } + let entry = channel.rootTag(); + if (!this._tree_detached) + entry.css({ display: "none" }).fadeIn("slow"); + entry.appendTo(tag); + if (elm != undefined) + elm.after(entry); + if (channel.channel_previous == channel) /* shall never happen */ + channel.channel_previous = undefined; + if (channel.channel_next == channel) /* shall never happen */ + channel.channel_next = undefined; + channel.initializeListener(); + channel.update_family_index(); + } + findChannel(channelId) { + for (let index = 0; index < this.channels.length; index++) + if (this.channels[index].getChannelId() == channelId) + return this.channels[index]; + return undefined; + } + find_channel_by_name(name, parent, force_parent = true) { + for (let index = 0; index < this.channels.length; index++) + if (this.channels[index].channelName() == name && (!force_parent || parent == this.channels[index].parent)) + return this.channels[index]; + return undefined; + } + moveChannel(channel, channel_previous, parent) { + if (channel_previous != null && channel_previous.parent != parent) { + console.error(_translations.GFlIOGAL || (_translations.GFlIOGAL = tr("Invalid channel move (different parents! (%o|%o)")), channel_previous.parent, parent); + return; + } + if (channel.channel_next) + channel.channel_next.channel_previous = channel.channel_previous; + if (channel.channel_previous) + channel.channel_previous.channel_next = channel.channel_next; + if (channel == this.channel_last) + this.channel_last = channel.channel_previous; + if (channel == this.channel_first) + this.channel_first = channel.channel_next; + channel.channel_next = undefined; + channel.channel_previous = channel_previous; + channel.parent = parent; + if (channel_previous) { + if (channel_previous == this.channel_last) + this.channel_last = channel; + channel.channel_next = channel_previous.channel_next; + channel_previous.channel_next = channel; + channel_previous.rootTag().after(channel.rootTag()); + if (channel.channel_next) + channel.channel_next.channel_previous = channel; + } + else { + if (parent) { + let children = parent.children(); + if (children.length <= 1) { //Self should be already in there + let left = channel.rootTag(); + left.appendTo(parent.siblingTag()); + channel.channel_next = undefined; + } + else { + channel.channel_previous = undefined; + channel.rootTag().prependTo(parent.siblingTag()); + channel.channel_next = children[1]; /* children 0 shall be the channel itself */ + channel.channel_next.channel_previous = channel; + } + } + else { + this._tag_entries.find(".server").after(channel.rootTag()); + channel.channel_next = this.channel_first; + if (this.channel_first) + this.channel_first.channel_previous = channel; + this.channel_first = channel; + } + } + channel.update_family_index(); + channel.children(true).forEach(e => e.update_family_index()); + channel.clients(true).forEach(e => e.update_family_index()); + if (channel.channel_previous == channel) { /* shall never happen */ + channel.channel_previous = undefined; + debugger; + } + if (channel.channel_next == channel) { /* shall never happen */ + channel.channel_next = undefined; + debugger; + } + } + deleteClient(client, animate_tag) { + const old_channel = client.currentChannel(); + this.clients.remove(client); + if (typeof (animate_tag) !== "boolean" || animate_tag) + this.__deleteAnimation(client); + else + client.tag.detach(); + client.onDelete(); + if (old_channel) { + this.client.side_bar.info_frame().update_channel_client_count(old_channel); + } + const voice_connection = this.client.serverConnection.voice_connection(); + if (client.get_audio_handle()) { + if (!voice_connection) { + log.warn(LogCategory.VOICE, _translations.UCLC4ivJ || (_translations.UCLC4ivJ = tr("Deleting client with a voice handle, but we haven't a voice connection!"))); + } + else { + voice_connection.unregister_client(client.get_audio_handle()); + } + } + client.set_audio_handle(undefined); + } + registerClient(client) { + this.clients.push(client); + client.channelTree = this; + const voice_connection = this.client.serverConnection.voice_connection(); + if (voice_connection) + client.set_audio_handle(voice_connection.register_client(client.clientId())); + } + unregisterClient(client) { + if (!this.clients.remove(client)) + return; + client.tree_unregistered(); + } + insertClient(client, channel) { + let newClient = this.findClient(client.clientId()); + if (newClient) + client = newClient; //Got new client :) + else { + this.registerClient(client); + } + client["_channel"] = channel; + let tag = client.tag; + if (!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY) + client.tag.hide(); + else if (!this._tree_detached) + tag.css("display", "none").fadeIn("slow"); + tag.appendTo(channel.clientTag()); + channel.reorderClients(); + /* schedule a reorder for this channel. */ + this._reorder_channels.add(client.currentChannel()); + if (!this._update_timer) { + this._update_timer = setTimeout(() => { + this._update_timer = undefined; + for (const channel of this._reorder_channels) { + channel.updateChannelTypeIcon(); + this.client.side_bar.info_frame().update_channel_client_count(channel); + } + this._reorder_channels.clear(); + }, 5); + } + client.update_family_index(); /* why the hell is this here?! */ + return client; + } + moveClient(client, channel) { + let oldChannel = client.currentChannel(); + client["_channel"] = channel; + let tag = client.tag; + tag.detach(); + tag.appendTo(client.currentChannel().clientTag()); + if (oldChannel) { + oldChannel.updateChannelTypeIcon(); + this.client.side_bar.info_frame().update_channel_client_count(oldChannel); + } + if (channel) { + channel.reorderClients(); + channel.updateChannelTypeIcon(); + this.client.side_bar.info_frame().update_channel_client_count(channel); + } + client.updateClientStatusIcons(); + client.update_family_index(); + } + findClient(clientId) { + for (let index = 0; index < this.clients.length; index++) { + if (this.clients[index].clientId() == clientId) + return this.clients[index]; + } + return undefined; + } + find_client_by_dbid(client_dbid) { + for (let index = 0; index < this.clients.length; index++) { + if (this.clients[index].properties.client_database_id == client_dbid) + return this.clients[index]; + } + return undefined; + } + find_client_by_unique_id(unique_id) { + for (let index = 0; index < this.clients.length; index++) { + if (this.clients[index].properties.client_unique_identifier == unique_id) + return this.clients[index]; + } + return undefined; + } + static same_selected_type(a, b) { + if (a instanceof ChannelEntry) + return b instanceof ChannelEntry; + if (a instanceof ClientEntry) + return b instanceof ClientEntry; + if (a instanceof ServerEntry) + return b instanceof ServerEntry; + return a == b; + } + onSelect(entry, enforce_single, flag_shift) { + if (this.currently_selected && (ppt.key_pressed(ppt.SpecialKey.SHIFT) || flag_shift) && entry instanceof ClientEntry) { //Currently we're only supporting client multiselects :D + if (!entry) + return; //Nowhere + if ($.isArray(this.currently_selected)) { + if (!ChannelTree.same_selected_type(this.currently_selected[0], entry)) + return; //Not the same type + } + else if (ChannelTree.same_selected_type(this.currently_selected, entry)) { + this.currently_selected = [this.currently_selected]; + } + if (entry instanceof ChannelEntry) + this.currently_selected_context_callback = this.callback_multiselect_channel.bind(this); + if (entry instanceof ClientEntry) + this.currently_selected_context_callback = this.callback_multiselect_client.bind(this); + } + else + this.currently_selected = undefined; + if (!$.isArray(this.currently_selected) || enforce_single) { + this.currently_selected = entry; + this._tag_entries.find(".selected").each(function (idx, e) { + $(e).removeClass("selected"); + }); + } + else { + for (const e of this.currently_selected) + if (e == entry) { + this.currently_selected.remove(e); + if (entry instanceof ChannelEntry) + entry.channelTag().removeClass("selected"); + else if (entry instanceof ClientEntry) + entry.tag.removeClass("selected"); + else if (entry instanceof ServerEntry) + entry.htmlTag.removeClass("selected"); + if (this.currently_selected.length == 1) + this.currently_selected = this.currently_selected[0]; + else if (this.currently_selected.length == 0) + this.currently_selected = undefined; + //Already selected + return; + } + this.currently_selected.push(entry); + } + if (entry instanceof ChannelEntry) + entry.channelTag().addClass("selected"); + else if (entry instanceof ClientEntry) + entry.tag.addClass("selected"); + else if (entry instanceof ServerEntry) + entry.htmlTag.addClass("selected"); + if (!$.isArray(this.currently_selected)) { + if (this.currently_selected instanceof ClientEntry && settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)) { + if (this.currently_selected instanceof MusicClientEntry) + this.client.side_bar.show_music_player(this.currently_selected); + else + this.client.side_bar.show_client_info(this.currently_selected); + } + else if (this.currently_selected instanceof ChannelEntry && settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)) { + this.client.side_bar.channel_conversations().set_current_channel(this.currently_selected.channelId); + this.client.side_bar.show_channel_conversations(); + } + else if (this.currently_selected instanceof ServerEntry && settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)) { + this.client.side_bar.channel_conversations().set_current_channel(0); + this.client.side_bar.show_channel_conversations(); + } + } + } + callback_multiselect_channel(event) { + console.log(_translations.ZrJ02URc || (_translations.ZrJ02URc = tr("Multiselect channel"))); + } + callback_multiselect_client(event) { + console.log(_translations.HSzvJwwR || (_translations.HSzvJwwR = tr("Multiselect client"))); + const clients = this.currently_selected; + const music_only = clients.map(e => e instanceof MusicClientEntry ? 0 : 1).reduce((a, b) => a + b, 0) == 0; + const music_entry = clients.map(e => e instanceof MusicClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0; + const local_client = clients.map(e => e instanceof LocalClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0; + let entries = []; + if (!music_entry && !local_client) { //Music bots or local client cant be poked + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-poke", + name: _translations.Ody_8yeT || (_translations.Ody_8yeT = tr("Poke clients")), + callback: () => { + createInputModal(_translations.ATXAwxWL || (_translations.ATXAwxWL = tr("Poke clients")), _translations.vsK8laF1 || (_translations.vsK8laF1 = tr("Poke message:
")), text => true, result => { + if (typeof (result) === "string") { + for (const client of this.currently_selected) + this.client.serverConnection.send_command("clientpoke", { + clid: client.clientId(), + msg: result + }); + } + }, { width: 400, maxLength: 512 }).open(); + } + }); + } + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-move_client_to_own_channel", + name: _translations.jMleGq7P || (_translations.jMleGq7P = tr("Move clients to your channel")), + callback: () => { + const target = this.client.getClient().currentChannel().getChannelId(); + for (const client of clients) + this.client.serverConnection.send_command("clientmove", { + clid: client.clientId(), + cid: target + }); + } + }); + if (!local_client) { //local client cant be kicked and/or banned or kicked + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-kick_channel", + name: _translations.fkTEx1Wh || (_translations.fkTEx1Wh = tr("Kick clients from channel")), + callback: () => { + createInputModal(_translations.SlAO2UCb || (_translations.SlAO2UCb = tr("Kick clients from channel")), _translations.beWd2uCp || (_translations.beWd2uCp = tr("Kick reason:
")), text => true, result => { + if (result) { + for (const client of clients) + this.client.serverConnection.send_command("clientkick", { + clid: client.clientId(), + reasonid: ViewReasonId.VREASON_CHANNEL_KICK, + reasonmsg: result + }); + } + }, { width: 400, maxLength: 255 }).open(); + } + }); + if (!music_entry) { //Music bots cant be banned or kicked + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-kick_server", + name: _translations.Utc4LAwu || (_translations.Utc4LAwu = tr("Kick clients fom server")), + callback: () => { + createInputModal(_translations.hPN4f_xI || (_translations.hPN4f_xI = tr("Kick clients from server")), _translations.jhZR81Qh || (_translations.jhZR81Qh = tr("Kick reason:
")), text => true, result => { + if (result) { + for (const client of clients) + this.client.serverConnection.send_command("clientkick", { + clid: client.clientId(), + reasonid: ViewReasonId.VREASON_SERVER_KICK, + reasonmsg: result + }); + } + }, { width: 400, maxLength: 255 }).open(); + } + }, { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-ban_client", + name: _translations.Y10NXL7W || (_translations.Y10NXL7W = tr("Ban clients")), + invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), + callback: () => { + Modals.spawnBanClient(this.client, (clients).map(entry => { + return { + name: entry.clientNickName(), + unique_id: entry.properties.client_unique_identifier + }; + }), (data) => { + for (const client of clients) + this.client.serverConnection.send_command("banclient", { + uid: client.properties.client_unique_identifier, + banreason: data.reason, + time: data.length + }, { + flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""] + }).then(() => { + this.client.sound.play(Sound.USER_BANNED); + }); + }); + } + }); + } + if (music_only) { + entries.push(contextmenu.Entry.HR()); + entries.push({ + name: _translations.CMX4tuF2 || (_translations.CMX4tuF2 = tr("Delete bots")), + icon_class: "client-delete", + disabled: false, + callback: () => { + const param_string = clients.map((_, index) => "{" + index + "}").join(', '); + const param_values = clients.map(client => client.createChatTag(true)); + const tag = $.spawn("div").append(...MessageHelper.formatMessage((_translations.u_LRSiVw || (_translations.u_LRSiVw = tr("Do you really want to delete "))) + param_string, ...param_values)); + const tag_container = $.spawn("div").append(tag); + Modals.spawnYesNo(_translations.O6jNMfCB || (_translations.O6jNMfCB = tr("Are you sure?")), tag_container, result => { + if (result) { + for (const client of clients) + this.client.serverConnection.send_command("musicbotdelete", { + botid: client.properties.client_database_id + }); + } + }); + }, + type: contextmenu.MenuEntryType.ENTRY + }); + } + } + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + } + clientsByGroup(group) { + let result = []; + for (let client of this.clients) { + if (client.groupAssigned(group)) + result.push(client); + } + return result; + } + clientsByChannel(channel) { + let result = []; + for (let client of this.clients) { + if (client.currentChannel() == channel) + result.push(client); + } + return result; + } + reset() { + const voice_connection = this.client.serverConnection ? this.client.serverConnection.voice_connection() : undefined; + for (const client of this.clients) { + if (client.get_audio_handle() && voice_connection) { + voice_connection.unregister_client(client.get_audio_handle()); + client.set_audio_handle(undefined); + } + client.destroy(); + } + this.clients = []; + for (const channel of this.channels) + channel.destroy(); + this.channels = []; + this._tag_entries.children().detach(); //Dont remove listeners + this.channel_first = undefined; + this.channel_last = undefined; + } + spawnCreateChannel(parent) { + Modals.createChannelModal(this.client, undefined, parent, this.client.permissions, (properties, permissions) => { + if (!properties) + return; + properties["cpid"] = parent ? parent.channelId : 0; + log.debug(LogCategory.CHANNEL, _translations.yFR88s_P || (_translations.yFR88s_P = tr("Creating a new channel.\nProperties: %o\nPermissions: %o")), properties); + this.client.serverConnection.send_command("channelcreate", properties).then(() => { + let channel = this.find_channel_by_name(properties.channel_name, parent, true); + if (!channel) { + log.error(LogCategory.CHANNEL, _translations.rAA5IByZ || (_translations.rAA5IByZ = tr("Failed to resolve channel after creation. Could not apply permissions!"))); + return; + } + if (permissions && permissions.length > 0) { + let perms = []; + for (let perm of permissions) { + perms.push({ + permvalue: perm.value, + permnegated: false, + permskip: false, + permid: perm.type.id + }); + } + perms[0]["cid"] = channel.channelId; + return this.client.serverConnection.send_command("channeladdperm", perms, { + flagset: ["continueonerror"] + }).then(() => new Promise(resolve => { resolve(channel); })); + } + return new Promise(resolve => { resolve(channel); }); + }).then(channel => { + this.client.log.log(log.server.Type.CHANNEL_CREATE, { + channel: channel.log_data(), + creator: this.client.getClient().log_data(), + own_action: true + }); + this.client.sound.play(Sound.CHANNEL_CREATED); + }); + }); + } + handle_resized() { + for (let channel of this.channels) + channel.handle_frame_resized(); + } + select_next_channel(channel, select_client) { + if (select_client) { + const clients = channel.clients_ordered(); + if (clients.length > 0) { + this.onSelect(clients[0], true); + return; + } + } + const children = channel.children(); + if (children.length > 0) { + this.onSelect(children[0], true); + return; + } + const next = channel.channel_next; + if (next) { + this.onSelect(next, true); + return; + } + let parent = channel.parent_channel(); + while (parent) { + const p_next = parent.channel_next; + if (p_next) { + this.onSelect(p_next, true); + return; + } + parent = parent.parent_channel(); + } + } + handle_key_press(event) { + //console.log("Keydown: %o | %o | %o", this._focused, this.currently_selected, Array.isArray(this.currently_selected)); + if (!this._focused || !this.currently_selected || Array.isArray(this.currently_selected)) + return; + if (event.keyCode == KeyCode.KEY_UP) { + event.preventDefault(); + if (this.currently_selected instanceof ChannelEntry) { + let previous = this.currently_selected.channel_previous; + if (previous) { + while (true) { + const siblings = previous.children(); + if (siblings.length == 0) + break; + previous = siblings.last(); + } + const clients = previous.clients_ordered(); + if (clients.length > 0) { + this.onSelect(clients.last(), true); + return; + } + else { + this.onSelect(previous, true); + return; + } + } + else if (this.currently_selected.hasParent()) { + const channel = this.currently_selected.parent_channel(); + const clients = channel.clients_ordered(); + if (clients.length > 0) { + this.onSelect(clients.last(), true); + return; + } + else { + this.onSelect(channel, true); + return; + } + } + else + this.onSelect(this.server, true); + } + else if (this.currently_selected instanceof ClientEntry) { + const channel = this.currently_selected.currentChannel(); + const clients = channel.clients_ordered(); + const index = clients.indexOf(this.currently_selected); + if (index > 0) { + this.onSelect(clients[index - 1], true); + return; + } + this.onSelect(channel, true); + return; + } + } + else if (event.keyCode == KeyCode.KEY_DOWN) { + event.preventDefault(); + if (this.currently_selected instanceof ChannelEntry) { + this.select_next_channel(this.currently_selected, true); + } + else if (this.currently_selected instanceof ClientEntry) { + const channel = this.currently_selected.currentChannel(); + const clients = channel.clients_ordered(); + const index = clients.indexOf(this.currently_selected); + if (index + 1 < clients.length) { + this.onSelect(clients[index + 1], true); + return; + } + this.select_next_channel(channel, false); + } + else if (this.currently_selected instanceof ServerEntry) + this.onSelect(this.channel_first, true); + } + else if (event.keyCode == KeyCode.KEY_RETURN) { + if (this.currently_selected instanceof ChannelEntry) { + this.currently_selected.joinChannel(); + } + } + } + toggle_server_queries(flag) { + if (this._show_queries == flag) + return; + this._show_queries = flag; + const channels = []; + for (const client of this.clients) + if (client.properties.client_type == ClientType.CLIENT_QUERY) { + if (this._show_queries) + client.tag.show(); + else + client.tag.hide(); + if (channels.indexOf(client.currentChannel()) == -1) + channels.push(client.currentChannel()); + } + } + get_first_channel() { + return this.channel_first; + } + unsubscribe_all_channels(subscribe_specified) { + if (!this.client.serverConnection || !this.client.serverConnection.connected()) + return; + this.client.serverConnection.send_command('channelunsubscribeall').then(() => { + const channels = []; + for (const channel of this.channels) { + if (channel.subscribe_mode == ChannelSubscribeMode.SUBSCRIBED) + channels.push(channel.getChannelId()); + } + if (channels.length > 0) { + this.client.serverConnection.send_command('channelsubscribe', channels.map(e => { return { cid: e }; })).catch(error => { + console.warn(_translations.Fh5FtgZp || (_translations.Fh5FtgZp = tr("Failed to subscribe to specific channels (%o)")), channels); + }); + } + }).catch(error => { + console.warn(_translations.pBoepRGH || (_translations.pBoepRGH = tr("Failed to unsubscribe to all channels! (%o)")), error); + }); + } + subscribe_all_channels() { + if (!this.client.serverConnection || !this.client.serverConnection.connected()) + return; + this.client.serverConnection.send_command('channelsubscribeall').then(() => { + const channels = []; + for (const channel of this.channels) { + if (channel.subscribe_mode == ChannelSubscribeMode.UNSUBSCRIBED) + channels.push(channel.getChannelId()); + } + if (channels.length > 0) { + this.client.serverConnection.send_command('channelunsubscribe', channels.map(e => { return { cid: e }; })).catch(error => { + console.warn(_translations.hSPoFqAs || (_translations.hSPoFqAs = tr("Failed to unsubscribe to specific channels (%o)")), channels); + }); + } + }).catch(error => { + console.warn(_translations.rbZFNFYt || (_translations.rbZFNFYt = tr("Failed to subscribe to all channels! (%o)")), error); + }); + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["aab7a9e827e45917f2b7350a68a622a37c8a253931f6051bba900999e332204d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["aab7a9e827e45917f2b7350a68a622a37c8a253931f6051bba900999e332204d"] = "aab7a9e827e45917f2b7350a68a622a37c8a253931f6051bba900999e332204d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Li5Gw9CA", path: "D:/TeaSpeak/web/shared/js/settings.ts (379,44)" }, { name: "uS6cXSmH", path: "D:/TeaSpeak/web/shared/js/settings.ts (382,34)" }, { name: "u4KNLKf0", path: "D:/TeaSpeak/web/shared/js/settings.ts (382,72)" }, { name: "F6RUQNLg", path: "D:/TeaSpeak/web/shared/js/settings.ts (494,48)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +//Used by CertAccept popup +if (typeof (customElements) !== "undefined") { + try { + class X_Properties extends HTMLElement { + } + class X_Property extends HTMLElement { + } + customElements.define('x-properties', X_Properties, { extends: 'div' }); + customElements.define('x-property', X_Property, { extends: 'div' }); + } + catch (error) { + console.warn("failed to define costum elements"); + } +} +class SettingsBase { + static transformStO(input, _default, default_type) { + default_type = default_type || typeof _default; + if (typeof input === "undefined") + return _default; + if (default_type === "string") + return input; + else if (default_type === "number") + return parseInt(input); + else if (default_type === "boolean") + return (input == "1" || input == "true"); + else if (default_type === "undefined") + return input; + return JSON.parse(input); + } + static transformOtS(input) { + if (typeof input === "string") + return input; + else if (typeof input === "number") + return input.toString(); + else if (typeof input === "boolean") + return input ? "1" : "0"; + else if (typeof input === "undefined") + return undefined; + return JSON.stringify(input); + } + static resolveKey(key, _default, resolver, default_type) { + let value = resolver(key.key); + if (!value) { + /* trying fallbacks */ + for (const fallback of key.fallback_keys || []) { + value = resolver(fallback); + if (typeof (value) === "string") { + /* fallback key succeeded */ + const importer = (key.fallback_imports || {})[fallback]; + if (importer) + return importer(value); + break; + } + } + } + if (typeof (value) !== 'string') + return _default; + return SettingsBase.transformStO(value, _default, default_type); + } + static keyify(key) { + if (typeof (key) === "string") + return { key: key }; + if (typeof (key) === "object" && key.key) + return key; + throw "key is not a key"; + } +} +SettingsBase.UPDATE_DIRECT = true; +class StaticSettings extends SettingsBase { + constructor(_reserved = undefined) { + super(); + if (_reserved && !StaticSettings._instance) { + this._staticPropsTag = $("#properties"); + this.initializeStatic(); + } + else { + this._handle = StaticSettings.instance; + } + } + static get instance() { + if (!this._instance) + this._instance = new StaticSettings(true); + return this._instance; + } + initializeStatic() { + let search; + if (window.opener && window.opener !== window) { + search = new URL(window.location.href).search; + } + else { + search = location.search; + } + search.substr(1).split("&").forEach(part => { + let item = part.split("="); + $("") + .attr("key", item[0]) + .attr("value", item[1]) + .appendTo(this._staticPropsTag); + }); + } + static(key, _default, default_type) { + if (this._handle) + return this._handle.static(key, _default, default_type); + key = StaticSettings.keyify(key); + return StaticSettings.resolveKey(key, _default, key => { + let result = this._staticPropsTag.find("[key='" + key + "']"); + if (result.length > 0) + return decodeURIComponent(result.last().attr('value')); + return false; + }, default_type); + } + deleteStatic(key) { + if (this._handle) { + this._handle.deleteStatic(key); + return; + } + key = StaticSettings.keyify(key); + let result = this._staticPropsTag.find("[key='" + key.key + "']"); + if (result.length != 0) + result.detach(); + } +} +class Settings extends StaticSettings { + constructor() { + super(); + this.cacheGlobal = {}; + this.updated = false; + const json = localStorage.getItem("settings.global"); + try { + this.cacheGlobal = JSON.parse(json); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.Li5Gw9CA || (_translations.Li5Gw9CA = tr("Failed to load global settings!\nJson: %s\nError: %o")), json, error); + const show_popup = () => { + createErrorModal(_translations.uS6cXSmH || (_translations.uS6cXSmH = tr("Failed to load global settings")), _translations.u4KNLKf0 || (_translations.u4KNLKf0 = tr("Failed to load global client settings!\nLookup console for more information."))).open(); + }; + if (!loader.finished()) + loader.register_task(loader.Stage.LOADED, { + priority: 0, + name: "Settings error", + function: () => __awaiter(this, void 0, void 0, function* () { return show_popup(); }) + }); + else + show_popup(); + } + if (!this.cacheGlobal) + this.cacheGlobal = {}; + this.saveWorker = setInterval(() => { + if (this.updated) + this.save(); + }, 5 * 1000); + } + static initialize() { + settings = new Settings(); + } + static_global(key, _default) { + const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; + const default_object = { seed: Math.random() }; + let _static = this.static(key, default_object, typeof _default); + if (_static !== default_object) + return StaticSettings.transformStO(_static, actual_default); + return this.global(key, actual_default); + } + global(key, _default) { + const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; + return StaticSettings.resolveKey(Settings.keyify(key), actual_default, key => this.cacheGlobal[key]); + } + changeGlobal(key, value) { + key = Settings.keyify(key); + if (this.cacheGlobal[key.key] == value) + return; + this.updated = true; + this.cacheGlobal[key.key] = StaticSettings.transformOtS(value); + if (Settings.UPDATE_DIRECT) + this.save(); + } + save() { + this.updated = false; + let global = JSON.stringify(this.cacheGlobal); + localStorage.setItem("settings.global", global); + if (localStorage.save) + localStorage.save(); + } +} +Settings.KEY_USER_IS_NEW = { + key: 'user_is_new_user', + default_value: true +}; +Settings.KEY_DISABLE_COSMETIC_SLOWDOWN = { + key: 'disable_cosmetic_slowdown', + description: 'Disable the cosmetic slowdows in some processes, like icon upload.' +}; +Settings.KEY_DISABLE_CONTEXT_MENU = { + key: 'disableContextMenu', + description: 'Disable the context menu for the channel tree which allows to debug the DOM easier' +}; +Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU = { + key: 'disableGlobalContextMenu', + description: 'Disable the general context menu prevention', + default_value: false +}; +Settings.KEY_DISABLE_UNLOAD_DIALOG = { + key: 'disableUnloadDialog', + description: 'Disables the unload popup on side closing' +}; +Settings.KEY_DISABLE_VOICE = { + key: 'disableVoice', + description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore' +}; +Settings.KEY_DISABLE_MULTI_SESSION = { + key: 'disableMultiSession', + default_value: false, + require_restart: true +}; +Settings.KEY_LOAD_DUMMY_ERROR = { + key: 'dummy_load_error', + description: 'Triggers a loading error at the end of the loading process.' +}; +/* Control bar */ +Settings.KEY_CONTROL_MUTE_INPUT = { + key: 'mute_input' +}; +Settings.KEY_CONTROL_MUTE_OUTPUT = { + key: 'mute_output' +}; +Settings.KEY_CONTROL_SHOW_QUERIES = { + key: 'show_server_queries' +}; +Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL = { + key: 'channel_subscribe_all' +}; +/* Connect parameters */ +Settings.KEY_FLAG_CONNECT_DEFAULT = { + key: 'connect_default' +}; +Settings.KEY_CONNECT_ADDRESS = { + key: 'connect_address' +}; +Settings.KEY_CONNECT_PROFILE = { + key: 'connect_profile', + default_value: 'default' +}; +Settings.KEY_CONNECT_USERNAME = { + key: 'connect_username' +}; +Settings.KEY_CONNECT_PASSWORD = { + key: 'connect_password' +}; +Settings.KEY_FLAG_CONNECT_PASSWORD = { + key: 'connect_password_hashed' +}; +Settings.KEY_CONNECT_HISTORY = { + key: 'connect_history' +}; +Settings.KEY_CONNECT_NO_DNSPROXY = { + key: 'connect_no_dnsproxy', + default_value: false +}; +Settings.KEY_CERTIFICATE_CALLBACK = { + key: 'certificate_callback' +}; +/* sounds */ +Settings.KEY_SOUND_MASTER = { + key: 'audio_master_volume', + default_value: 100 +}; +Settings.KEY_SOUND_MASTER_SOUNDS = { + key: 'audio_master_volume_sounds', + default_value: 100 +}; +Settings.KEY_CHAT_FIXED_TIMESTAMPS = { + key: 'chat_fixed_timestamps', + default_value: false, + description: 'Enables fixed timestamps for chat messages and disabled the updating once (2 seconds ago... etc)' +}; +Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS = { + key: 'chat_colloquial_timestamps', + default_value: true, + description: 'Enabled colloquial timestamp formatting like "Yesterday at ..." or "Today at ..."' +}; +Settings.KEY_CHAT_COLORED_EMOJIES = { + key: 'chat_colored_emojies', + default_value: true, + description: 'Enables colored emojies powered by Twemoji' +}; +Settings.KEY_CHAT_TAG_URLS = { + key: 'chat_tag_urls', + default_value: true, + description: 'Automatically link urls with [url]' +}; +Settings.KEY_CHAT_ENABLE_MARKDOWN = { + key: 'chat_enable_markdown', + default_value: true, + description: 'Enabled markdown chat support.' +}; +Settings.KEY_CHAT_ENABLE_BBCODE = { + key: 'chat_enable_bbcode', + default_value: false, + description: 'Enabled bbcode support in chat.' +}; +Settings.KEY_CHAT_IMAGE_WHITELIST_REGEX = { + key: 'chat_image_whitelist_regex', + default_value: JSON.stringify([]) +}; +Settings.KEY_SWITCH_INSTANT_CHAT = { + key: 'switch_instant_chat', + default_value: true, + description: 'Directly switch to channel chat on channel select' +}; +Settings.KEY_SWITCH_INSTANT_CLIENT = { + key: 'switch_instant_client', + default_value: true, + description: 'Directly switch to client info on client select' +}; +Settings.KEY_HOSTBANNER_BACKGROUND = { + key: 'hostbanner_background', + default_value: false, + description: 'Enables a default background begind the hostbanner' +}; +Settings.KEY_CHANNEL_EDIT_ADVANCED = { + key: 'channel_edit_advanced', + default_value: false, + description: 'Edit channels in advanced mode with a lot more settings' +}; +Settings.KEY_PERMISSIONS_SHOW_ALL = { + key: 'permissions_show_all', + default_value: false, + description: 'Show all permissions even thou they dont make sense for the server/channel group' +}; +Settings.KEY_TEAFORO_URL = { + key: "teaforo_url", + default_value: "https://forum.teaspeak.de/" +}; +Settings.KEY_FONT_SIZE = { + key: "font_size" +}; +Settings.KEY_ICON_SIZE = { + key: "icon_size", + default_value: 100 +}; +Settings.KEY_LAST_INVITE_LINK_TYPE = { + key: "last_invite_link_type", + default_value: "tea-web" +}; +Settings.FN_INVITE_LINK_SETTING = name => { + return { + key: 'invite_link_setting_' + name + }; +}; +Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE = channel => { + return { + key: 'channel_subscribe_mode_' + channel + }; +}; +Settings.FN_PROFILE_RECORD = name => { + return { + key: 'profile_record' + name + }; +}; +Settings.KEYS = (() => { + const result = []; + for (const key in Settings) { + if (!key.toUpperCase().startsWith("KEY_")) + continue; + if (key.toUpperCase() == "KEYS") + continue; + result.push(key); + } + return result; +})(); +class ServerSettings extends SettingsBase { + constructor() { + super(); + this.cacheServer = {}; + this._server_settings_updated = false; + this._destroyed = false; + this._server_save_worker = setInterval(() => { + if (this._server_settings_updated) + this.save(); + }, 5 * 1000); + } + destroy() { + this._destroyed = true; + this._server_unique_id = undefined; + this.cacheServer = undefined; + clearInterval(this._server_save_worker); + this._server_save_worker = undefined; + } + server(key, _default) { + if (this._destroyed) + throw "destroyed"; + return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]); + } + changeServer(key, value) { + if (this._destroyed) + throw "destroyed"; + key = Settings.keyify(key); + if (this.cacheServer[key.key] == value) + return; + this._server_settings_updated = true; + this.cacheServer[key.key] = StaticSettings.transformOtS(value); + if (Settings.UPDATE_DIRECT) + this.save(); + } + setServer(server_unique_id) { + if (this._destroyed) + throw "destroyed"; + if (this._server_unique_id) { + this.save(); + this.cacheServer = {}; + this._server_unique_id = undefined; + } + this._server_unique_id = server_unique_id; + if (this._server_unique_id) { + const json = localStorage.getItem("settings.server_" + server_unique_id); + try { + this.cacheServer = JSON.parse(json); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.F6RUQNLg || (_translations.F6RUQNLg = tr("Failed to load server settings for server %s!\nJson: %s\nError: %o")), server_unique_id, json, error); + } + if (!this.cacheServer) + this.cacheServer = {}; + } + } + save() { + if (this._destroyed) + throw "destroyed"; + this._server_settings_updated = false; + if (this._server_unique_id) { + let server = JSON.stringify(this.cacheServer); + localStorage.setItem("settings.server_" + this._server_unique_id, server); + if (localStorage.save) + localStorage.save(); + } + } +} +let settings; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["9905e0706fb3679c8418586821d6454792687444f209b49eeb1a5af522c90114"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["9905e0706fb3679c8418586821d6454792687444f209b49eeb1a5af522c90114"] = "9905e0706fb3679c8418586821d6454792687444f209b49eeb1a5af522c90114"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Iazp8WAl", path: "D:/TeaSpeak/web/shared/js/connection/ConnectionBase.ts (168,30)" }, { name: "osT4NYsM", path: "D:/TeaSpeak/web/shared/js/connection/ConnectionBase.ts (197,35)" }, { name: "kb_ARAZP", path: "D:/TeaSpeak/web/shared/js/connection/ConnectionBase.ts (209,35)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var connection; +(function (connection_1) { + connection_1.CommandOptionDefaults = { + flagset: [], + process_result: true, + timeout: 1000 + }; + class AbstractServerConnection { + constructor(client) { + this.client = client; + this.command_helper = new connection_1.CommandHelper(this); + } + } + connection_1.AbstractServerConnection = AbstractServerConnection; + let voice; + (function (voice) { + let PlayerState; + (function (PlayerState) { + PlayerState[PlayerState["PREBUFFERING"] = 0] = "PREBUFFERING"; + PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING"; + PlayerState[PlayerState["BUFFERING"] = 2] = "BUFFERING"; + PlayerState[PlayerState["STOPPING"] = 3] = "STOPPING"; + PlayerState[PlayerState["STOPPED"] = 4] = "STOPPED"; + })(PlayerState = voice.PlayerState || (voice.PlayerState = {})); + class AbstractVoiceConnection { + constructor(connection) { + this.connection = connection; + } + } + voice.AbstractVoiceConnection = AbstractVoiceConnection; + })(voice = connection_1.voice || (connection_1.voice = {})); + class ServerCommand { + } + connection_1.ServerCommand = ServerCommand; + class AbstractCommandHandler { + constructor(connection) { + this.volatile_handler_boss = false; /* if true than the command handler could be registered twice to two or more handlers */ + this.ignore_consumed = false; + this.connection = connection; + } + } + connection_1.AbstractCommandHandler = AbstractCommandHandler; + class AbstractCommandHandlerBoss { + constructor(connection) { + this.command_handlers = []; + /* TODO: Timeout */ + this.single_command_handler = []; + this.connection = connection; + } + destroy() { + this.command_handlers = undefined; + this.single_command_handler = undefined; + } + register_handler(handler) { + if (!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; + this.command_handlers.remove(handler); /* just to be sure */ + this.command_handlers.push(handler); + handler.handler_boss = this; + } + unregister_handler(handler) { + if (!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(_translations.Iazp8WAl || (_translations.Iazp8WAl = tr("Tried to unregister command handler which does not belong to the handler boss"))); + return; + } + this.command_handlers.remove(handler); + handler.handler_boss = undefined; + } + register_single_handler(handler) { + this.single_command_handler.push(handler); + } + remove_single_handler(handler) { + this.single_command_handler.remove(handler); + } + handlers() { + return this.command_handlers; + } + invoke_handle(command) { + let flag_consumed = false; + for (const handler of this.command_handlers) { + try { + if (!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } + catch (error) { + console.error(_translations.osT4NYsM || (_translations.osT4NYsM = tr("Failed to invoke command handler. Invocation results in an exception: %o")), error); + } + } + for (const handler of [...this.single_command_handler]) { + if (handler.command && handler.command != command.command) + continue; + try { + if (handler.function(command)) + this.single_command_handler.remove(handler); + } + catch (error) { + console.error(_translations.kb_ARAZP || (_translations.kb_ARAZP = tr("Failed to invoke single command handler. Invocation results in an exception: %o")), error); + } + } + return flag_consumed; + } + } + connection_1.AbstractCommandHandlerBoss = AbstractCommandHandlerBoss; +})(connection || (connection = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["37651e247358097e3a713c020f24649c058880f6fa693a9c81181c5193e5b8ef"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["37651e247358097e3a713c020f24649c058880f6fa693a9c81181c5193e5b8ef"] = "37651e247358097e3a713c020f24649c058880f6fa693a9c81181c5193e5b8ef"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "pCk7xYst", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (98,47)" }, { name: "G_WEzBlb", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (112,59)" }, { name: "y8lxS0mg", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (143,50)" }, { name: "RBhEfyBT", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (163,51)" }, { name: "N3Jb_ujY", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (165,51)" }, { name: "byMDJXBp", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (199,33)" }, { name: "kJ99wZWj", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (228,34)" }, { name: "vSKb8F5P", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (228,61)" }, { name: "z78_SMMr", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (236,45)" }, { name: "smS5MNGV", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (236,70)" }, { name: "yKYk5MrN", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (238,46)" }, { name: "T9XgymAy", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (238,99)" }, { name: "xGqliR_T", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (274,50)" }, { name: "hTYqyg0W", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (290,59)" }, { name: "LZKNb1ud", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (297,55)" }, { name: "m95mid7y", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (329,47)" }, { name: "cO7AsMbZ", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (351,46)" }, { name: "Tr8N0T8J", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (356,55)" }, { name: "tGrW50fT", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (367,46)" }, { name: "tqXi_0qx", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (372,55)" }, { name: "YwXRwg0u", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (442,38)" }, { name: "cJ2nujBf", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (498,55)" }, { name: "NTMrV0pQ", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (551,63)" }, { name: "gEcJmHRi", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (586,51)" }, { name: "rsbbZ5vo", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (591,51)" }, { name: "FUu2Z3p0", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (597,55)" }, { name: "BuZ27Ktg", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (601,25)" }, { name: "BID316jI", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (684,30)" }, { name: "fzc4kFcz", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (694,51)" }, { name: "mF6Ppr3u", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (700,51)" }, { name: "XxAerQnq", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (706,51)" }, { name: "tBW9j_7C", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (719,51)" }, { name: "_E2eMYr4", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (767,55)" }, { name: "kgIHfhjS", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (767,136)" }, { name: "ruiHzDfv", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (767,159)" }, { name: "U0exVRxM", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (871,47)" }, { name: "suQhKZ2M", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (882,51)" }, { name: "BxOvO8Ec", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (938,46)" }, { name: "_5Wlb_tM", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (990,34)" }, { name: "HMeyZ6hh", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1002,34)" }, { name: "bkMX72xe", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1016,50)" }, { name: "Pqgsw3nk", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1056,46)" }, { name: "acswhp6U", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1072,46)" }, { name: "JHeDrma5", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1094,46)" }, { name: "KYFQM8h2", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1118,46)" }, { name: "MX9isGwd", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1132,46)" }, { name: "x8tCYqXl", path: "D:/TeaSpeak/web/shared/js/connection/CommandHandler.ts (1147,46)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var connection; +(function (connection_2) { + class ServerConnectionCommandBoss extends connection_2.AbstractCommandHandlerBoss { + constructor(connection) { + super(connection); + } + } + connection_2.ServerConnectionCommandBoss = ServerConnectionCommandBoss; + class ConnectionCommandHandler extends connection_2.AbstractCommandHandler { + constructor(connection) { + super(connection); + this.connection_handler = connection.client; + this["error"] = this.handleCommandResult; + this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; + this["notifychannelcreated"] = this.handleCommandChannelCreate; + this["notifychanneldeleted"] = this.handleCommandChannelDelete; + this["notifychannelhide"] = this.handleCommandChannelHide; + this["notifychannelshow"] = this.handleCommandChannelShow; + this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; + this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; + this["notifycliententerview"] = this.handleCommandClientEnterView; + this["notifyclientleftview"] = this.handleCommandClientLeftView; + this["notifyclientmoved"] = this.handleNotifyClientMoved; + this["initserver"] = this.handleCommandServerInit; + this["notifychannelmoved"] = this.handleNotifyChannelMoved; + this["notifychanneledited"] = this.handleNotifyChannelEdited; + this["notifytextmessage"] = this.handleNotifyTextMessage; + this["notifyclientchatcomposing"] = this.notifyClientChatComposing; + this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; + this["notifyclientupdated"] = this.handleNotifyClientUpdated; + this["notifyserveredited"] = this.handleNotifyServerEdited; + this["notifyserverupdated"] = this.handleNotifyServerUpdated; + this["notifyclientpoke"] = this.handleNotifyClientPoke; + this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; + this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; + this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; + this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; + this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; + this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; + this["notifyconversationhistory"] = this.handleNotifyConversationHistory; + this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; + this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; + this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; + this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; + this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; + this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; + this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + } + loggable_invoker(unique_id, client_id, name) { + const id = parseInt(client_id); + if (typeof (client_id) === "undefined" || Number.isNaN(id)) + return undefined; + if (id == 0) + return { + client_id: 0, + client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, + client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, + }; + return { + client_unique_id: unique_id, + client_name: name, + client_id: client_id + }; + } + proxy_command_promise(promise, options) { + if (!options.process_result) + return promise; + return promise.catch(ex => { + if (options.process_result) { + if (ex instanceof CommandResult) { + let res = ex; + if (!res.success) { + if (res.id == ErrorID.PERMISSION_ERROR) { //Permission error + const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"]); + res.message = (_translations.pCk7xYst || (_translations.pCk7xYst = tr("Insufficient client permissions. Failed on permission "))) + (permission ? permission.name : "unknown"); + this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, { + permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"]) + }); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + else if (res.id != ErrorID.EMPTY_RESULT) { + this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, { + message: res.extra_message.length == 0 ? res.message : res.extra_message + }); + } + } + } + else if (typeof (ex) === "string") { + this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, { error: ex }); + } + else { + log.error(LogCategory.NETWORKING, _translations.G_WEzBlb || (_translations.G_WEzBlb = tr("Invalid promise result type: %s. Result: %o")), typeof (ex), ex); + } + } + return Promise.reject(ex); + }); + } + handle_command(command) { + if (this[command.command]) { + this[command.command](command.arguments); + return true; + } + return false; + } + set_handler(command, handler) { + this[command] = handler; + } + unset_handler(command, handler) { + if (handler && this[command] != handler) + return; + this[command] = undefined; + } + handleCommandResult(json) { + json = json[0]; //Only one bulk + let code = json["return_code"]; + if (!code || code.length == 0) { + log.warn(LogCategory.NETWORKING, _translations.y8lxS0mg || (_translations.y8lxS0mg = tr("Invalid return code! (%o)")), json); + return; + } + let retListeners = this.connection["_retListener"]; + for (let e of retListeners) { + if (e.code != code) + continue; + retListeners.remove(e); + let result = new CommandResult(json); + if (result.success) + e.resolve(result); + else + e.reject(result); + break; + } + } + handleCommandServerInit(json) { + //We could setup the voice channel + if (this.connection.support_voice()) { + log.debug(LogCategory.NETWORKING, _translations.RBhEfyBT || (_translations.RBhEfyBT = tr("Setting up voice"))); + } + else { + log.debug(LogCategory.NETWORKING, _translations.N3Jb_ujY || (_translations.N3Jb_ujY = tr("Skipping voice setup (No voice bridge available)"))); + } + json = json[0]; //Only one bulk + this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); + this.connection.client.side_bar.channel_conversations().reset(); + this.connection.client.clientId = parseInt(json["aclid"]); + this.connection.client.getClient().updateVariables({ key: "client_nickname", value: json["acn"] }); + let updates = []; + for (let key in json) { + if (key === "aclid") + continue; + if (key === "acn") + continue; + updates.push({ key: key, value: json[key] }); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + const properties = this.connection.client.channelTree.server.properties; + /* host message */ + if (properties.virtualserver_hostmessage_mode > 0) { + if (properties.virtualserver_hostmessage_mode == 1) { + /* show in log */ + this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, { + message: properties.virtualserver_hostmessage + }); + } + else { + /* create modal/create modal and quit */ + createModal({ + header: _translations.byMDJXBp || (_translations.byMDJXBp = tr("Host message")), + body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), + footer: undefined + }).open(); + if (properties.virtualserver_hostmessage_mode == 3) { + /* first let the client initialize his stuff */ + setTimeout(() => { + this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { + message: properties.virtualserver_welcomemessage + }); + this.connection.disconnect("host message disconnect"); + this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); + this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); + }, 100); + } + } + } + /* welcome message */ + if (properties.virtualserver_welcomemessage) { + this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, { + message: properties.virtualserver_welcomemessage + }); + } + /* priviledge key */ + if (properties.virtualserver_ask_for_privilegekey) { + createInputModal(_translations.kJ99wZWj || (_translations.kJ99wZWj = tr("Use a privilege key")), _translations.vSKb8F5P || (_translations.vSKb8F5P = tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions.")), message => message.length > 0, result => { + if (!result) + return; + const scon = server_connections.active_connection_handler(); + if (scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(_translations.z78_SMMr || (_translations.z78_SMMr = tr("Use privilege key")), _translations.smS5MNGV || (_translations.smS5MNGV = tr("Privilege key successfully used!"))).open(); + }).catch(error => { + createErrorModal(_translations.yKYk5MrN || (_translations.yKYk5MrN = tr("Use privilege key")), MessageHelper.formatMessage(_translations.T9XgymAy || (_translations.T9XgymAy = tr("Failed to use privilege key: {}")), error instanceof CommandResult ? error.message : error)).open(); + }); + }, { field_placeholder: 'Enter Privilege Key' }).open(); + } + this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, { + own_client: this.connection_handler.getClient().log_data() + }); + this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); + this.connection.client.onConnected(); + } + handleNotifyServerConnectionInfo(json) { + json = json[0]; + /* everything is a number, so lets parse it */ + for (const key of Object.keys(json)) + json[key] = parseFloat(json[key]); + this.connection_handler.channelTree.server.set_connection_info(json); + } + handleNotifyConnectionInfo(json) { + json = json[0]; + const object = new ClientConnectionInfo(); + /* everything is a number (except ip), so lets parse it */ + for (const key of Object.keys(json)) { + if (key === "connection_client_ip") + object[key] = json[key]; + else + object[key] = parseFloat(json[key]); + } + const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); + if (!client) { + log.warn(LogCategory.NETWORKING, _translations.xGqliR_T || (_translations.xGqliR_T = tr("Received client connection info for unknown client (%o)")), json["clid"]); + return; + } + client.set_connection_info(object); + } + createChannelFromJson(json, ignoreOrder = false) { + let tree = this.connection.client.channelTree; + let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); + tree.insertChannel(channel); + if (json["channel_order"] !== "0") { + let prev = tree.findChannel(json["channel_order"]); + if (!prev && json["channel_order"] != 0) { + if (!ignoreOrder) { + log.error(LogCategory.NETWORKING, _translations.hTYqyg0W || (_translations.hTYqyg0W = tr("Invalid channel order id!"))); + return; + } + } + let parent = tree.findChannel(json["cpid"]); + if (!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, _translations.LZKNb1ud || (_translations.LZKNb1ud = tr("Invalid channel parent"))); + return; + } + tree.moveChannel(channel, prev, parent); //TODO test if channel exists! + } + if (ignoreOrder) { + for (let ch of tree.channels) { + if (ch.properties.channel_order == channel.channelId) { + tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) + } + } + } + let updates = []; + for (let key in json) { + if (key === "cid") + continue; + if (key === "cpid") + continue; + if (key === "invokerid") + continue; + if (key === "invokername") + continue; + if (key === "invokeruid") + continue; + if (key === "reasonid") + continue; + updates.push({ key: key, value: json[key] }); + } + channel.updateVariables(...updates); + } + handleCommandChannelList(json) { + this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ + log.debug(LogCategory.NETWORKING, _translations.m95mid7y || (_translations.m95mid7y = tr("Got %d new channels")), json.length); + for (let index = 0; index < json.length; index++) + this.createChannelFromJson(json[index], true); + } + handleCommandChannelListFinished(json) { + this.connection.client.channelTree.show_channel_tree(); + } + handleCommandChannelCreate(json) { + this.createChannelFromJson(json[0]); + } + handleCommandChannelShow(json) { + this.createChannelFromJson(json[0]); //TODO may chat? + } + handleCommandChannelDelete(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + log.info(LogCategory.NETWORKING, _translations.cO7AsMbZ || (_translations.cO7AsMbZ = tr("Got %d channel deletions")), json.length); + for (let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if (!channel) { + log.error(LogCategory.NETWORKING, _translations.Tr8N0T8J || (_translations.Tr8N0T8J = tr("Invalid channel onDelete (Unknown channel)"))); + continue; + } + tree.deleteChannel(channel); + } + } + handleCommandChannelHide(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + log.info(LogCategory.NETWORKING, _translations.tGrW50fT || (_translations.tGrW50fT = tr("Got %d channel hides")), json.length); + for (let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if (!channel) { + log.error(LogCategory.NETWORKING, _translations.tqXi_0qx || (_translations.tqXi_0qx = tr("Invalid channel on hide (Unknown channel)"))); + continue; + } + tree.deleteChannel(channel); + } + } + handleCommandClientEnterView(json) { + let tree = this.connection.client.channelTree; + let client; + let channel = undefined; + let old_channel = undefined; + let reason_id, reason_msg; + let invokerid, invokername, invokeruid; + for (const entry of json) { + /* attempt to update properties if given */ + channel = typeof (entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; + old_channel = typeof (entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; + reason_id = typeof (entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; + reason_msg = typeof (entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; + invokerid = typeof (entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; + invokername = typeof (entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; + invokeruid = typeof (entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; + client = tree.findClient(parseInt(entry["clid"])); + if (!client) { + if (parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { + client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } + else { + client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } + client.properties.client_type = parseInt(entry["client_type"]); + client = tree.insertClient(client, channel); + } + else { + tree.moveClient(client, channel); + } + if (this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, { + channel_from: old_channel ? old_channel.log_data() : undefined, + channel_to: channel ? channel.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(invokeruid, invokerid, invokername), + message: reason_msg, + reason: parseInt(reason_id), + own_channel: channel == own_channel + }); + if (reason_id == ViewReasonId.VREASON_USER_ACTION) { + if (own_channel == channel) + if (old_channel) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else + this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); + } + else if (reason_id == ViewReasonId.VREASON_MOVED) { + if (own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + } + else if (reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + if (own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + } + else if (reason_id == ViewReasonId.VREASON_SYSTEM) { + } + else { + console.warn(_translations.YwXRwg0u || (_translations.YwXRwg0u = tr("Unknown reasonid for %o")), reason_id); + } + } + let updates = []; + for (let key in entry) { + if (key == "cfid") + continue; + if (key == "ctid") + continue; + if (key === "invokerid") + continue; + if (key === "invokername") + continue; + if (key === "invokeruid") + continue; + if (key === "reasonid") + continue; + updates.push({ key: key, value: entry[key] }); + } + client.updateVariables(...updates); + /* if its a new client join, or a system reason (like we joined) */ + if (!old_channel || reason_id == 2) { + /* client new join */ + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + unique_id: client.properties.client_unique_identifier, + client_id: client.clientId(), + name: client.clientNickName() + }, { + create: false, + attach: true + }); + if (conversation) + client.flag_text_unread = conversation.is_unread(); + } + if (client instanceof LocalClientEntry) { + client.initializeListener(); + this.connection_handler.update_voice_status(); + this.connection_handler.side_bar.info_frame().update_channel_talk(); + const conversations = this.connection.client.side_bar.channel_conversations(); + conversations.set_current_channel(client.currentChannel().channelId); + } + } + } + handleCommandClientLeftView(json) { + let reason_id = -1; + for (const entry of json) { + reason_id = entry["reasonid"] || reason_id; + let tree = this.connection.client.channelTree; + let client = tree.findClient(entry["clid"]); + if (!client) { + log.error(LogCategory.NETWORKING, _translations.cJ2nujBf || (_translations.cJ2nujBf = tr("Unknown client left!"))); + return 0; + } + if (client == this.connection.client.getClient()) { + if (reason_id == ViewReasonId.VREASON_BAN) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); + } + else if (reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); + } + else if (reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } + else if (reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } + else { + this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); + } + this.connection_handler.side_bar.info_frame().update_channel_talk(); + return; + } + if (this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + let channel_from = tree.findChannel(entry["cfid"]); + let channel_to = tree.findChannel(entry["ctid"]); + const is_own_channel = channel_from == own_channel; + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, { + channel_from: channel_from ? channel_from.log_data() : undefined, + channel_to: channel_to ? channel_to.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), + message: entry["reasonmsg"], + reason: parseInt(entry["reasonid"]), + ban_time: parseInt(entry["bantime"]), + own_channel: is_own_channel + }); + if (is_own_channel) { + if (reason_id == ViewReasonId.VREASON_USER_ACTION) { + this.connection_handler.sound.play(Sound.USER_LEFT); + } + else if (reason_id == ViewReasonId.VREASON_SERVER_LEFT) { + this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); + } + else if (reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); + } + else if (reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } + else if (reason_id == ViewReasonId.VREASON_BAN) { + this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); + } + else if (reason_id == ViewReasonId.VREASON_TIMEOUT) { + this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); + } + else if (reason_id == ViewReasonId.VREASON_MOVED) { + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } + else { + log.error(LogCategory.NETWORKING, _translations.NTMrV0pQ || (_translations.NTMrV0pQ = tr("Unknown client left reason %d!")), reason_id); + } + } + if (!channel_to) { + /* client left the server */ + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + unique_id: client.properties.client_unique_identifier, + client_id: client.clientId(), + name: client.clientNickName() + }, { + create: false, + attach: false + }); + if (conversation) { + conversation.set_state(chat.PrivateConversationState.DISCONNECTED); + } + } + } + tree.deleteClient(client); + } + } + handleNotifyClientMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + let self = client instanceof LocalClientEntry; + let channel_to = tree.findChannel(parseInt(json["ctid"])); + let channel_from = tree.findChannel(parseInt(json["cfid"])); + if (!client) { + log.error(LogCategory.NETWORKING, _translations.gEcJmHRi || (_translations.gEcJmHRi = tr("Unknown client move (Client)!"))); + return 0; + } + if (!channel_to) { + log.error(LogCategory.NETWORKING, _translations.rsbbZ5vo || (_translations.rsbbZ5vo = tr("Unknown client move (Channel to)!"))); + return 0; + } + if (!self) { + if (!channel_from) { + log.error(LogCategory.NETWORKING, _translations.FUu2Z3p0 || (_translations.FUu2Z3p0 = tr("Unknown client move (Channel from)!"))); + channel_from = client.currentChannel(); + } + else if (channel_from != client.currentChannel()) { + log.error(LogCategory.NETWORKING, _translations.BuZ27Ktg || (_translations.BuZ27Ktg = tr("Client move from invalid source channel! Local client registered in channel %d but server send %d.")), client.currentChannel().channelId, channel_from.channelId); + } + } + else { + channel_from = client.currentChannel(); + } + tree.moveClient(client, channel_to); + if (self) { + this.connection_handler.update_voice_status(channel_to); + for (const entry of client.channelTree.clientsByChannel(channel_from)) { + if (entry !== client && entry.get_audio_handle()) { + entry.get_audio_handle().abort_replay(); + entry.speaking = false; + } + } + const side_bar = this.connection_handler.side_bar; + side_bar.info_frame().update_channel_talk(); + const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); + if (conversation_to) + conversation_to.update_private_state(); + if (channel_from) { + const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); + if (conversation_from) + conversation_from.update_private_state(); + } + side_bar.channel_conversations().update_chat_box(); + } + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, { + channel_from: channel_from ? { + channel_id: channel_from.channelId, + channel_name: channel_from.channelName() + } : undefined, + channel_from_own: channel_from == own_channel, + channel_to: channel_to ? { + channel_id: channel_to.channelId, + channel_name: channel_to.channelName() + } : undefined, + channel_to_own: channel_to == own_channel, + client: { + client_id: client.clientId(), + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier + }, + client_own: self, + invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), + message: json["reasonmsg"], + reason: parseInt(json["reasonid"]), + }); + if (json["reasonid"] == ViewReasonId.VREASON_MOVED) { + if (self) + this.connection_handler.sound.play(Sound.USER_MOVED_SELF); + else if (own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + else if (own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } + else if (json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + if (self) { } //If we do an action we wait for the error response + else if (own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else if (own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT); + } + else if (json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + if (self) { + this.connection_handler.sound.play(Sound.CHANNEL_KICKED); + } + else if (own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + else if (own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } + else { + console.warn(_translations.BID316jI || (_translations.BID316jI = tr("Unknown reason id %o")), json["reasonid"]); + } + } + handleNotifyChannelMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if (!channel) { + log.error(LogCategory.NETWORKING, _translations.fzc4kFcz || (_translations.fzc4kFcz = tr("Unknown channel move (Channel)!"))); + return 0; + } + let prev = tree.findChannel(json["order"]); + if (!prev && json["order"] != 0) { + log.error(LogCategory.NETWORKING, _translations.mF6Ppr3u || (_translations.mF6Ppr3u = tr("Unknown channel move (prev)!"))); + return 0; + } + let parent = tree.findChannel(json["cpid"]); + if (!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, _translations.XxAerQnq || (_translations.XxAerQnq = tr("Unknown channel move (parent)!"))); + return 0; + } + tree.moveChannel(channel, prev, parent); + } + handleNotifyChannelEdited(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if (!channel) { + log.error(LogCategory.NETWORKING, _translations.tBW9j_7C || (_translations.tBW9j_7C = tr("Unknown channel edit (Channel)!"))); + return 0; + } + let updates = []; + for (let key in json) { + if (key === "cid") + continue; + if (key === "invokerid") + continue; + if (key === "invokername") + continue; + if (key === "invokeruid") + continue; + if (key === "reasonid") + continue; + updates.push({ key: key, value: json[key] }); + } + channel.updateVariables(...updates); + if (this.connection_handler.getClient().currentChannel() === channel) { + //TODO: Playback sound that your channel has been edited + this.connection_handler.update_voice_status(); + } + } + handleNotifyTextMessage(json) { + json = json[0]; //Only one bulk + let mode = json["targetmode"]; + if (mode == 1) { + //json["invokerid"], json["invokername"], json["invokeruid"] + const target_client_id = parseInt(json["target"]); + const target_own = target_client_id === this.connection.client.getClientId(); + if (target_own && target_client_id === json["invokerid"]) { + log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json)); + return; + } + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, + unique_id: target_own ? json["invokeruid"] : undefined, + name: target_own ? json["invokername"] : undefined + }, { + create: target_own, + attach: target_own + }); + if (!conversation) { + log.error(LogCategory.NETWORKING, _translations._E2eMYr4 || (_translations._E2eMYr4 = tr("Received conversation message for unknown conversation! (%s)")), target_own ? _translations.kgIHfhjS || (_translations.kgIHfhjS = tr("Remote message")) : _translations.ruiHzDfv || (_translations.ruiHzDfv = tr("Own message"))); + return; + } + conversation.append_message(json["msg"], { + type: target_own ? "partner" : "self", + name: json["invokername"], + unique_id: json["invokeruid"], + client_id: parseInt(json["invokerid"]) + }); + if (target_own) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, { default_volume: .5 }); + const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + if (client) /* the client itself might be invisible */ + client.flag_text_unread = conversation.is_unread(); + } + else { + this.connection_handler.sound.play(Sound.MESSAGE_SEND, { default_volume: .5 }); + } + } + else if (mode == 2) { + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const own_channel_id = this.connection.client.getClient().currentChannel().channelId; + const channel_id = typeof (json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; + const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); + if (json["invokerid"] == this.connection.client.clientId) + this.connection_handler.sound.play(Sound.MESSAGE_SEND, { default_volume: .5 }); + else if (channel_id == own_channel_id) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, { default_volume: .5 }); + } + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(channel_id); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + timestamp: typeof (json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + if (conversation.is_unread() && channel) + channel.flag_text_unread = true; + } + else if (mode == 3) { + this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, { + message: json["msg"], + sender: { + client_unique_id: json["invokeruid"], + client_name: json["invokername"], + client_id: parseInt(json["invokerid"]) + } + }); + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(0); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + timestamp: typeof (json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); + } + } + notifyClientChatComposing(json) { + json = json[0]; + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if (!conversation) + return; + conversation.trigger_typing(); + } + handleNotifyClientChatClosed(json) { + json = json[0]; //Only one bulk + //Chat partner has closed the conversation + //clid: "6" + //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if (!conversation) { + log.warn(LogCategory.GENERAL, _translations.U0exVRxM || (_translations.U0exVRxM = tr("Received chat close for client, but we haven't a chat open."))); + return; + } + conversation.set_state(chat.PrivateConversationState.CLOSED); + } + handleNotifyClientUpdated(json) { + json = json[0]; //Only one bulk + let client = this.connection.client.channelTree.findClient(json["clid"]); + if (!client) { + log.error(LogCategory.NETWORKING, _translations.suQhKZ2M || (_translations.suQhKZ2M = tr("Tried to update an non existing client"))); + return; + } + let updates = []; + for (let key in json) { + if (key == "clid") + continue; + updates.push({ key: key, value: json[key] }); + } + client.updateVariables(...updates); + } + handleNotifyServerEdited(json) { + json = json[0]; + let updates = []; + for (let key in json) { + if (key === "invokerid") + continue; + if (key === "invokername") + continue; + if (key === "invokeruid") + continue; + if (key === "reasonid") + continue; + updates.push({ key: key, value: json[key] }); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + } + handleNotifyServerUpdated(json) { + json = json[0]; + let updates = []; + for (let key in json) { + if (key === "invokerid") + continue; + if (key === "invokername") + continue; + if (key === "invokeruid") + continue; + if (key === "reasonid") + continue; + updates.push({ key: key, value: json[key] }); + } + this.connection.client.channelTree.server.updateVariables(true, ...updates); + } + handleNotifyMusicPlayerInfo(json) { + json = json[0]; + let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); + if (!bot || !(bot instanceof MusicClientEntry)) { + log.warn(LogCategory.CLIENT, _translations.BxOvO8Ec || (_translations.BxOvO8Ec = tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)")), json["bot_id"], bot); + return; + } + bot.handlePlayerInfo(json); + } + handleNotifyClientPoke(json) { + json = json[0]; + Modals.spawnPoke(this.connection_handler, { + id: parseInt(json["invokerid"]), + name: json["invokername"], + unique_id: json["invokeruid"] + }, json["msg"]); + this.connection_handler.sound.play(Sound.USER_POKED_SELF); + } + //TODO server chat message + handleNotifyServerGroupClientAdd(json) { + json = json[0]; + const self = this.connection.client.getClient(); + if (json["clid"] == self.clientId()) + this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); + } + //TODO server chat message + handleNotifyServerGroupClientRemove(json) { + json = json[0]; + const self = this.connection.client.getClient(); + if (json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); + } + else { + } + } + //TODO server chat message + handleNotifyClientChannelGroupChanged(json) { + json = json[0]; + const self = this.connection.client.getClient(); + if (json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); + } + } + handleNotifyChannelSubscribed(json) { + for (const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if (!channel) { + console.warn(_translations._5Wlb_tM || (_translations._5Wlb_tM = tr("Received channel subscribed for not visible channel (cid: %d)")), entry['cid']); + continue; + } + channel.flag_subscribed = true; + } + } + handleNotifyChannelUnsubscribed(json) { + for (const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if (!channel) { + console.warn(_translations.HMeyZ6hh || (_translations.HMeyZ6hh = tr("Received channel unsubscribed for not visible channel (cid: %d)")), entry['cid']); + continue; + } + channel.flag_subscribed = false; + for (const client of channel.clients(false)) + this.connection.client.channelTree.deleteClient(client); + } + } + handleNotifyConversationHistory(json) { + const conversations = this.connection.client.side_bar.channel_conversations(); + const conversation = conversations.conversation(parseInt(json[0]["cid"])); + if (!conversation) { + log.warn(LogCategory.NETWORKING, _translations.bkMX72xe || (_translations.bkMX72xe = tr("Received conversation history for invalid or unknown conversation (%o)")), json[0]["cid"]); + return; + } + for (const entry of json) { + conversation.register_new_message({ + message: entry["msg"], + sender_unique_id: entry["sender_unique_id"], + sender_name: entry["sender_name"], + timestamp: parseInt(entry["timestamp"]), + sender_database_id: parseInt(entry["sender_database_id"]) + }, false); + } + /* now update the boxes */ + /* No update needed because the command which triggers this notify should update the chat box on success + conversation.fix_scroll(true); + conversation.handle.update_chat_box(); + */ + } + handleNotifyConversationMessageDelete(json) { + let conversation; + const conversations = this.connection.client.side_bar.channel_conversations(); + for (const entry of json) { + if (typeof (entry["cid"]) !== "undefined") + conversation = conversations.conversation(parseInt(entry["cid"]), false); + if (!conversation) + continue; + conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); + } + } + handleNotifyMusicStatusUpdate(json) { + json = json[0]; + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.Pqgsw3nk || (_translations.Pqgsw3nk = tr("Received music bot status update for unknown bot (%d)")), bot_id); + return; + } + client.events.fire("music_status_update", { + player_replay_index: parseInt(json["player_replay_index"]), + player_buffered_index: parseInt(json["player_buffered_index"]) + }); + } + handleMusicPlayerSongChange(json) { + json = json[0]; + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.acswhp6U || (_translations.acswhp6U = tr("Received music bot status update for unknown bot (%d)")), bot_id); + return; + } + const song_id = parseInt(json["song_id"]); + let song; + if (song_id) { + song = new SongInfo(); + JSON.map_to(song, json); + } + client.events.fire("music_song_change", { + song: song + }); + } + handleNotifyPlaylistSongAdd(json) { + json = json[0]; + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.JHeDrma5 || (_translations.JHeDrma5 = tr("Received playlist song add event, but we've no music bot for the playlist (%d)")), playlist_id); + return; + } + client.events.fire("playlist_song_add", { + song: { + song_id: parseInt(json["song_id"]), + song_invoker: json["song_invoker"], + song_previous_song_id: parseInt(json["song_previous_song_id"]), + song_url: json["song_url"], + song_url_loader: json["song_url_loader"], + song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", + song_metadata: json["song_metadata"] + } + }); + } + handleNotifyPlaylistSongRemove(json) { + json = json[0]; + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.KYFQM8h2 || (_translations.KYFQM8h2 = tr("Received playlist song remove event, but we've no music bot for the playlist (%d)")), playlist_id); + return; + } + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_remove", { song_id: song_id }); + } + handleNotifyPlaylistSongReorder(json) { + json = json[0]; + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.MX9isGwd || (_translations.MX9isGwd = tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)")), playlist_id); + return; + } + const song_id = parseInt(json["song_id"]); + const previous_song_id = parseInt(json["song_previous_song_id"]); + client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); + } + handleNotifyPlaylistSongLoaded(json) { + json = json[0]; + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if (!client) { + log.warn(LogCategory.CLIENT, _translations.x8tCYqXl || (_translations.x8tCYqXl = tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)")), playlist_id); + return; + } + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_loaded", { + song_id: song_id, + success: json["success"] == 1, + error_msg: json["load_error_msg"], + metadata: json["song_metadata"] + }); + } + } + connection_2.ConnectionCommandHandler = ConnectionCommandHandler; +})(connection || (connection = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e9e95922a16d121df1c28714498c8e3967cc4a33184c1c27b9daebcd034ed5cd"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e9e95922a16d121df1c28714498c8e3967cc4a33184c1c27b9daebcd034ed5cd"] = "e9e95922a16d121df1c28714498c8e3967cc4a33184c1c27b9daebcd034ed5cd"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "MwbGPBq7", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (248,43)" }, { name: "nF1SZku6", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (271,43)" }, { name: "Ycyq5vFI", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (545,26)" }, { name: "xjn9wuVk", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (672,47)" }, { name: "Dqn_5EM4", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (681,47)" }, { name: "yJaaczeF", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (726,43)" }, { name: "HC0JhWOd", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (772,47)" }, { name: "Zfxv8Gh8", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (881,48)" }, { name: "RpfhT5G_", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (890,48)" }, { name: "qcVDOT3t", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (928,43)" }, { name: "KjfmxVkY", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (931,48)" }, { name: "N9t94bx_", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (940,48)" }, { name: "h_SQX0Ol", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (952,55)" }, { name: "W3cTHtbm", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (997,47)" }, { name: "n7xfvxab", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (999,74)" }, { name: "N5oq_P39", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (1062,48)" }, { name: "UUEkQF5a", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (1076,48)" }, { name: "qeVWbl1T", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (1082,56)" }, { name: "yPO5jFrp", path: "D:/TeaSpeak/web/shared/js/FileManager.ts (1099,46)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +class FileEntry { +} +class FileListRequest { +} +var transfer; +(function (transfer) { + function spawn_download_transfer(key) { + return new RequestFileDownload(key); + } + transfer.spawn_download_transfer = spawn_download_transfer; + function spawn_upload_transfer(key) { + return new RequestFileUpload(key); + } + transfer.spawn_upload_transfer = spawn_upload_transfer; +})(transfer || (transfer = {})); +class RequestFileDownload { + constructor(key) { + this.transfer_key = key; + } + request_file() { + return __awaiter(this, void 0, void 0, function* () { + return yield this.try_fetch("https://" + this.transfer_key.peer.hosts[0] + ":" + this.transfer_key.peer.port); + }); + } + try_fetch(url) { + return __awaiter(this, void 0, void 0, function* () { + const response = yield fetch(url, { + method: 'GET', + cache: "no-cache", + mode: 'cors', + headers: { + 'transfer-key': this.transfer_key.key, + 'download-name': this.transfer_key.file_name, + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Expose-Headers': '*' + } + }); + if (!response.ok) { + debugger; + throw (response.type == 'opaque' || response.type == 'opaqueredirect' ? "invalid cross origin flag! May target isn't a TeaSpeak server?" : response.statusText || "response is not ok"); + } + return response; + }); + } + get_key() { + return this.transfer_key; + } +} +class RequestFileUpload { + constructor(key) { + this.transfer_key = key; + } + get_key() { + return this.transfer_key; + } + put_data(data) { + return __awaiter(this, void 0, void 0, function* () { + const form_data = new FormData(); + if (data instanceof File) { + if (data.size != this.transfer_key.total_size) + throw "invalid size"; + form_data.append("file", data); + } + else if (typeof (data) === "string") { + if (data.length != this.transfer_key.total_size) + throw "invalid size"; + form_data.append("file", new Blob([data], { type: "application/octet-stream" })); + } + else { + const buffer = data; + if (buffer.byteLength != this.transfer_key.total_size) + throw "invalid size"; + form_data.append("file", new Blob([buffer], { type: "application/octet-stream" })); + } + yield this.try_put(form_data, "https://" + this.transfer_key.peer.hosts[0] + ":" + this.transfer_key.peer.port); + }); + } + try_put(data, url) { + return __awaiter(this, void 0, void 0, function* () { + const response = yield fetch(url, { + method: 'POST', + cache: "no-cache", + mode: 'cors', + body: data, + headers: { + 'transfer-key': this.transfer_key.key, + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Expose-Headers': '*' + } + }); + if (!response.ok) + throw (response.type == 'opaque' || response.type == 'opaqueredirect' ? "invalid cross origin flag! May target isn't a TeaSpeak server?" : response.statusText || "response is not ok"); + }); + } +} +class FileManager extends connection.AbstractCommandHandler { + constructor(client) { + super(client.serverConnection); + this.listRequests = []; + this.pending_download_requests = []; + this.pending_upload_requests = []; + this.transfer_counter = 1; + this.handle = client; + this.icons = new IconManager(this); + this.avatars = new AvatarManager(this); + this.connection.command_handler_boss().register_handler(this); + } + destroy() { + if (this.connection) { + const hboss = this.connection.command_handler_boss(); + if (hboss) + hboss.unregister_handler(this); + } + this.listRequests = undefined; + this.pending_download_requests = undefined; + this.pending_upload_requests = undefined; + this.icons && this.icons.destroy(); + this.icons = undefined; + this.avatars && this.avatars.destroy(); + this.avatars = undefined; + } + handle_command(command) { + switch (command.command) { + case "notifyfilelist": + this.notifyFileList(command.arguments); + return true; + case "notifyfilelistfinished": + this.notifyFileListFinished(command.arguments); + return true; + case "notifystartdownload": + this.notifyStartDownload(command.arguments); + return true; + case "notifystartupload": + this.notifyStartUpload(command.arguments); + return true; + } + return false; + } + /******************************** File list ********************************/ + //TODO multiple requests (same path) + requestFileList(path, channel, password) { + const _this = this; + return new Promise((accept, reject) => { + let req = new FileListRequest(); + req.path = path; + req.entries = []; + req.callback = accept; + _this.listRequests.push(req); + _this.handle.serverConnection.send_command("ftgetfilelist", { "path": path, "cid": (channel ? channel.channelId : "0"), "cpw": (password ? password : "") }).then(() => { }).catch(reason => { + _this.listRequests.remove(req); + if (reason instanceof CommandResult) { + if (reason.id == 0x0501) { + accept([]); //Empty result + return; + } + } + reject(reason); + }); + }); + } + notifyFileList(json) { + let entry = undefined; + for (let e of this.listRequests) { + if (e.path == json[0]["path"]) { + entry = e; + break; + } + } + if (!entry) { + log.error(LogCategory.CLIENT, _translations.MwbGPBq7 || (_translations.MwbGPBq7 = tr("Invalid file list entry. Path: %s")), json[0]["path"]); + return; + } + for (let e of json) { + e.datetime = parseInt(e.datetime + ""); + e.size = parseInt(e.size + ""); + e.type = parseInt(e.type + ""); + entry.entries.push(e); + } + } + notifyFileListFinished(json) { + let entry = undefined; + for (let e of this.listRequests) { + if (e.path == json[0]["path"]) { + entry = e; + this.listRequests.remove(e); + break; + } + } + if (!entry) { + log.error(LogCategory.CLIENT, _translations.nF1SZku6 || (_translations.nF1SZku6 = tr("Invalid file list entry finish. Path: ")), json[0]["path"]); + return; + } + entry.callback(entry.entries); + } + /******************************** File download/upload ********************************/ + download_file(path, file, channel, password) { + const transfer_data = { + file_name: file, + file_path: path, + client_transfer_id: this.transfer_counter++ + }; + this.pending_download_requests.push(transfer_data); + return new Promise((resolve, reject) => { + transfer_data["_callback"] = resolve; + this.handle.serverConnection.send_command("ftinitdownload", { + "path": path, + "name": file, + "cid": (channel ? channel.channelId : "0"), + "cpw": (password ? password : ""), + "clientftfid": transfer_data.client_transfer_id, + "seekpos": 0, + "proto": 1 + }, { process_result: false }).catch(reason => { + this.pending_download_requests.remove(transfer_data); + reject(reason); + }); + }); + } + upload_file(options) { + const transfer_data = { + file_path: options.path, + file_name: options.name, + client_transfer_id: this.transfer_counter++, + total_size: options.size + }; + this.pending_upload_requests.push(transfer_data); + return new Promise((resolve, reject) => { + transfer_data["_callback"] = resolve; + this.handle.serverConnection.send_command("ftinitupload", { + "path": options.path, + "name": options.name, + "cid": (options.channel ? options.channel.channelId : "0"), + "cpw": options.channel_password || "", + "clientftfid": transfer_data.client_transfer_id, + "size": options.size, + "overwrite": options.overwrite, + "resume": false, + "proto": 1 + }).catch(reason => { + this.pending_upload_requests.remove(transfer_data); + reject(reason); + }); + }); + } + notifyStartDownload(json) { + json = json[0]; + let clientftfid = parseInt(json["clientftfid"]); + let transfer; + for (let e of this.pending_download_requests) + if (e.client_transfer_id == clientftfid) { + transfer = e; + break; + } + transfer.server_transfer_id = parseInt(json["serverftfid"]); + transfer.key = json["ftkey"]; + transfer.total_size = json["size"]; + transfer.peer = { + hosts: (json["ip"] || "").split(","), + port: parseInt(json["port"]) + }; + if (transfer.peer.hosts.length == 0) + transfer.peer.hosts.push("0.0.0.0"); + if (transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0') + transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host; + transfer["_callback"](transfer); + this.pending_download_requests.remove(transfer); + } + notifyStartUpload(json) { + json = json[0]; + let transfer; + let clientftfid = parseInt(json["clientftfid"]); + for (let e of this.pending_upload_requests) + if (e.client_transfer_id == clientftfid) { + transfer = e; + break; + } + transfer.server_transfer_id = parseInt(json["serverftfid"]); + transfer.key = json["ftkey"]; + transfer.peer = { + hosts: (json["ip"] || "").split(","), + port: parseInt(json["port"]) + }; + if (transfer.peer.hosts.length == 0) + transfer.peer.hosts.push("0.0.0.0"); + if (transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0') + transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host; + transfer["_callback"](transfer); + this.pending_upload_requests.remove(transfer); + } + /** File management **/ + delete_file(props) { + return __awaiter(this, void 0, void 0, function* () { + if (!props.name) + throw "invalid name!"; + try { + yield this.handle.serverConnection.send_command("ftdeletefile", { + cid: props.cid || 0, + cpw: props.cpw, + path: props.path || "", + name: props.name + }); + } + catch (error) { + throw error; + } + }); + } +} +class Icon { +} +var ImageType; +(function (ImageType) { + ImageType[ImageType["UNKNOWN"] = 0] = "UNKNOWN"; + ImageType[ImageType["BITMAP"] = 1] = "BITMAP"; + ImageType[ImageType["PNG"] = 2] = "PNG"; + ImageType[ImageType["GIF"] = 3] = "GIF"; + ImageType[ImageType["SVG"] = 4] = "SVG"; + ImageType[ImageType["JPEG"] = 5] = "JPEG"; +})(ImageType || (ImageType = {})); +function media_image_type(type, file) { + switch (type) { + case ImageType.BITMAP: + return "bmp"; + case ImageType.GIF: + return "gif"; + case ImageType.SVG: + return file ? "svg" : "svg+xml"; + case ImageType.JPEG: + return "jpeg"; + case ImageType.UNKNOWN: + case ImageType.PNG: + default: + return "png"; + } +} +function image_type(encoded_data, base64_encoded) { + const ab2str10 = () => { + const buf = new Uint8Array(encoded_data); + if (buf.byteLength < 10) + return ""; + let result = ""; + for (let index = 0; index < 10; index++) + result += String.fromCharCode(buf[index]); + return result; + }; + const bin = typeof (encoded_data) === "string" ? ((typeof (base64_encoded) === "undefined" || base64_encoded) ? atob(encoded_data) : encoded_data) : ab2str10(); + if (bin.length < 10) + return ImageType.UNKNOWN; + if (bin[0] == String.fromCharCode(66) && bin[1] == String.fromCharCode(77)) { + return ImageType.BITMAP; + } + else if (bin.substr(0, 8) == "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a") { + return ImageType.PNG; + } + else if (bin.substr(0, 4) == "\x47\x49\x46\x38" && (bin[4] == '\x37' || bin[4] == '\x39') && bin[5] == '\x61') { + return ImageType.GIF; + } + else if (bin[0] == '\x3c') { + return ImageType.SVG; + } + else if (bin[0] == '\xFF' && bin[1] == '\xd8') { + return ImageType.JPEG; + } + return ImageType.UNKNOWN; +} +class CacheManager { + constructor(name) { + this.cache_name = name; + } + setupped() { return !!this._cache_category; } + reset() { + return __awaiter(this, void 0, void 0, function* () { + if (!window.caches) + return; + try { + yield caches.delete(this.cache_name); + } + catch (error) { + throw "Failed to delete cache: " + error; + } + try { + yield this.setup(); + } + catch (error) { + throw "Failed to reinitialize cache!"; + } + }); + } + setup() { + return __awaiter(this, void 0, void 0, function* () { + if (!window.caches) + throw "Missing caches!"; + this._cache_category = yield caches.open(this.cache_name); + }); + } + cleanup(max_age) { + return __awaiter(this, void 0, void 0, function* () { + /* FIXME: TODO */ + }); + } + resolve_cached(key, max_age) { + return __awaiter(this, void 0, void 0, function* () { + max_age = typeof (max_age) === "number" ? max_age : -1; + const cached_response = yield this._cache_category.match("https://_local_cache/cache_request_" + key); + if (!cached_response) + return undefined; + /* FIXME: Max age */ + return cached_response; + }); + } + put_cache(key, value, type, headers) { + return __awaiter(this, void 0, void 0, function* () { + const new_headers = new Headers(); + for (const key of value.headers.keys()) + new_headers.set(key, value.headers.get(key)); + if (type) + new_headers.set("Content-type", type); + for (const key of Object.keys(headers || {})) + new_headers.set(key, headers[key]); + yield this._cache_category.put("https://_local_cache/cache_request_" + key, new Response(value.body, { + headers: new_headers + })); + }); + } + delete(key) { + return __awaiter(this, void 0, void 0, function* () { + const flag = yield this._cache_category.delete("https://_local_cache/cache_request_" + key, { + ignoreVary: true, + ignoreMethod: true, + ignoreSearch: true + }); + if (!flag) { + console.warn(_translations.Ycyq5vFI || (_translations.Ycyq5vFI = tr("Failed to delete key %s from cache!")), flag); + } + }); + } +} +class IconManager { + constructor(handle) { + this._id_urls = {}; + this._loading_promises = {}; + this.handle = handle; + } + destroy() { + if (URL.revokeObjectURL) { + for (const id of Object.keys(this._id_urls)) + URL.revokeObjectURL(this._id_urls[id]); + } + this._id_urls = undefined; + this._loading_promises = undefined; + } + clear_cache() { + return __awaiter(this, void 0, void 0, function* () { + yield IconManager.cache.reset(); + if (URL.revokeObjectURL) { + for (const id of Object.keys(this._id_urls)) + URL.revokeObjectURL(this._id_urls[id]); + } + this._id_urls = {}; + this._loading_promises = {}; + }); + } + delete_icon(id) { + return __awaiter(this, void 0, void 0, function* () { + if (id <= 1000) + throw "invalid id!"; + yield this.handle.delete_file({ + name: '/icon_' + id + }); + }); + } + iconList() { + return this.handle.requestFileList("/icons"); + } + create_icon_download(id) { + return this.handle.download_file("", "/icon_" + id); + } + static _response_url(response) { + return __awaiter(this, void 0, void 0, function* () { + if (!response.headers.has('X-media-bytes')) + throw "missing media bytes"; + const type = image_type(response.headers.get('X-media-bytes')); + const media = media_image_type(type); + const blob = yield response.blob(); + if (blob.type !== "image/" + media) + return URL.createObjectURL(blob.slice(0, blob.size, "image/" + media)); + else + return URL.createObjectURL(blob); + }); + } + resolved_cached(id) { + return __awaiter(this, void 0, void 0, function* () { + if (this._id_urls[id]) + return { + id: id, + url: this._id_urls[id] + }; + if (!IconManager.cache.setupped()) + yield IconManager.cache.setup(); + const response = yield IconManager.cache.resolve_cached('icon_' + id); //TODO age! + if (response) { + const url = yield IconManager._response_url(response); + if (this._id_urls[id]) + URL.revokeObjectURL(this._id_urls[id]); + return { + id: id, + url: url + }; + } + return undefined; + }); + } + static load_cached_icon(id, ignore_age) { + if (this._static_id_url[id]) { + return { + id: id, + url: this._static_id_url[id] + }; + } + if (this._static_cached_promise[id]) + return this._static_cached_promise[id]; + return (this._static_cached_promise[id] = (() => __awaiter(this, void 0, void 0, function* () { + if (!this.cache.setupped()) + yield this.cache.setup(); + const response = yield this.cache.resolve_cached('icon_' + id); //TODO age! + if (response) { + const url = yield this._response_url(response); + if (this._static_id_url[id]) + URL.revokeObjectURL(this._static_id_url[id]); + this._static_id_url[id] = url; + return { + id: id, + url: url + }; + } + }))()); + } + _load_icon(id) { + return __awaiter(this, void 0, void 0, function* () { + try { + let download_key; + try { + download_key = yield this.create_icon_download(id); + } + catch (error) { + log.error(LogCategory.CLIENT, _translations.xjn9wuVk || (_translations.xjn9wuVk = tr("Could not request download for icon %d: %o")), id, error); + throw "Failed to request icon"; + } + const downloader = transfer.spawn_download_transfer(download_key); + let response; + try { + response = yield downloader.request_file(); + } + catch (error) { + log.error(LogCategory.CLIENT, _translations.Dqn_5EM4 || (_translations.Dqn_5EM4 = tr("Could not download icon %d: %o")), id, error); + throw "failed to download icon"; + } + const type = image_type(response.headers.get('X-media-bytes')); + const media = media_image_type(type); + yield IconManager.cache.put_cache('icon_' + id, response.clone(), "image/" + media); + const url = yield IconManager._response_url(response.clone()); + if (this._id_urls[id]) + URL.revokeObjectURL(this._id_urls[id]); + this._id_urls[id] = url; + this._loading_promises[id] = undefined; + return { + id: id, + url: url + }; + } + catch (error) { + setTimeout(() => { + this._loading_promises[id] = undefined; + }, 1000 * 60); /* try again in 60 seconds */ + throw error; + } + }); + } + download_icon(id) { + return this._loading_promises[id] || (this._loading_promises[id] = this._load_icon(id)); + } + resolve_icon(id) { + return __awaiter(this, void 0, void 0, function* () { + id = id >>> 0; + try { + const result = yield this.resolved_cached(id); + if (result) + return result; + throw ""; + } + catch (error) { } + try { + const result = yield this.download_icon(id); + if (result) + return result; + throw "load result is empty"; + } + catch (error) { + log.error(LogCategory.CLIENT, _translations.yJaaczeF || (_translations.yJaaczeF = tr("Icon download failed of icon %d: %o")), id, error); + } + throw "icon not found"; + }); + } + static generate_tag(icon, options) { + options = options || {}; + let icon_container = $.spawn("div").addClass("icon-container icon_empty"); + let icon_load_image = $.spawn("div").addClass("icon_loading"); + const icon_image = $.spawn("img").attr("width", 16).attr("height", 16).attr("alt", ""); + const _apply = (icon) => { + let id = icon ? (icon.id >>> 0) : 0; + if (!icon || id == 0) { + icon_load_image.remove(); + icon_load_image = undefined; + return; + } + else if (id < 1000) { + icon_load_image.remove(); + icon_load_image = undefined; + icon_container.removeClass("icon_empty").addClass("icon_em client-group_" + id); + return; + } + icon_image.attr("src", icon.url); + icon_container.append(icon_image).removeClass("icon_empty"); + if (typeof (options.animate) !== "boolean" || options.animate) { + icon_image.css("opacity", 0); + icon_load_image.animate({ opacity: 0 }, 50, function () { + icon_load_image.remove(); + icon_image.animate({ opacity: 1 }, 150); + }); + } + else { + icon_load_image.remove(); + icon_load_image = undefined; + } + }; + if (icon instanceof Promise) { + icon.then(_apply).catch(error => { + log.error(LogCategory.CLIENT, _translations.HC0JhWOd || (_translations.HC0JhWOd = tr("Could not load icon. Reason: %s")), error); + icon_load_image.removeClass("icon_loading").addClass("icon client-warning").attr("tag", "Could not load icon"); + }); + } + else { + _apply(icon); + } + if (icon_load_image) + icon_load_image.appendTo(icon_container); + return icon_container; + } + generateTag(id, options) { + options = options || {}; + id = id >>> 0; + if (id == 0 || !id) + return IconManager.generate_tag({ id: id, url: "" }, options); + else if (id < 1000) + return IconManager.generate_tag({ id: id, url: "" }, options); + if (this._id_urls[id]) { + return IconManager.generate_tag({ id: id, url: this._id_urls[id] }, options); + } + else { + return IconManager.generate_tag(this.resolve_icon(id), options); + } + } +} +IconManager.cache = new CacheManager("icons"); +IconManager._static_id_url = {}; +IconManager._static_cached_promise = {}; +class Avatar { +} +class AvatarManager { + constructor(handle) { + this._cached_avatars = {}; + this._loading_promises = {}; + this.handle = handle; + if (!AvatarManager.cache) + AvatarManager.cache = new CacheManager("avatars"); + } + destroy() { + this._cached_avatars = undefined; + this._loading_promises = undefined; + } + _response_url(response, type) { + return __awaiter(this, void 0, void 0, function* () { + if (!response.headers.has('X-media-bytes')) + throw "missing media bytes"; + const media = media_image_type(type); + const blob = yield response.blob(); + if (blob.type !== "image/" + media) + return URL.createObjectURL(blob.slice(0, blob.size, "image/" + media)); + else + return URL.createObjectURL(blob); + }); + } + resolved_cached(client_avatar_id, avatar_version) { + return __awaiter(this, void 0, void 0, function* () { + let avatar = this._cached_avatars[avatar_version]; + if (avatar) { + if (typeof (avatar_version) !== "string" || avatar.avatar_id == avatar_version) + return avatar; + avatar = undefined; + } + if (!AvatarManager.cache.setupped()) + yield AvatarManager.cache.setup(); + const response = yield AvatarManager.cache.resolve_cached('avatar_' + client_avatar_id); //TODO age! + if (!response) + return undefined; + let response_avatar_version = response.headers.has("X-avatar-version") ? response.headers.get("X-avatar-version") : undefined; + if (typeof (avatar_version) === "string" && response_avatar_version != avatar_version) + return undefined; + const type = image_type(response.headers.get('X-media-bytes')); + return this._cached_avatars[client_avatar_id] = { + client_avatar_id: client_avatar_id, + avatar_id: avatar_version || response_avatar_version, + url: yield this._response_url(response, type), + type: type + }; + }); + } + create_avatar_download(client_avatar_id) { + log.debug(LogCategory.GENERAL, "Requesting download for avatar %s", client_avatar_id); + return this.handle.download_file("", "/avatar_" + client_avatar_id); + } + _load_avatar(client_avatar_id, avatar_version) { + return __awaiter(this, void 0, void 0, function* () { + try { + let download_key; + try { + download_key = yield this.create_avatar_download(client_avatar_id); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.Zfxv8Gh8 || (_translations.Zfxv8Gh8 = tr("Could not request download for avatar %s: %o")), client_avatar_id, error); + throw "failed to request avatar download"; + } + const downloader = transfer.spawn_download_transfer(download_key); + let response; + try { + response = yield downloader.request_file(); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.RpfhT5G_ || (_translations.RpfhT5G_ = tr("Could not download avatar %s: %o")), client_avatar_id, error); + throw "failed to download avatar"; + } + const type = image_type(response.headers.get('X-media-bytes')); + const media = media_image_type(type); + yield AvatarManager.cache.put_cache('avatar_' + client_avatar_id, response.clone(), "image/" + media, { + "X-avatar-version": avatar_version + }); + const url = yield this._response_url(response.clone(), type); + return this._cached_avatars[client_avatar_id] = { + client_avatar_id: client_avatar_id, + avatar_id: avatar_version, + url: url, + type: type + }; + } + finally { + this._loading_promises[client_avatar_id] = undefined; + } + }); + } + /* loads an avatar by the avatar id and optional with the avatar version */ + load_avatar(client_avatar_id, avatar_version) { + return this._loading_promises[client_avatar_id] || (this._loading_promises[client_avatar_id] = this._load_avatar(client_avatar_id, avatar_version)); + } + generate_client_tag(client) { + return this.generate_tag(client.avatarId(), client.properties.client_flag_avatar); + } + update_cache(client_avatar_id, avatar_id) { + const _cached = this._cached_avatars[client_avatar_id]; + if (_cached) { + if (_cached.avatar_id === avatar_id) + return; /* cache is up2date */ + log.info(LogCategory.GENERAL, _translations.qcVDOT3t || (_translations.qcVDOT3t = tr("Deleting cached avatar for client %s. Cached version: %s; New version: %s")), client_avatar_id, _cached.avatar_id, avatar_id); + delete this._cached_avatars[client_avatar_id]; + AvatarManager.cache.delete("avatar_" + client_avatar_id).catch(error => { + log.error(LogCategory.GENERAL, _translations.KjfmxVkY || (_translations.KjfmxVkY = tr("Failed to delete cached avatar for client %o: %o")), client_avatar_id, error); + }); + } + else { + this.resolved_cached(client_avatar_id).then(avatar => { + if (avatar && avatar.avatar_id !== avatar_id) { + /* this time we ensured that its cached */ + this.update_cache(client_avatar_id, avatar_id); + } + }).catch(error => { + log.error(LogCategory.GENERAL, _translations.N9t94bx_ || (_translations.N9t94bx_ = tr("Failed to delete cached avatar for client %o (cache lookup failed): %o")), client_avatar_id, error); + }); + } + } + generate_tag(client_avatar_id, avatar_id, options) { + options = options || {}; + let avatar_container = $.spawn("div"); + let avatar_image = $.spawn("img").attr("alt", _translations.h_SQX0Ol || (_translations.h_SQX0Ol = tr("Client avatar"))); + let cached_avatar = this._cached_avatars[client_avatar_id]; + if (avatar_id === "") { + avatar_container.append(this.generate_default_image()); + } + else if (cached_avatar && cached_avatar.avatar_id == avatar_id) { + avatar_image.attr("src", cached_avatar.url); + avatar_container.append(avatar_image); + if (options.callback_image) + options.callback_image(avatar_image); + if (options.callback_avatar) + options.callback_avatar(cached_avatar); + } + else { + let loader_image = $.spawn("img"); + loader_image.attr("src", "img/loading_image.svg").css("width", "75%"); + avatar_container.append(loader_image); + (() => __awaiter(this, void 0, void 0, function* () { + let avatar; + try { + avatar = yield this.resolved_cached(client_avatar_id, avatar_id); + } + catch (error) { + log.error(LogCategory.CLIENT, error); + } + if (!avatar) + avatar = yield this.load_avatar(client_avatar_id, avatar_id); + if (!avatar) + throw "failed to load avatar"; + if (options.callback_avatar) + options.callback_avatar(avatar); + avatar_image.attr("src", avatar.url); + avatar_image.css("opacity", 0); + avatar_container.append(avatar_image); + loader_image.animate({ opacity: 0 }, 50, () => { + loader_image.remove(); + avatar_image.animate({ opacity: 1 }, 150, () => { + if (options.callback_image) + options.callback_image(avatar_image); + }); + }); + }))().catch(reason => { + log.error(LogCategory.CLIENT, _translations.W3cTHtbm || (_translations.W3cTHtbm = tr("Could not load avatar for id %s. Reason: %s")), client_avatar_id, reason); + //TODO Broken image + loader_image.addClass("icon client-warning").attr("tag", (_translations.n7xfvxab || (_translations.n7xfvxab = tr("Could not load avatar "))) + client_avatar_id); + }); + } + return avatar_container; + } + unique_id_2_avatar_id(unique_id) { + function str2ab(str) { + let buf = new ArrayBuffer(str.length); // 2 bytes for each char + let bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; + } + try { + let raw = atob(unique_id); + let input = hex.encode(str2ab(raw)); + let result = ""; + for (let index = 0; index < input.length; index++) { + let c = input.charAt(index); + let offset = 0; + if (c >= '0' && c <= '9') + offset = c.charCodeAt(0) - '0'.charCodeAt(0); + else if (c >= 'A' && c <= 'F') + offset = c.charCodeAt(0) - 'A'.charCodeAt(0) + 0x0A; + else if (c >= 'a' && c <= 'f') + offset = c.charCodeAt(0) - 'a'.charCodeAt(0) + 0x0A; + result += String.fromCharCode('a'.charCodeAt(0) + offset); + } + return result; + } + catch (e) { //invalid base 64 (like music bot etc) + return undefined; + } + } + generate_default_image() { + return $.spawn("img").attr("src", "img/style/avatar.png").css({ width: '100%', height: '100%' }); + } + generate_chat_tag(client, client_unique_id, callback_loaded) { + let client_handle; + if (typeof (client.id) == "number") + client_handle = this.handle.handle.channelTree.findClient(client.id); + if (!client_handle && typeof (client.id) == "number") { + client_handle = this.handle.handle.channelTree.find_client_by_dbid(client.database_id); + } + if (client_handle && client_handle.clientUid() !== client_unique_id) + client_handle = undefined; + const container = $.spawn("div").addClass("avatar"); + if (client_handle && !client_handle.properties.client_flag_avatar) + return container.append(this.generate_default_image()); + const avatar_id = client_handle ? client_handle.avatarId() : this.unique_id_2_avatar_id(client_unique_id); + if (avatar_id) { + if (this._cached_avatars[avatar_id]) { /* Test if we're may able to load the client avatar sync without a loading screen */ + const cache = this._cached_avatars[avatar_id]; + log.debug(LogCategory.GENERAL, _translations.N5oq_P39 || (_translations.N5oq_P39 = tr("Using cached avatar. ID: %o | Version: %o (Cached: %o)")), avatar_id, client_handle ? client_handle.properties.client_flag_avatar : undefined, cache.avatar_id); + if (!client_handle || client_handle.properties.client_flag_avatar == cache.avatar_id) { + const image = $.spawn("img").attr("src", cache.url).css({ width: '100%', height: '100%' }); + return container.append(image); + } + } + const image_loading = $.spawn("img").attr("src", "img/loading_image.svg").css({ width: '100%', height: '100%' }); + /* lets actually load the avatar */ + (() => __awaiter(this, void 0, void 0, function* () { + let avatar; + let loaded_image = this.generate_default_image(); + log.debug(LogCategory.GENERAL, _translations.UUEkQF5a || (_translations.UUEkQF5a = tr("Resolving avatar. ID: %o | Version: %o")), avatar_id, client_handle ? client_handle.properties.client_flag_avatar : undefined); + try { + //TODO: Cache if avatar load failed and try again in some minutes/may just even consider using the default avatar 'till restart + try { + avatar = yield this.resolved_cached(avatar_id, client_handle ? client_handle.properties.client_flag_avatar : undefined); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.qeVWbl1T || (_translations.qeVWbl1T = tr("Failed to use cached avatar: %o")), error); + } + if (!avatar) + avatar = yield this.load_avatar(avatar_id, client_handle ? client_handle.properties.client_flag_avatar : undefined); + if (!avatar) + throw "no avatar present!"; + loaded_image = $.spawn("img").attr("src", avatar.url).css({ width: '100%', height: '100%' }); + } + catch (error) { + throw error; + } + finally { + container.children().remove(); + container.append(loaded_image); + } + }))().then(() => callback_loaded && callback_loaded(true)).catch(error => { + log.warn(LogCategory.CLIENT, _translations.yPO5jFrp || (_translations.yPO5jFrp = tr("Failed to load chat avatar for client %s. Error: %o")), client_unique_id, error); + callback_loaded && callback_loaded(false, error); + }); + image_loading.appendTo(container); + } + else { + this.generate_default_image().appendTo(container); + } + return container; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["455475c2b4f1106cf44d1f419a1499fbd8c48c3600ecab7876758f075e94cc7e"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["455475c2b4f1106cf44d1f419a1499fbd8c48c3600ecab7876758f075e94cc7e"] = "455475c2b4f1106cf44d1f419a1499fbd8c48c3600ecab7876758f075e94cc7e"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "FouNbgIJ", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (98,52)" }, { name: "SGTDpOXd", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (99,32)" }, { name: "WVeMm7W3", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (103,28)" }, { name: "rHWS7RQ3", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (113,17)" }, { name: "KlKrGumy", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (118,40)" }, { name: "dVNCwi_A", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (121,40)" }, { name: "T5m8C2bd", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (143,32)" }, { name: "A7HLafCo", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (192,45)" }, { name: "SPhST49p", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (201,48)" }, { name: "YDqxTCU7", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (204,48)" }, { name: "JLZp_U_5", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (226,45)" }, { name: "qarwc6lt", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (300,31)" }, { name: "PagJWgYb", path: "D:/TeaSpeak/web/shared/js/i18n/localize.ts (302,38)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} +var i18n; +(function (i18n) { + let translations = []; + let fast_translate = {}; + function tr(message, key) { + const sloppy = fast_translate[message]; + if (sloppy) + return sloppy; + log.info(LogCategory.I18N, "Translating \"%s\". Default: \"%s\"", key, message); + let translated = message; + for (const translation of translations) { + if (translation.key.message == message) { + translated = translation.translated; + break; + } + } + fast_translate[message] = translated; + return translated; + } + i18n.tr = tr; + function tra(message, ...args) { + message = tr(message); + return MessageHelper.formatMessage(message, ...args); + } + i18n.tra = tra; + function load_translation_file(url, path) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + $.ajax({ + url: url, + async: true, + success: result => { + try { + const file = (typeof (result) === "string" ? JSON.parse(result) : result); + if (!file) { + reject("Invalid json"); + return; + } + file.full_url = url; + file.path = path; + //TODO: Validate file + resolve(file); + } + catch (error) { + log.warn(LogCategory.I18N, _translations.FouNbgIJ || (_translations.FouNbgIJ = tr("Failed to load translation file %s. Failed to parse or process json: %o")), url, error); + reject(_translations.SGTDpOXd || (_translations.SGTDpOXd = tr("Failed to process or parse json!"))); + } + }, + error: (xhr, error) => { + reject((_translations.WVeMm7W3 || (_translations.WVeMm7W3 = tr("Failed to load file: "))) + error); + } + }); + }); + }); + } + function load_file(url, path) { + return load_translation_file(url, path).then((result) => __awaiter(this, void 0, void 0, function* () { + /* TODO: Improve this test?!*/ + try { + _translations.rHWS7RQ3 || (_translations.rHWS7RQ3 = tr("Dummy translation test")); + } + catch (error) { + throw "dummy test failed"; + } + log.info(LogCategory.I18N, _translations.KlKrGumy || (_translations.KlKrGumy = tr("Successfully initialized up translation file from %s")), url); + translations = result.translations; + })).catch(error => { + log.warn(LogCategory.I18N, _translations.dVNCwi_A || (_translations.dVNCwi_A = tr("Failed to load translation file from \"%s\". Error: %o")), url, error); + return Promise.reject(error); + }); + } + i18n.load_file = load_file; + function load_repository0(repo, reload) { + return __awaiter(this, void 0, void 0, function* () { + if (!repo.load_timestamp || repo.load_timestamp < 1000 || reload) { + const info_json = yield new Promise((resolve, reject) => { + $.ajax({ + url: repo.url + "/info.json", + async: true, + cache: !reload, + success: result => { + const file = (typeof (result) === "string" ? JSON.parse(result) : result); + if (!file) { + reject("Invalid json"); + return; + } + resolve(file); + }, + error: (xhr, error) => { + reject((_translations.T5m8C2bd || (_translations.T5m8C2bd = tr("Failed to load file: "))) + error); + } + }); + }); + Object.assign(repo, info_json); + } + if (!repo.unique_id) + repo.unique_id = guid(); + repo.translations = repo.translations || []; + repo.load_timestamp = Date.now(); + }); + } + function load_repository(url) { + return __awaiter(this, void 0, void 0, function* () { + const result = {}; + result.url = url; + yield load_repository0(result, false); + return result; + }); + } + i18n.load_repository = load_repository; + let config; + (function (config_1) { + const repository_config_key = "i18n.repository"; + let _cached_repository_config; + function repository_config() { + if (_cached_repository_config) + return _cached_repository_config; + const config_string = localStorage.getItem(repository_config_key); + let config; + try { + config = config_string ? JSON.parse(config_string) : {}; + } + catch (error) { + log.error(LogCategory.I18N, _translations.A7HLafCo || (_translations.A7HLafCo = tr("Failed to parse repository config: %o")), error); + } + config.repositories = config.repositories || []; + for (const repo of config.repositories) + (repo.repository || { load_timestamp: 0 }).load_timestamp = 0; + if (config.repositories.length == 0) { + //Add the default TeaSpeak repository + load_repository(StaticSettings.instance.static("i18n.default_repository", "https://web.teaspeak.de/i18n/")).then(repo => { + log.info(LogCategory.I18N, _translations.SPhST49p || (_translations.SPhST49p = tr("Successfully added default repository from \"%s\".")), repo.url); + register_repository(repo); + }).catch(error => { + log.warn(LogCategory.I18N, _translations.YDqxTCU7 || (_translations.YDqxTCU7 = tr("Failed to add default repository. Error: %o")), error); + }); + } + return _cached_repository_config = config; + } + config_1.repository_config = repository_config; + function save_repository_config() { + localStorage.setItem(repository_config_key, JSON.stringify(_cached_repository_config)); + } + config_1.save_repository_config = save_repository_config; + const translation_config_key = "i18n.translation"; + let _cached_translation_config; + function translation_config() { + if (_cached_translation_config) + return _cached_translation_config; + const config_string = localStorage.getItem(translation_config_key); + try { + _cached_translation_config = config_string ? JSON.parse(config_string) : {}; + } + catch (error) { + log.error(LogCategory.I18N, _translations.JLZp_U_5 || (_translations.JLZp_U_5 = tr("Failed to initialize translation config. Using default one. Error: %o")), error); + _cached_translation_config = {}; + } + return _cached_translation_config; + } + config_1.translation_config = translation_config; + function save_translation_config() { + localStorage.setItem(translation_config_key, JSON.stringify(_cached_translation_config)); + } + config_1.save_translation_config = save_translation_config; + })(config = i18n.config || (i18n.config = {})); + function register_repository(repository) { + if (!repository) + return; + for (const repo of config.repository_config().repositories) + if (repo.url == repository.url) + return; + config.repository_config().repositories.push(repository); + config.save_repository_config(); + } + i18n.register_repository = register_repository; + function registered_repositories() { + return config.repository_config().repositories.map(e => e.repository || { url: e.url, load_timestamp: 0 }); + } + i18n.registered_repositories = registered_repositories; + function delete_repository(repository) { + if (!repository) + return; + for (const repo of [...config.repository_config().repositories]) + if (repo.url == repository.url) { + config.repository_config().repositories.remove(repo); + } + config.save_repository_config(); + } + i18n.delete_repository = delete_repository; + function iterate_repositories(callback_entry) { + return __awaiter(this, void 0, void 0, function* () { + const promises = []; + for (const repository of registered_repositories()) { + promises.push(load_repository0(repository, false).then(() => callback_entry(repository)).catch(error => { + log.warn(LogCategory.I18N, "Failed to fetch repository %s. error: %o", repository.url, error); + })); + } + yield Promise.all(promises); + }); + } + i18n.iterate_repositories = iterate_repositories; + function select_translation(repository, entry) { + const cfg = config.translation_config(); + if (entry && repository) { + cfg.current_language = entry.name; + cfg.current_repository_url = repository.url; + cfg.current_translation_url = repository.url + entry.path; + cfg.current_translation_path = entry.path; + } + else { + cfg.current_language = undefined; + cfg.current_repository_url = undefined; + cfg.current_translation_url = undefined; + cfg.current_translation_path = undefined; + } + config.save_translation_config(); + } + i18n.select_translation = select_translation; + /* ATTENTION: This method is called before most other library inizialisations! */ + function initialize() { + return __awaiter(this, void 0, void 0, function* () { + const rcfg = config.repository_config(); /* initialize */ + const cfg = config.translation_config(); + if (cfg.current_translation_url) { + try { + yield load_file(cfg.current_translation_url, cfg.current_translation_path); + } + catch (error) { + console.error(_translations.qarwc6lt || (_translations.qarwc6lt = tr("Failed to initialize selected translation: %o")), error); + const show_error = () => { + createErrorModal(_translations.PagJWgYb || (_translations.PagJWgYb = tr("Translation System")), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open(); + }; + if (loader.running()) + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + priority: 10, + function: () => __awaiter(this, void 0, void 0, function* () { return show_error(); }), + name: "I18N error display" + }); + else + show_error(); + } + } + // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/de_DE.translation"); + // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/test.json"); + }); + } + i18n.initialize = initialize; +})(i18n || (i18n = {})); +// @ts-ignore +const tr = i18n.tr; +const tra = i18n.tra; +window.tr = i18n.tr; +window.tra = i18n.tra; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["63756334f56b822c20cbf8616d626c6c4d92049d142c2a8274090f6c288c800a"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["63756334f56b822c20cbf8616d626c6c4d92049d142c2a8274090f6c288c800a"] = "63756334f56b822c20cbf8616d626c6c4d92049d142c2a8274090f6c288c800a"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "n_KYAaMS", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (397,13)" }, { name: "XWPfjb2K", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (399,22)" }, { name: "le2qQrVZ", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (399,38)" }, { name: "_idwzRBJ", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (400,48)" }, { name: "tzCmjh5N", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (401,44)" }, { name: "HH613O5m", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (508,16)" }, { name: "mwOSnQ5t", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (509,20)" }, { name: "biW_sg0z", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (510,20)" }, { name: "XEpW1KUu", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (511,20)" }, { name: "xQjKeR1B", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (512,20)" }, { name: "Qaqyrcb5", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (513,16)" }, { name: "VPP72Y7O", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (514,20)" }, { name: "sRQvs1pe", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (515,20)" }, { name: "MKTJCaHB", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (516,20)" }, { name: "o_mBuxpj", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (517,16)" }, { name: "Ke3sTyPP", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (518,20)" }, { name: "w1vxsh9K", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (519,20)" }, { name: "tYJyWqOO", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (520,20)" }, { name: "WZsNfPyK", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (521,20)" }, { name: "WIiqWiL4", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (522,20)" }, { name: "TOhfWBxb", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (523,16)" }, { name: "_rPytECo", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (524,20)" }, { name: "CJT92u8o", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (525,20)" }, { name: "F7lDIM5W", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (526,20)" }, { name: "p978M8l6", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (527,20)" }, { name: "nj0btL0v", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (528,16)" }, { name: "yFgZNi8s", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (529,20)" }, { name: "SiWRTYIe", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (530,20)" }, { name: "UZZVYe0I", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (531,20)" }, { name: "t7dRXZeR", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (532,20)" }, { name: "bGpv2bKN", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (534,16)" }, { name: "mPnGQldc", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (550,51)" }, { name: "xNZTALgn", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (646,75)" }, { name: "DKPKNRNP", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (655,30)" }, { name: "xRgg5id2", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (683,43)" }, { name: "zjGxpKHl", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (692,47)" }, { name: "CTD6hjuf", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (701,75)" }, { name: "bczqMhnT", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (719,55)" }, { name: "JhTXHwBa", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (740,44)" }, { name: "G5Jy9mCS", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (917,100)" }, { name: "X_wUbyDP", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (1022,44)" }, { name: "va1TCPgM", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (1025,47)" }, { name: "dpJgGcEJ", path: "D:/TeaSpeak/web/shared/js/permission/PermissionManager.ts (1047,27)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var PermissionType; +(function (PermissionType) { + PermissionType["B_SERVERINSTANCE_HELP_VIEW"] = "b_serverinstance_help_view"; + PermissionType["B_SERVERINSTANCE_VERSION_VIEW"] = "b_serverinstance_version_view"; + PermissionType["B_SERVERINSTANCE_INFO_VIEW"] = "b_serverinstance_info_view"; + PermissionType["B_SERVERINSTANCE_VIRTUALSERVER_LIST"] = "b_serverinstance_virtualserver_list"; + PermissionType["B_SERVERINSTANCE_BINDING_LIST"] = "b_serverinstance_binding_list"; + PermissionType["B_SERVERINSTANCE_PERMISSION_LIST"] = "b_serverinstance_permission_list"; + PermissionType["B_SERVERINSTANCE_PERMISSION_FIND"] = "b_serverinstance_permission_find"; + PermissionType["B_VIRTUALSERVER_CREATE"] = "b_virtualserver_create"; + PermissionType["B_VIRTUALSERVER_DELETE"] = "b_virtualserver_delete"; + PermissionType["B_VIRTUALSERVER_START_ANY"] = "b_virtualserver_start_any"; + PermissionType["B_VIRTUALSERVER_STOP_ANY"] = "b_virtualserver_stop_any"; + PermissionType["B_VIRTUALSERVER_CHANGE_MACHINE_ID"] = "b_virtualserver_change_machine_id"; + PermissionType["B_VIRTUALSERVER_CHANGE_TEMPLATE"] = "b_virtualserver_change_template"; + PermissionType["B_SERVERQUERY_LOGIN"] = "b_serverquery_login"; + PermissionType["B_SERVERINSTANCE_TEXTMESSAGE_SEND"] = "b_serverinstance_textmessage_send"; + PermissionType["B_SERVERINSTANCE_LOG_VIEW"] = "b_serverinstance_log_view"; + PermissionType["B_SERVERINSTANCE_LOG_ADD"] = "b_serverinstance_log_add"; + PermissionType["B_SERVERINSTANCE_STOP"] = "b_serverinstance_stop"; + PermissionType["B_SERVERINSTANCE_MODIFY_SETTINGS"] = "b_serverinstance_modify_settings"; + PermissionType["B_SERVERINSTANCE_MODIFY_QUERYGROUP"] = "b_serverinstance_modify_querygroup"; + PermissionType["B_SERVERINSTANCE_MODIFY_TEMPLATES"] = "b_serverinstance_modify_templates"; + PermissionType["B_VIRTUALSERVER_SELECT"] = "b_virtualserver_select"; + PermissionType["B_VIRTUALSERVER_SELECT_GODMODE"] = "b_virtualserver_select_godmode"; + PermissionType["B_VIRTUALSERVER_INFO_VIEW"] = "b_virtualserver_info_view"; + PermissionType["B_VIRTUALSERVER_CONNECTIONINFO_VIEW"] = "b_virtualserver_connectioninfo_view"; + PermissionType["B_VIRTUALSERVER_CHANNEL_LIST"] = "b_virtualserver_channel_list"; + PermissionType["B_VIRTUALSERVER_CHANNEL_SEARCH"] = "b_virtualserver_channel_search"; + PermissionType["B_VIRTUALSERVER_CLIENT_LIST"] = "b_virtualserver_client_list"; + PermissionType["B_VIRTUALSERVER_CLIENT_SEARCH"] = "b_virtualserver_client_search"; + PermissionType["B_VIRTUALSERVER_CLIENT_DBLIST"] = "b_virtualserver_client_dblist"; + PermissionType["B_VIRTUALSERVER_CLIENT_DBSEARCH"] = "b_virtualserver_client_dbsearch"; + PermissionType["B_VIRTUALSERVER_CLIENT_DBINFO"] = "b_virtualserver_client_dbinfo"; + PermissionType["B_VIRTUALSERVER_PERMISSION_FIND"] = "b_virtualserver_permission_find"; + PermissionType["B_VIRTUALSERVER_CUSTOM_SEARCH"] = "b_virtualserver_custom_search"; + PermissionType["B_VIRTUALSERVER_START"] = "b_virtualserver_start"; + PermissionType["B_VIRTUALSERVER_STOP"] = "b_virtualserver_stop"; + PermissionType["B_VIRTUALSERVER_TOKEN_LIST"] = "b_virtualserver_token_list"; + PermissionType["B_VIRTUALSERVER_TOKEN_ADD"] = "b_virtualserver_token_add"; + PermissionType["B_VIRTUALSERVER_TOKEN_USE"] = "b_virtualserver_token_use"; + PermissionType["B_VIRTUALSERVER_TOKEN_DELETE"] = "b_virtualserver_token_delete"; + PermissionType["B_VIRTUALSERVER_LOG_VIEW"] = "b_virtualserver_log_view"; + PermissionType["B_VIRTUALSERVER_LOG_ADD"] = "b_virtualserver_log_add"; + PermissionType["B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD"] = "b_virtualserver_join_ignore_password"; + PermissionType["B_VIRTUALSERVER_NOTIFY_REGISTER"] = "b_virtualserver_notify_register"; + PermissionType["B_VIRTUALSERVER_NOTIFY_UNREGISTER"] = "b_virtualserver_notify_unregister"; + PermissionType["B_VIRTUALSERVER_SNAPSHOT_CREATE"] = "b_virtualserver_snapshot_create"; + PermissionType["B_VIRTUALSERVER_SNAPSHOT_DEPLOY"] = "b_virtualserver_snapshot_deploy"; + PermissionType["B_VIRTUALSERVER_PERMISSION_RESET"] = "b_virtualserver_permission_reset"; + PermissionType["B_VIRTUALSERVER_MODIFY_NAME"] = "b_virtualserver_modify_name"; + PermissionType["B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE"] = "b_virtualserver_modify_welcomemessage"; + PermissionType["B_VIRTUALSERVER_MODIFY_MAXCLIENTS"] = "b_virtualserver_modify_maxclients"; + PermissionType["B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS"] = "b_virtualserver_modify_reserved_slots"; + PermissionType["B_VIRTUALSERVER_MODIFY_PASSWORD"] = "b_virtualserver_modify_password"; + PermissionType["B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP"] = "b_virtualserver_modify_default_servergroup"; + PermissionType["B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP"] = "b_virtualserver_modify_default_musicgroup"; + PermissionType["B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP"] = "b_virtualserver_modify_default_channelgroup"; + PermissionType["B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP"] = "b_virtualserver_modify_default_channeladmingroup"; + PermissionType["B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE"] = "b_virtualserver_modify_channel_forced_silence"; + PermissionType["B_VIRTUALSERVER_MODIFY_COMPLAIN"] = "b_virtualserver_modify_complain"; + PermissionType["B_VIRTUALSERVER_MODIFY_ANTIFLOOD"] = "b_virtualserver_modify_antiflood"; + PermissionType["B_VIRTUALSERVER_MODIFY_FT_SETTINGS"] = "b_virtualserver_modify_ft_settings"; + PermissionType["B_VIRTUALSERVER_MODIFY_FT_QUOTAS"] = "b_virtualserver_modify_ft_quotas"; + PermissionType["B_VIRTUALSERVER_MODIFY_HOSTMESSAGE"] = "b_virtualserver_modify_hostmessage"; + PermissionType["B_VIRTUALSERVER_MODIFY_HOSTBANNER"] = "b_virtualserver_modify_hostbanner"; + PermissionType["B_VIRTUALSERVER_MODIFY_HOSTBUTTON"] = "b_virtualserver_modify_hostbutton"; + PermissionType["B_VIRTUALSERVER_MODIFY_PORT"] = "b_virtualserver_modify_port"; + PermissionType["B_VIRTUALSERVER_MODIFY_HOST"] = "b_virtualserver_modify_host"; + PermissionType["B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES"] = "b_virtualserver_modify_default_messages"; + PermissionType["B_VIRTUALSERVER_MODIFY_AUTOSTART"] = "b_virtualserver_modify_autostart"; + PermissionType["B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL"] = "b_virtualserver_modify_needed_identity_security_level"; + PermissionType["B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR"] = "b_virtualserver_modify_priority_speaker_dimm_modificator"; + PermissionType["B_VIRTUALSERVER_MODIFY_LOG_SETTINGS"] = "b_virtualserver_modify_log_settings"; + PermissionType["B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION"] = "b_virtualserver_modify_min_client_version"; + PermissionType["B_VIRTUALSERVER_MODIFY_ICON_ID"] = "b_virtualserver_modify_icon_id"; + PermissionType["B_VIRTUALSERVER_MODIFY_WEBLIST"] = "b_virtualserver_modify_weblist"; + PermissionType["B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE"] = "b_virtualserver_modify_codec_encryption_mode"; + PermissionType["B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS"] = "b_virtualserver_modify_temporary_passwords"; + PermissionType["B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN"] = "b_virtualserver_modify_temporary_passwords_own"; + PermissionType["B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT"] = "b_virtualserver_modify_channel_temp_delete_delay_default"; + PermissionType["B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT"] = "b_virtualserver_modify_music_bot_limit"; + PermissionType["B_VIRTUALSERVER_MODIFY_COUNTRY_CODE"] = "b_virtualserver_modify_country_code"; + PermissionType["I_CHANNEL_MIN_DEPTH"] = "i_channel_min_depth"; + PermissionType["I_CHANNEL_MAX_DEPTH"] = "i_channel_max_depth"; + PermissionType["B_CHANNEL_GROUP_INHERITANCE_END"] = "b_channel_group_inheritance_end"; + PermissionType["I_CHANNEL_PERMISSION_MODIFY_POWER"] = "i_channel_permission_modify_power"; + PermissionType["I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER"] = "i_channel_needed_permission_modify_power"; + PermissionType["B_CHANNEL_INFO_VIEW"] = "b_channel_info_view"; + PermissionType["B_CHANNEL_CREATE_CHILD"] = "b_channel_create_child"; + PermissionType["B_CHANNEL_CREATE_PERMANENT"] = "b_channel_create_permanent"; + PermissionType["B_CHANNEL_CREATE_SEMI_PERMANENT"] = "b_channel_create_semi_permanent"; + PermissionType["B_CHANNEL_CREATE_TEMPORARY"] = "b_channel_create_temporary"; + PermissionType["B_CHANNEL_CREATE_PRIVATE"] = "b_channel_create_private"; + PermissionType["B_CHANNEL_CREATE_WITH_TOPIC"] = "b_channel_create_with_topic"; + PermissionType["B_CHANNEL_CREATE_WITH_DESCRIPTION"] = "b_channel_create_with_description"; + PermissionType["B_CHANNEL_CREATE_WITH_PASSWORD"] = "b_channel_create_with_password"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8"] = "b_channel_create_modify_with_codec_speex8"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16"] = "b_channel_create_modify_with_codec_speex16"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32"] = "b_channel_create_modify_with_codec_speex32"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48"] = "b_channel_create_modify_with_codec_celtmono48"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE"] = "b_channel_create_modify_with_codec_opusvoice"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC"] = "b_channel_create_modify_with_codec_opusmusic"; + PermissionType["I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY"] = "i_channel_create_modify_with_codec_maxquality"; + PermissionType["I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN"] = "i_channel_create_modify_with_codec_latency_factor_min"; + PermissionType["I_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_LENGTH"] = "i_channel_create_modify_conversation_history_length"; + PermissionType["B_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_UNLIMITED"] = "b_channel_create_modify_conversation_history_unlimited"; + PermissionType["B_CHANNEL_CREATE_MODIFY_CONVERSATION_PRIVATE"] = "b_channel_create_modify_conversation_private"; + PermissionType["B_CHANNEL_CREATE_WITH_MAXCLIENTS"] = "b_channel_create_with_maxclients"; + PermissionType["B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS"] = "b_channel_create_with_maxfamilyclients"; + PermissionType["B_CHANNEL_CREATE_WITH_SORTORDER"] = "b_channel_create_with_sortorder"; + PermissionType["B_CHANNEL_CREATE_WITH_DEFAULT"] = "b_channel_create_with_default"; + PermissionType["B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER"] = "b_channel_create_with_needed_talk_power"; + PermissionType["B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD"] = "b_channel_create_modify_with_force_password"; + PermissionType["I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY"] = "i_channel_create_modify_with_temp_delete_delay"; + PermissionType["B_CHANNEL_MODIFY_PARENT"] = "b_channel_modify_parent"; + PermissionType["B_CHANNEL_MODIFY_MAKE_DEFAULT"] = "b_channel_modify_make_default"; + PermissionType["B_CHANNEL_MODIFY_MAKE_PERMANENT"] = "b_channel_modify_make_permanent"; + PermissionType["B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT"] = "b_channel_modify_make_semi_permanent"; + PermissionType["B_CHANNEL_MODIFY_MAKE_TEMPORARY"] = "b_channel_modify_make_temporary"; + PermissionType["B_CHANNEL_MODIFY_NAME"] = "b_channel_modify_name"; + PermissionType["B_CHANNEL_MODIFY_TOPIC"] = "b_channel_modify_topic"; + PermissionType["B_CHANNEL_MODIFY_DESCRIPTION"] = "b_channel_modify_description"; + PermissionType["B_CHANNEL_MODIFY_PASSWORD"] = "b_channel_modify_password"; + PermissionType["B_CHANNEL_MODIFY_CODEC"] = "b_channel_modify_codec"; + PermissionType["B_CHANNEL_MODIFY_CODEC_QUALITY"] = "b_channel_modify_codec_quality"; + PermissionType["B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR"] = "b_channel_modify_codec_latency_factor"; + PermissionType["B_CHANNEL_MODIFY_MAXCLIENTS"] = "b_channel_modify_maxclients"; + PermissionType["B_CHANNEL_MODIFY_MAXFAMILYCLIENTS"] = "b_channel_modify_maxfamilyclients"; + PermissionType["B_CHANNEL_MODIFY_SORTORDER"] = "b_channel_modify_sortorder"; + PermissionType["B_CHANNEL_MODIFY_NEEDED_TALK_POWER"] = "b_channel_modify_needed_talk_power"; + PermissionType["I_CHANNEL_MODIFY_POWER"] = "i_channel_modify_power"; + PermissionType["I_CHANNEL_NEEDED_MODIFY_POWER"] = "i_channel_needed_modify_power"; + PermissionType["B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED"] = "b_channel_modify_make_codec_encrypted"; + PermissionType["B_CHANNEL_MODIFY_TEMP_DELETE_DELAY"] = "b_channel_modify_temp_delete_delay"; + PermissionType["B_CHANNEL_DELETE_PERMANENT"] = "b_channel_delete_permanent"; + PermissionType["B_CHANNEL_DELETE_SEMI_PERMANENT"] = "b_channel_delete_semi_permanent"; + PermissionType["B_CHANNEL_DELETE_TEMPORARY"] = "b_channel_delete_temporary"; + PermissionType["B_CHANNEL_DELETE_FLAG_FORCE"] = "b_channel_delete_flag_force"; + PermissionType["I_CHANNEL_DELETE_POWER"] = "i_channel_delete_power"; + PermissionType["B_CHANNEL_CONVERSATION_MESSAGE_DELETE"] = "b_channel_conversation_message_delete"; + PermissionType["I_CHANNEL_NEEDED_DELETE_POWER"] = "i_channel_needed_delete_power"; + PermissionType["B_CHANNEL_JOIN_PERMANENT"] = "b_channel_join_permanent"; + PermissionType["B_CHANNEL_JOIN_SEMI_PERMANENT"] = "b_channel_join_semi_permanent"; + PermissionType["B_CHANNEL_JOIN_TEMPORARY"] = "b_channel_join_temporary"; + PermissionType["B_CHANNEL_JOIN_IGNORE_PASSWORD"] = "b_channel_join_ignore_password"; + PermissionType["B_CHANNEL_JOIN_IGNORE_MAXCLIENTS"] = "b_channel_join_ignore_maxclients"; + PermissionType["B_CHANNEL_IGNORE_VIEW_POWER"] = "b_channel_ignore_view_power"; + PermissionType["I_CHANNEL_JOIN_POWER"] = "i_channel_join_power"; + PermissionType["I_CHANNEL_NEEDED_JOIN_POWER"] = "i_channel_needed_join_power"; + PermissionType["B_CHANNEL_IGNORE_JOIN_POWER"] = "b_channel_ignore_join_power"; + PermissionType["B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER"] = "b_channel_ignore_description_view_power"; + PermissionType["I_CHANNEL_VIEW_POWER"] = "i_channel_view_power"; + PermissionType["I_CHANNEL_NEEDED_VIEW_POWER"] = "i_channel_needed_view_power"; + PermissionType["I_CHANNEL_SUBSCRIBE_POWER"] = "i_channel_subscribe_power"; + PermissionType["I_CHANNEL_NEEDED_SUBSCRIBE_POWER"] = "i_channel_needed_subscribe_power"; + PermissionType["I_CHANNEL_DESCRIPTION_VIEW_POWER"] = "i_channel_description_view_power"; + PermissionType["I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER"] = "i_channel_needed_description_view_power"; + PermissionType["I_ICON_ID"] = "i_icon_id"; + PermissionType["I_MAX_ICON_FILESIZE"] = "i_max_icon_filesize"; + PermissionType["I_MAX_PLAYLIST_SIZE"] = "i_max_playlist_size"; + PermissionType["I_MAX_PLAYLISTS"] = "i_max_playlists"; + PermissionType["B_ICON_MANAGE"] = "b_icon_manage"; + PermissionType["B_GROUP_IS_PERMANENT"] = "b_group_is_permanent"; + PermissionType["I_GROUP_AUTO_UPDATE_TYPE"] = "i_group_auto_update_type"; + PermissionType["I_GROUP_AUTO_UPDATE_MAX_VALUE"] = "i_group_auto_update_max_value"; + PermissionType["I_GROUP_SORT_ID"] = "i_group_sort_id"; + PermissionType["I_GROUP_SHOW_NAME_IN_TREE"] = "i_group_show_name_in_tree"; + PermissionType["B_VIRTUALSERVER_SERVERGROUP_CREATE"] = "b_virtualserver_servergroup_create"; + PermissionType["B_VIRTUALSERVER_SERVERGROUP_LIST"] = "b_virtualserver_servergroup_list"; + PermissionType["B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST"] = "b_virtualserver_servergroup_permission_list"; + PermissionType["B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST"] = "b_virtualserver_servergroup_client_list"; + PermissionType["B_VIRTUALSERVER_CHANNELGROUP_CREATE"] = "b_virtualserver_channelgroup_create"; + PermissionType["B_VIRTUALSERVER_CHANNELGROUP_LIST"] = "b_virtualserver_channelgroup_list"; + PermissionType["B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST"] = "b_virtualserver_channelgroup_permission_list"; + PermissionType["B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST"] = "b_virtualserver_channelgroup_client_list"; + PermissionType["B_VIRTUALSERVER_CLIENT_PERMISSION_LIST"] = "b_virtualserver_client_permission_list"; + PermissionType["B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST"] = "b_virtualserver_channel_permission_list"; + PermissionType["B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST"] = "b_virtualserver_channelclient_permission_list"; + PermissionType["B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST"] = "b_virtualserver_playlist_permission_list"; + PermissionType["I_SERVER_GROUP_MODIFY_POWER"] = "i_server_group_modify_power"; + PermissionType["I_SERVER_GROUP_NEEDED_MODIFY_POWER"] = "i_server_group_needed_modify_power"; + PermissionType["I_SERVER_GROUP_MEMBER_ADD_POWER"] = "i_server_group_member_add_power"; + PermissionType["I_SERVER_GROUP_SELF_ADD_POWER"] = "i_server_group_self_add_power"; + PermissionType["I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER"] = "i_server_group_needed_member_add_power"; + PermissionType["I_SERVER_GROUP_MEMBER_REMOVE_POWER"] = "i_server_group_member_remove_power"; + PermissionType["I_SERVER_GROUP_SELF_REMOVE_POWER"] = "i_server_group_self_remove_power"; + PermissionType["I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER"] = "i_server_group_needed_member_remove_power"; + PermissionType["I_CHANNEL_GROUP_MODIFY_POWER"] = "i_channel_group_modify_power"; + PermissionType["I_CHANNEL_GROUP_NEEDED_MODIFY_POWER"] = "i_channel_group_needed_modify_power"; + PermissionType["I_CHANNEL_GROUP_MEMBER_ADD_POWER"] = "i_channel_group_member_add_power"; + PermissionType["I_CHANNEL_GROUP_SELF_ADD_POWER"] = "i_channel_group_self_add_power"; + PermissionType["I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER"] = "i_channel_group_needed_member_add_power"; + PermissionType["I_CHANNEL_GROUP_MEMBER_REMOVE_POWER"] = "i_channel_group_member_remove_power"; + PermissionType["I_CHANNEL_GROUP_SELF_REMOVE_POWER"] = "i_channel_group_self_remove_power"; + PermissionType["I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER"] = "i_channel_group_needed_member_remove_power"; + PermissionType["I_GROUP_MEMBER_ADD_POWER"] = "i_group_member_add_power"; + PermissionType["I_GROUP_NEEDED_MEMBER_ADD_POWER"] = "i_group_needed_member_add_power"; + PermissionType["I_GROUP_MEMBER_REMOVE_POWER"] = "i_group_member_remove_power"; + PermissionType["I_GROUP_NEEDED_MEMBER_REMOVE_POWER"] = "i_group_needed_member_remove_power"; + PermissionType["I_GROUP_MODIFY_POWER"] = "i_group_modify_power"; + PermissionType["I_GROUP_NEEDED_MODIFY_POWER"] = "i_group_needed_modify_power"; + PermissionType["I_PERMISSION_MODIFY_POWER"] = "i_permission_modify_power"; + PermissionType["B_PERMISSION_MODIFY_POWER_IGNORE"] = "b_permission_modify_power_ignore"; + PermissionType["B_VIRTUALSERVER_SERVERGROUP_DELETE"] = "b_virtualserver_servergroup_delete"; + PermissionType["B_VIRTUALSERVER_CHANNELGROUP_DELETE"] = "b_virtualserver_channelgroup_delete"; + PermissionType["I_CLIENT_PERMISSION_MODIFY_POWER"] = "i_client_permission_modify_power"; + PermissionType["I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER"] = "i_client_needed_permission_modify_power"; + PermissionType["I_CLIENT_MAX_CLONES_UID"] = "i_client_max_clones_uid"; + PermissionType["I_CLIENT_MAX_CLONES_IP"] = "i_client_max_clones_ip"; + PermissionType["I_CLIENT_MAX_CLONES_HWID"] = "i_client_max_clones_hwid"; + PermissionType["I_CLIENT_MAX_IDLETIME"] = "i_client_max_idletime"; + PermissionType["I_CLIENT_MAX_AVATAR_FILESIZE"] = "i_client_max_avatar_filesize"; + PermissionType["I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS"] = "i_client_max_channel_subscriptions"; + PermissionType["I_CLIENT_MAX_CHANNELS"] = "i_client_max_channels"; + PermissionType["I_CLIENT_MAX_TEMPORARY_CHANNELS"] = "i_client_max_temporary_channels"; + PermissionType["I_CLIENT_MAX_SEMI_CHANNELS"] = "i_client_max_semi_channels"; + PermissionType["I_CLIENT_MAX_PERMANENT_CHANNELS"] = "i_client_max_permanent_channels"; + PermissionType["B_CLIENT_USE_PRIORITY_SPEAKER"] = "b_client_use_priority_speaker"; + PermissionType["B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS"] = "b_client_skip_channelgroup_permissions"; + PermissionType["B_CLIENT_FORCE_PUSH_TO_TALK"] = "b_client_force_push_to_talk"; + PermissionType["B_CLIENT_IGNORE_BANS"] = "b_client_ignore_bans"; + PermissionType["B_CLIENT_IGNORE_VPN"] = "b_client_ignore_vpn"; + PermissionType["B_CLIENT_IGNORE_ANTIFLOOD"] = "b_client_ignore_antiflood"; + PermissionType["B_CLIENT_ENFORCE_VALID_HWID"] = "b_client_enforce_valid_hwid"; + PermissionType["B_CLIENT_ALLOW_INVALID_PACKET"] = "b_client_allow_invalid_packet"; + PermissionType["B_CLIENT_ALLOW_INVALID_BADGES"] = "b_client_allow_invalid_badges"; + PermissionType["B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND"] = "b_client_issue_client_query_command"; + PermissionType["B_CLIENT_USE_RESERVED_SLOT"] = "b_client_use_reserved_slot"; + PermissionType["B_CLIENT_USE_CHANNEL_COMMANDER"] = "b_client_use_channel_commander"; + PermissionType["B_CLIENT_REQUEST_TALKER"] = "b_client_request_talker"; + PermissionType["B_CLIENT_AVATAR_DELETE_OTHER"] = "b_client_avatar_delete_other"; + PermissionType["B_CLIENT_IS_STICKY"] = "b_client_is_sticky"; + PermissionType["B_CLIENT_IGNORE_STICKY"] = "b_client_ignore_sticky"; + PermissionType["B_CLIENT_MUSIC_CREATE_PERMANENT"] = "b_client_music_create_permanent"; + PermissionType["B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT"] = "b_client_music_create_semi_permanent"; + PermissionType["B_CLIENT_MUSIC_CREATE_TEMPORARY"] = "b_client_music_create_temporary"; + PermissionType["B_CLIENT_MUSIC_MODIFY_PERMANENT"] = "b_client_music_modify_permanent"; + PermissionType["B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT"] = "b_client_music_modify_semi_permanent"; + PermissionType["B_CLIENT_MUSIC_MODIFY_TEMPORARY"] = "b_client_music_modify_temporary"; + PermissionType["I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME"] = "i_client_music_create_modify_max_volume"; + PermissionType["I_CLIENT_MUSIC_LIMIT"] = "i_client_music_limit"; + PermissionType["I_CLIENT_MUSIC_NEEDED_DELETE_POWER"] = "i_client_music_needed_delete_power"; + PermissionType["I_CLIENT_MUSIC_DELETE_POWER"] = "i_client_music_delete_power"; + PermissionType["I_CLIENT_MUSIC_PLAY_POWER"] = "i_client_music_play_power"; + PermissionType["I_CLIENT_MUSIC_NEEDED_PLAY_POWER"] = "i_client_music_needed_play_power"; + PermissionType["I_CLIENT_MUSIC_MODIFY_POWER"] = "i_client_music_modify_power"; + PermissionType["I_CLIENT_MUSIC_NEEDED_MODIFY_POWER"] = "i_client_music_needed_modify_power"; + PermissionType["I_CLIENT_MUSIC_RENAME_POWER"] = "i_client_music_rename_power"; + PermissionType["I_CLIENT_MUSIC_NEEDED_RENAME_POWER"] = "i_client_music_needed_rename_power"; + PermissionType["B_PLAYLIST_CREATE"] = "b_playlist_create"; + PermissionType["I_PLAYLIST_VIEW_POWER"] = "i_playlist_view_power"; + PermissionType["I_PLAYLIST_NEEDED_VIEW_POWER"] = "i_playlist_needed_view_power"; + PermissionType["I_PLAYLIST_MODIFY_POWER"] = "i_playlist_modify_power"; + PermissionType["I_PLAYLIST_NEEDED_MODIFY_POWER"] = "i_playlist_needed_modify_power"; + PermissionType["I_PLAYLIST_PERMISSION_MODIFY_POWER"] = "i_playlist_permission_modify_power"; + PermissionType["I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER"] = "i_playlist_needed_permission_modify_power"; + PermissionType["I_PLAYLIST_DELETE_POWER"] = "i_playlist_delete_power"; + PermissionType["I_PLAYLIST_NEEDED_DELETE_POWER"] = "i_playlist_needed_delete_power"; + PermissionType["I_PLAYLIST_SONG_ADD_POWER"] = "i_playlist_song_add_power"; + PermissionType["I_PLAYLIST_SONG_NEEDED_ADD_POWER"] = "i_playlist_song_needed_add_power"; + PermissionType["I_PLAYLIST_SONG_REMOVE_POWER"] = "i_playlist_song_remove_power"; + PermissionType["I_PLAYLIST_SONG_NEEDED_REMOVE_POWER"] = "i_playlist_song_needed_remove_power"; + PermissionType["B_CLIENT_INFO_VIEW"] = "b_client_info_view"; + PermissionType["B_CLIENT_PERMISSIONOVERVIEW_VIEW"] = "b_client_permissionoverview_view"; + PermissionType["B_CLIENT_PERMISSIONOVERVIEW_OWN"] = "b_client_permissionoverview_own"; + PermissionType["B_CLIENT_REMOTEADDRESS_VIEW"] = "b_client_remoteaddress_view"; + PermissionType["I_CLIENT_SERVERQUERY_VIEW_POWER"] = "i_client_serverquery_view_power"; + PermissionType["I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER"] = "i_client_needed_serverquery_view_power"; + PermissionType["B_CLIENT_CUSTOM_INFO_VIEW"] = "b_client_custom_info_view"; + PermissionType["B_CLIENT_MUSIC_CHANNEL_LIST"] = "b_client_music_channel_list"; + PermissionType["B_CLIENT_MUSIC_SERVER_LIST"] = "b_client_music_server_list"; + PermissionType["I_CLIENT_MUSIC_INFO"] = "i_client_music_info"; + PermissionType["I_CLIENT_MUSIC_NEEDED_INFO"] = "i_client_music_needed_info"; + PermissionType["I_CLIENT_KICK_FROM_SERVER_POWER"] = "i_client_kick_from_server_power"; + PermissionType["I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER"] = "i_client_needed_kick_from_server_power"; + PermissionType["I_CLIENT_KICK_FROM_CHANNEL_POWER"] = "i_client_kick_from_channel_power"; + PermissionType["I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER"] = "i_client_needed_kick_from_channel_power"; + PermissionType["I_CLIENT_BAN_POWER"] = "i_client_ban_power"; + PermissionType["I_CLIENT_NEEDED_BAN_POWER"] = "i_client_needed_ban_power"; + PermissionType["I_CLIENT_MOVE_POWER"] = "i_client_move_power"; + PermissionType["I_CLIENT_NEEDED_MOVE_POWER"] = "i_client_needed_move_power"; + PermissionType["I_CLIENT_COMPLAIN_POWER"] = "i_client_complain_power"; + PermissionType["I_CLIENT_NEEDED_COMPLAIN_POWER"] = "i_client_needed_complain_power"; + PermissionType["B_CLIENT_COMPLAIN_LIST"] = "b_client_complain_list"; + PermissionType["B_CLIENT_COMPLAIN_DELETE_OWN"] = "b_client_complain_delete_own"; + PermissionType["B_CLIENT_COMPLAIN_DELETE"] = "b_client_complain_delete"; + PermissionType["B_CLIENT_BAN_LIST"] = "b_client_ban_list"; + PermissionType["B_CLIENT_BAN_LIST_GLOBAL"] = "b_client_ban_list_global"; + PermissionType["B_CLIENT_BAN_TRIGGER_LIST"] = "b_client_ban_trigger_list"; + PermissionType["B_CLIENT_BAN_CREATE"] = "b_client_ban_create"; + PermissionType["B_CLIENT_BAN_CREATE_GLOBAL"] = "b_client_ban_create_global"; + PermissionType["B_CLIENT_BAN_NAME"] = "b_client_ban_name"; + PermissionType["B_CLIENT_BAN_IP"] = "b_client_ban_ip"; + PermissionType["B_CLIENT_BAN_HWID"] = "b_client_ban_hwid"; + PermissionType["B_CLIENT_BAN_EDIT"] = "b_client_ban_edit"; + PermissionType["B_CLIENT_BAN_EDIT_GLOBAL"] = "b_client_ban_edit_global"; + PermissionType["B_CLIENT_BAN_DELETE_OWN"] = "b_client_ban_delete_own"; + PermissionType["B_CLIENT_BAN_DELETE"] = "b_client_ban_delete"; + PermissionType["B_CLIENT_BAN_DELETE_OWN_GLOBAL"] = "b_client_ban_delete_own_global"; + PermissionType["B_CLIENT_BAN_DELETE_GLOBAL"] = "b_client_ban_delete_global"; + PermissionType["I_CLIENT_BAN_MAX_BANTIME"] = "i_client_ban_max_bantime"; + PermissionType["I_CLIENT_PRIVATE_TEXTMESSAGE_POWER"] = "i_client_private_textmessage_power"; + PermissionType["I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER"] = "i_client_needed_private_textmessage_power"; + PermissionType["B_CLIENT_EVEN_TEXTMESSAGE_SEND"] = "b_client_even_textmessage_send"; + PermissionType["B_CLIENT_SERVER_TEXTMESSAGE_SEND"] = "b_client_server_textmessage_send"; + PermissionType["B_CLIENT_CHANNEL_TEXTMESSAGE_SEND"] = "b_client_channel_textmessage_send"; + PermissionType["B_CLIENT_OFFLINE_TEXTMESSAGE_SEND"] = "b_client_offline_textmessage_send"; + PermissionType["I_CLIENT_TALK_POWER"] = "i_client_talk_power"; + PermissionType["I_CLIENT_NEEDED_TALK_POWER"] = "i_client_needed_talk_power"; + PermissionType["I_CLIENT_POKE_POWER"] = "i_client_poke_power"; + PermissionType["I_CLIENT_NEEDED_POKE_POWER"] = "i_client_needed_poke_power"; + PermissionType["B_CLIENT_SET_FLAG_TALKER"] = "b_client_set_flag_talker"; + PermissionType["I_CLIENT_WHISPER_POWER"] = "i_client_whisper_power"; + PermissionType["I_CLIENT_NEEDED_WHISPER_POWER"] = "i_client_needed_whisper_power"; + PermissionType["B_CLIENT_MODIFY_DESCRIPTION"] = "b_client_modify_description"; + PermissionType["B_CLIENT_MODIFY_OWN_DESCRIPTION"] = "b_client_modify_own_description"; + PermissionType["B_CLIENT_USE_BBCODE_ANY"] = "b_client_use_bbcode_any"; + PermissionType["B_CLIENT_USE_BBCODE_URL"] = "b_client_use_bbcode_url"; + PermissionType["B_CLIENT_USE_BBCODE_IMAGE"] = "b_client_use_bbcode_image"; + PermissionType["B_CLIENT_MODIFY_DBPROPERTIES"] = "b_client_modify_dbproperties"; + PermissionType["B_CLIENT_DELETE_DBPROPERTIES"] = "b_client_delete_dbproperties"; + PermissionType["B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN"] = "b_client_create_modify_serverquery_login"; + PermissionType["B_CLIENT_QUERY_CREATE"] = "b_client_query_create"; + PermissionType["B_CLIENT_QUERY_LIST"] = "b_client_query_list"; + PermissionType["B_CLIENT_QUERY_LIST_OWN"] = "b_client_query_list_own"; + PermissionType["B_CLIENT_QUERY_RENAME"] = "b_client_query_rename"; + PermissionType["B_CLIENT_QUERY_RENAME_OWN"] = "b_client_query_rename_own"; + PermissionType["B_CLIENT_QUERY_CHANGE_PASSWORD"] = "b_client_query_change_password"; + PermissionType["B_CLIENT_QUERY_CHANGE_OWN_PASSWORD"] = "b_client_query_change_own_password"; + PermissionType["B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL"] = "b_client_query_change_password_global"; + PermissionType["B_CLIENT_QUERY_DELETE"] = "b_client_query_delete"; + PermissionType["B_CLIENT_QUERY_DELETE_OWN"] = "b_client_query_delete_own"; + PermissionType["B_FT_IGNORE_PASSWORD"] = "b_ft_ignore_password"; + PermissionType["B_FT_TRANSFER_LIST"] = "b_ft_transfer_list"; + PermissionType["I_FT_FILE_UPLOAD_POWER"] = "i_ft_file_upload_power"; + PermissionType["I_FT_NEEDED_FILE_UPLOAD_POWER"] = "i_ft_needed_file_upload_power"; + PermissionType["I_FT_FILE_DOWNLOAD_POWER"] = "i_ft_file_download_power"; + PermissionType["I_FT_NEEDED_FILE_DOWNLOAD_POWER"] = "i_ft_needed_file_download_power"; + PermissionType["I_FT_FILE_DELETE_POWER"] = "i_ft_file_delete_power"; + PermissionType["I_FT_NEEDED_FILE_DELETE_POWER"] = "i_ft_needed_file_delete_power"; + PermissionType["I_FT_FILE_RENAME_POWER"] = "i_ft_file_rename_power"; + PermissionType["I_FT_NEEDED_FILE_RENAME_POWER"] = "i_ft_needed_file_rename_power"; + PermissionType["I_FT_FILE_BROWSE_POWER"] = "i_ft_file_browse_power"; + PermissionType["I_FT_NEEDED_FILE_BROWSE_POWER"] = "i_ft_needed_file_browse_power"; + PermissionType["I_FT_DIRECTORY_CREATE_POWER"] = "i_ft_directory_create_power"; + PermissionType["I_FT_NEEDED_DIRECTORY_CREATE_POWER"] = "i_ft_needed_directory_create_power"; + PermissionType["I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT"] = "i_ft_quota_mb_download_per_client"; + PermissionType["I_FT_QUOTA_MB_UPLOAD_PER_CLIENT"] = "i_ft_quota_mb_upload_per_client"; +})(PermissionType || (PermissionType = {})); +class PermissionInfo { + is_boolean() { return this.name.startsWith("b_"); } + id_grant() { + return this.id | (1 << 15); + } +} +class PermissionGroup { +} +class GroupedPermissions { +} +class PermissionValue { + constructor(type, value) { + this.type = type; + this.value = value; + } + granted(requiredValue, required = true) { + let result; + result = this.value == -1 || this.value >= requiredValue || (this.value == -2 && requiredValue == -2 && !required); + log.trace(LogCategory.PERMISSIONS, _translations.n_KYAaMS || (_translations.n_KYAaMS = tr("Required permission test resulted for permission %s: %s. Required value: %s, Granted value: %s")), this.type ? this.type.name : "unknown", result ? _translations.XWPfjb2K || (_translations.XWPfjb2K = tr("granted")) : _translations.le2qQrVZ || (_translations.le2qQrVZ = tr("denied")), requiredValue + (required ? " (" + (_translations._idwzRBJ || (_translations._idwzRBJ = tr("required"))) + ")" : ""), this.hasValue() ? this.value : _translations.tzCmjh5N || (_translations.tzCmjh5N = tr("none"))); + return result; + } + hasValue() { + return typeof (this.value) !== "undefined" && this.value != -2; + } + hasGrant() { + return typeof (this.granted_value) !== "undefined" && this.granted_value != -2; + } +} +class NeededPermissionValue extends PermissionValue { + constructor(type, value) { + super(type, value); + } +} +class PermissionManager extends connection.AbstractCommandHandler { + constructor(client) { + super(client.serverConnection); + this.permissionList = []; + this.permissionGroups = []; + this.neededPermissions = []; + this.needed_permission_change_listener = {}; + this.requests_channel_permissions = []; + this.requests_client_permissions = []; + this.requests_client_channel_permissions = []; + this.requests_playlist_permissions = []; + this.requests_playlist_client_permissions = []; + this.requests_permfind = []; + this.initializedListener = []; + this.criteria_equal = (a, b) => { + for (const criteria of ["client_id", "channel_id", "playlist_id"]) { + if ((typeof a[criteria] === "undefined") !== (typeof b[criteria] === "undefined")) + return false; + if (a[criteria] != b[criteria]) + return false; + } + return true; + }; + //FIXME? Dont register the handler like this? + this.volatile_handler_boss = true; + client.serverConnection.command_handler_boss().register_handler(this); + this.handle = client; + } + static parse_permission_bulk(json, manager) { + let permissions = []; + for (let perm of json) { + if (perm["permid"] === undefined) + continue; + let perm_id = parseInt(perm["permid"]); + let perm_grant = (perm_id & (1 << 15)) > 0; + if (perm_grant) + perm_id &= ~(1 << 15); + let perm_info = manager.resolveInfo(perm_id); + if (!perm_info) { + log.warn(LogCategory.PERMISSIONS, _translations.mPnGQldc || (_translations.mPnGQldc = tr("Got unknown permission id (%o/%o (%o))!")), perm["permid"], perm_id, perm["permsid"]); + return; + } + let permission; + for (let ref_perm of permissions) { + if (ref_perm.type == perm_info) { + permission = ref_perm; + break; + } + } + if (!permission) { + permission = new PermissionValue(perm_info, 0); + permission.granted_value = undefined; + permission.value = undefined; + permissions.push(permission); + } + if (perm_grant) { + permission.granted_value = parseInt(perm["permvalue"]); + } + else { + permission.value = parseInt(perm["permvalue"]); + permission.flag_negate = perm["permnegated"] == "1"; + permission.flag_skip = perm["permskip"] == "1"; + } + } + return permissions; + } + destroy() { + this.handle.serverConnection && this.handle.serverConnection.command_handler_boss().unregister_handler(this); + this.needed_permission_change_listener = {}; + this.permissionList = undefined; + this.permissionGroups = undefined; + this.neededPermissions = undefined; + /* delete all requests */ + for (const key of Object.keys(this)) + if (key.startsWith("requests")) + delete this[key]; + this.initializedListener = undefined; + this._cacheNeededPermissions = undefined; + } + handle_command(command) { + switch (command.command) { + case "notifyclientneededpermissions": + this.onNeededPermissions(command.arguments); + return true; + case "notifypermissionlist": + this.onPermissionList(command.arguments); + return true; + case "notifychannelpermlist": + this.onChannelPermList(command.arguments); + return true; + case "notifyclientpermlist": + this.onClientPermList(command.arguments); + return true; + case "notifyclientchannelpermlist": + this.onChannelClientPermList(command.arguments); + return true; + case "notifyplaylistpermlist": + this.onPlaylistPermList(command.arguments); + return true; + case "notifyplaylistclientpermlist": + this.onPlaylistClientPermList(command.arguments); + return true; + } + return false; + } + initialized() { + return this.permissionList.length > 0; + } + requestPermissionList() { + this.handle.serverConnection.send_command("permissionlist"); + } + onPermissionList(json) { + this.permissionList = []; + this.permissionGroups = []; + this._group_mapping = PermissionManager.group_mapping.slice(); + let group = log.group(log.LogType.TRACE, LogCategory.PERMISSIONS, _translations.xNZTALgn || (_translations.xNZTALgn = tr("Permission mapping"))); + const table_entries = []; + let permission_id = 0; + for (let e of json) { + if (e["group_id_end"]) { + let group = new PermissionGroup(); + group.begin = this.permissionGroups.length ? this.permissionGroups.last().end : 0; + group.end = parseInt(e["group_id_end"]); + group.deep = 0; + group.name = (_translations.DKPKNRNP || (_translations.DKPKNRNP = tr("Group "))) + e["group_id_end"]; + let info = this._group_mapping.pop_front(); + if (info) { + group.name = info.name; + group.deep = info.deep; + } + this.permissionGroups.push(group); + continue; + } + let perm = new PermissionInfo(); + permission_id++; + perm.name = e["permname"]; + perm.id = parseInt(e["permid"]) || permission_id; /* using permission_id as fallback if we dont have permid */ + perm.description = e["permdesc"]; + this.permissionList.push(perm); + table_entries.push({ + "id": perm.id, + "name": perm.name, + "description": perm.description + }); + } + log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Permission list", table_entries); + group.end(); + log.info(LogCategory.PERMISSIONS, _translations.xRgg5id2 || (_translations.xRgg5id2 = tr("Got %i permissions")), this.permissionList.length); + if (this._cacheNeededPermissions) + this.onNeededPermissions(this._cacheNeededPermissions); + for (let listener of this.initializedListener) + listener(true); + } + onNeededPermissions(json) { + if (this.permissionList.length == 0) { + log.warn(LogCategory.PERMISSIONS, _translations.zjGxpKHl || (_translations.zjGxpKHl = tr("Got needed permissions but don't have a permission list!"))); + this._cacheNeededPermissions = json; + return; + } + this._cacheNeededPermissions = undefined; + let copy = this.neededPermissions.slice(); + let addcount = 0; + let group = log.group(log.LogType.TRACE, LogCategory.PERMISSIONS, _translations.CTD6hjuf || (_translations.CTD6hjuf = tr("Got %d needed permissions.")), json.length); + const table_entries = []; + for (let e of json) { + let entry = undefined; + for (let p of copy) { + if (p.type.id == e["permid"]) { + entry = p; + copy.remove(p); + break; + } + } + if (!entry) { + let info = this.resolveInfo(e["permid"]); + if (info) { + entry = new NeededPermissionValue(info, -2); + this.neededPermissions.push(entry); + } + else { + log.warn(LogCategory.PERMISSIONS, _translations.bczqMhnT || (_translations.bczqMhnT = tr("Could not resolve perm for id %s (%o|%o)")), e["permid"], e, info); + continue; + } + addcount++; + } + if (entry.value == parseInt(e["permvalue"])) + continue; + entry.value = parseInt(e["permvalue"]); + for (const listener of this.needed_permission_change_listener[entry.type.name] || []) + listener(); + table_entries.push({ + "permission": entry.type.name, + "value": entry.value + }); + } + log.table(LogType.DEBUG, LogCategory.PERMISSIONS, "Needed client permissions", table_entries); + group.end(); + log.debug(LogCategory.PERMISSIONS, _translations.JhTXHwBa || (_translations.JhTXHwBa = tr("Dropping %o needed permissions and added %o permissions.")), copy.length, addcount); + for (let e of copy) { + e.value = -2; + for (const listener of this.needed_permission_change_listener[e.type.name] || []) + listener(); + } + } + register_needed_permission(key, listener) { + const array = this.needed_permission_change_listener[key] || []; + array.push(listener); + this.needed_permission_change_listener[key] = array; + } + unregister_needed_permission(key, listener) { + const array = this.needed_permission_change_listener[key]; + if (!array) + return; + array.remove(listener); + this.needed_permission_change_listener[key] = array.length > 0 ? array : undefined; + } + resolveInfo(key) { + for (let perm of this.permissionList) + if (perm.id == key || perm.name == key) + return perm; + return undefined; + } + /* channel permission request */ + onChannelPermList(json) { + let channelId = parseInt(json[0]["cid"]); + this.fullfill_permission_request("requests_channel_permissions", { + channel_id: channelId + }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); + } + execute_channel_permission_request(request) { + this.handle.serverConnection.send_command("channelpermlist", { "cid": request.channel_id }).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) + this.fullfill_permission_request("requests_channel_permissions", request, "success", []); + else + this.fullfill_permission_request("requests_channel_permissions", request, "error", error); + }); + } + requestChannelPermissions(channelId) { + const keys = { + channel_id: channelId + }; + return this.execute_permission_request("requests_channel_permissions", keys, this.execute_channel_permission_request.bind(this)); + } + /* client permission request */ + onClientPermList(json) { + let client = parseInt(json[0]["cldbid"]); + this.fullfill_permission_request("requests_client_permissions", { + client_id: client + }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); + } + execute_client_permission_request(request) { + this.handle.serverConnection.send_command("clientpermlist", { cldbid: request.client_id }).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) + this.fullfill_permission_request("requests_client_permissions", request, "success", []); + else + this.fullfill_permission_request("requests_client_permissions", request, "error", error); + }); + } + requestClientPermissions(client_id) { + const keys = { + client_id: client_id + }; + return this.execute_permission_request("requests_client_permissions", keys, this.execute_client_permission_request.bind(this)); + } + /* client channel permission request */ + onChannelClientPermList(json) { + let client_id = parseInt(json[0]["cldbid"]); + let channel_id = parseInt(json[0]["cid"]); + this.fullfill_permission_request("requests_client_channel_permissions", { + client_id: client_id, + channel_id: channel_id + }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); + } + execute_client_channel_permission_request(request) { + this.handle.serverConnection.send_command("channelclientpermlist", { cldbid: request.client_id, cid: request.channel_id }) + .catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) + this.fullfill_permission_request("requests_client_channel_permissions", request, "success", []); + else + this.fullfill_permission_request("requests_client_channel_permissions", request, "error", error); + }); + } + requestClientChannelPermissions(client_id, channel_id) { + const keys = { + client_id: client_id + }; + return this.execute_permission_request("requests_client_channel_permissions", keys, this.execute_client_channel_permission_request.bind(this)); + } + /* playlist permissions */ + onPlaylistPermList(json) { + let playlist_id = parseInt(json[0]["playlist_id"]); + this.fullfill_permission_request("requests_playlist_permissions", { + playlist_id: playlist_id + }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); + } + execute_playlist_permission_request(request) { + this.handle.serverConnection.send_command("playlistpermlist", { playlist_id: request.playlist_id }) + .catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) + this.fullfill_permission_request("requests_playlist_permissions", request, "success", []); + else + this.fullfill_permission_request("requests_playlist_permissions", request, "error", error); + }); + } + requestPlaylistPermissions(playlist_id) { + const keys = { + playlist_id: playlist_id + }; + return this.execute_permission_request("requests_playlist_permissions", keys, this.execute_playlist_permission_request.bind(this)); + } + /* playlist client permissions */ + onPlaylistClientPermList(json) { + let playlist_id = parseInt(json[0]["playlist_id"]); + let client_id = parseInt(json[0]["cldbid"]); + this.fullfill_permission_request("requests_playlist_client_permissions", { + playlist_id: playlist_id, + client_id: client_id + }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); + } + execute_playlist_client_permission_request(request) { + this.handle.serverConnection.send_command("playlistclientpermlist", { playlist_id: request.playlist_id, cldbid: request.client_id }) + .catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) + this.fullfill_permission_request("requests_playlist_client_permissions", request, "success", []); + else + this.fullfill_permission_request("requests_playlist_client_permissions", request, "error", error); + }); + } + requestPlaylistClientPermissions(playlist_id, client_database_id) { + const keys = { + playlist_id: playlist_id, + client_id: client_database_id + }; + return this.execute_permission_request("requests_playlist_client_permissions", keys, this.execute_playlist_client_permission_request.bind(this)); + } + execute_permission_request(list, criteria, execute) { + for (const request of this[list]) + if (this.criteria_equal(request, criteria) && request.promise.time() + 1000 < Date.now()) + return request.promise; + const result = Object.assign({ + timeout_id: setTimeout(() => this.fullfill_permission_request(list, criteria, "error", _translations.G5Jy9mCS || (_translations.G5Jy9mCS = tr("timeout"))), 5000), + promise: new LaterPromise() + }, criteria); + this[list].push(result); + execute(criteria); + return result.promise; + } + ; + fullfill_permission_request(list, criteria, status, result) { + for (const request of this[list]) { + if (this.criteria_equal(request, criteria)) { + this[list].remove(request); + clearTimeout(request.timeout_id); + status === "error" ? request.promise.rejected(result) : request.promise.resolved(result); + } + } + } + find_permission(...permissions) { + const permission_ids = []; + for (const permission of permissions) { + const info = this.resolveInfo(permission); + if (!info) + continue; + permission_ids.push(info.id); + } + if (!permission_ids.length) + return Promise.resolve([]); + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifypermfind", + function: command => { + const result = []; + for (const entry of command.arguments) { + const perm_id = parseInt(entry["p"]); + if (permission_ids.indexOf(perm_id) === -1) + return; /* not our permfind result */ + const value = parseInt(entry["v"]); + const type = parseInt(entry["t"]); + let data; + switch (type) { + case 0: + data = { + type: "server_group", + group_id: parseInt(entry["id1"]), + }; + break; + case 1: + data = { + type: "client", + client_id: parseInt(entry["id2"]), + }; + break; + case 2: + data = { + type: "channel", + channel_id: parseInt(entry["id2"]), + }; + break; + case 3: + data = { + type: "channel_group", + group_id: parseInt(entry["id1"]), + }; + break; + case 4: + data = { + type: "client_channel", + client_id: parseInt(entry["id1"]), + channel_id: parseInt(entry["id1"]), + }; + break; + default: + continue; + } + data.id = perm_id; + data.value = value; + result.push(data); + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("permfind", permission_ids.map(e => { return { permid: e }; })).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + reject(error); + }); + }); + } + neededPermission(key) { + for (let perm of this.neededPermissions) + if (perm.type.id == key || perm.type.name == key || perm.type == key) + return perm; + log.debug(LogCategory.PERMISSIONS, _translations.X_wUbyDP || (_translations.X_wUbyDP = tr("Could not resolve grant permission %o. Creating a new one.")), key); + let info = key instanceof PermissionInfo ? key : this.resolveInfo(key); + if (!info) { + log.warn(LogCategory.PERMISSIONS, _translations.va1TCPgM || (_translations.va1TCPgM = tr("Requested needed permission with invalid key! (%o)")), key); + return new NeededPermissionValue(undefined, -2); + } + let result = new NeededPermissionValue(info, -2); + this.neededPermissions.push(result); + return result; + } + groupedPermissions() { + let result = []; + let current; + for (let group of this.permissionGroups) { + if (group.deep == 0) { + current = new GroupedPermissions(); + current.group = group; + current.parent = undefined; + current.children = []; + current.permissions = []; + result.push(current); + } + else { + if (!current) { + throw _translations.dpJgGcEJ || (_translations.dpJgGcEJ = tr("invalid order!")); + } + else { + while (group.deep <= current.group.deep) + current = current.parent; + let parent = current; + current = new GroupedPermissions(); + current.group = group; + current.parent = parent; + current.children = []; + current.permissions = []; + parent.children.push(current); + } + } + for (let permission of this.permissionList) + if (permission.id > current.group.begin && permission.id <= current.group.end) + current.permissions.push(permission); + } + return result; + } + /** + * Generates an enum with all know permission types, used for the enum above + */ + export_permission_types() { + let result = ""; + result = result + "enum PermissionType {\n"; + for (const permission of this.permissionList) { + if (!permission.name) + continue; + result = result + "\t" + permission.name.toUpperCase() + " = \"" + permission.name.toLowerCase() + "\", /* Permission ID: " + permission.id + " */\n"; + } + result = result + "}"; + return result; + } +} +/* Static info mapping until TeaSpeak implements a detailed info */ +PermissionManager.group_mapping = [ + { name: _translations.HH613O5m || (_translations.HH613O5m = tr("Global")), deep: 0 }, + { name: _translations.mwOSnQ5t || (_translations.mwOSnQ5t = tr("Information")), deep: 1 }, + { name: _translations.biW_sg0z || (_translations.biW_sg0z = tr("Virtual server management")), deep: 1 }, + { name: _translations.XEpW1KUu || (_translations.XEpW1KUu = tr("Administration")), deep: 1 }, + { name: _translations.xQjKeR1B || (_translations.xQjKeR1B = tr("Settings")), deep: 1 }, + { name: _translations.Qaqyrcb5 || (_translations.Qaqyrcb5 = tr("Virtual Server")), deep: 0 }, + { name: _translations.VPP72Y7O || (_translations.VPP72Y7O = tr("Information")), deep: 1 }, + { name: _translations.sRQvs1pe || (_translations.sRQvs1pe = tr("Administration")), deep: 1 }, + { name: _translations.MKTJCaHB || (_translations.MKTJCaHB = tr("Settings")), deep: 1 }, + { name: _translations.o_mBuxpj || (_translations.o_mBuxpj = tr("Channel")), deep: 0 }, + { name: _translations.Ke3sTyPP || (_translations.Ke3sTyPP = tr("Information")), deep: 1 }, + { name: _translations.w1vxsh9K || (_translations.w1vxsh9K = tr("Create")), deep: 1 }, + { name: _translations.tYJyWqOO || (_translations.tYJyWqOO = tr("Modify")), deep: 1 }, + { name: _translations.WZsNfPyK || (_translations.WZsNfPyK = tr("Delete")), deep: 1 }, + { name: _translations.WIiqWiL4 || (_translations.WIiqWiL4 = tr("Access")), deep: 1 }, + { name: _translations.TOhfWBxb || (_translations.TOhfWBxb = tr("Group")), deep: 0 }, + { name: _translations._rPytECo || (_translations._rPytECo = tr("Information")), deep: 1 }, + { name: _translations.CJT92u8o || (_translations.CJT92u8o = tr("Create")), deep: 1 }, + { name: _translations.F7lDIM5W || (_translations.F7lDIM5W = tr("Modify")), deep: 1 }, + { name: _translations.p978M8l6 || (_translations.p978M8l6 = tr("Delete")), deep: 1 }, + { name: _translations.nj0btL0v || (_translations.nj0btL0v = tr("Client")), deep: 0 }, + { name: _translations.yFgZNi8s || (_translations.yFgZNi8s = tr("Information")), deep: 1 }, + { name: _translations.SiWRTYIe || (_translations.SiWRTYIe = tr("Admin")), deep: 1 }, + { name: _translations.UZZVYe0I || (_translations.UZZVYe0I = tr("Basics")), deep: 1 }, + { name: _translations.t7dRXZeR || (_translations.t7dRXZeR = tr("Modify")), deep: 1 }, + //TODO Music bot + { name: _translations.bGpv2bKN || (_translations.bGpv2bKN = tr("File Transfer")), deep: 0 }, +]; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["b0ea492a6dc1c0dcbed405d36aac400c0dbb882c0f941543ac830e0a838c53e1"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["b0ea492a6dc1c0dcbed405d36aac400c0dbb882c0f941543ac830e0a838c53e1"] = "b0ea492a6dc1c0dcbed405d36aac400c0dbb882c0f941543ac830e0a838c53e1"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "m_Pwx8sY", path: "D:/TeaSpeak/web/shared/js/permission/GroupManager.ts (142,43)" }, { name: "HQc_WylI", path: "D:/TeaSpeak/web/shared/js/permission/GroupManager.ts (158,51)" }, { name: "ry6JbE1j", path: "D:/TeaSpeak/web/shared/js/permission/GroupManager.ts (207,42)" }, { name: "lM4enCzV", path: "D:/TeaSpeak/web/shared/js/permission/GroupManager.ts (216,48)" }, { name: "OlcOY9Oi", path: "D:/TeaSpeak/web/shared/js/permission/GroupManager.ts (225,47)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var GroupType; +(function (GroupType) { + GroupType[GroupType["QUERY"] = 0] = "QUERY"; + GroupType[GroupType["TEMPLATE"] = 1] = "TEMPLATE"; + GroupType[GroupType["NORMAL"] = 2] = "NORMAL"; +})(GroupType || (GroupType = {})); +var GroupTarget; +(function (GroupTarget) { + GroupTarget[GroupTarget["SERVER"] = 0] = "SERVER"; + GroupTarget[GroupTarget["CHANNEL"] = 1] = "CHANNEL"; +})(GroupTarget || (GroupTarget = {})); +class GroupProperties { + constructor() { + this.iconid = 0; + this.sortid = 0; + this.savedb = false; + this.namemode = 0; + } +} +class GroupPermissionRequest { +} +class Group { + constructor(handle, id, target, type, name) { + this.properties = new GroupProperties(); + this.requiredModifyPower = 0; + this.requiredMemberAddPower = 0; + this.requiredMemberRemovePower = 0; + this.handle = handle; + this.id = id; + this.target = target; + this.type = type; + this.name = name; + } + updateProperty(key, value) { + if (!JSON.map_field_to(this.properties, value, key)) + return; /* no updates */ + if (key == "iconid") { + this.properties.iconid = (new Uint32Array([this.properties.iconid]))[0]; + this.handle.handle.channelTree.clientsByGroup(this).forEach(client => { + client.updateGroupIcon(this); + }); + } + else if (key == "sortid") + this.handle.handle.channelTree.clientsByGroup(this).forEach(client => { + client.update_group_icon_order(); + }); + } +} +class GroupManager extends connection.AbstractCommandHandler { + constructor(client) { + super(client.serverConnection); + this.serverGroups = []; + this.channelGroups = []; + this.requests_group_permissions = []; + client.serverConnection.command_handler_boss().register_handler(this); + this.handle = client; + } + destroy() { + this.handle.serverConnection && this.handle.serverConnection.command_handler_boss().unregister_handler(this); + this.serverGroups = undefined; + this.channelGroups = undefined; + } + handle_command(command) { + switch (command.command) { + case "notifyservergrouplist": + case "notifychannelgrouplist": + this.handle_grouplist(command.arguments); + return true; + case "notifyservergrouppermlist": + case "notifychannelgrouppermlist": + this.handle_group_permission_list(command.arguments); + return true; + } + return false; + } + requestGroups() { + this.handle.serverConnection.send_command("servergrouplist"); + this.handle.serverConnection.send_command("channelgrouplist"); + } + static sorter() { + return (a, b) => { + if (!a) + return b ? 1 : 0; + if (!b) + return a ? -1 : 0; + if (a.properties.sortid > b.properties.sortid) + return 1; + if (a.properties.sortid < b.properties.sortid) + return -1; + if (a.id < b.id) + return -1; + if (a.id > b.id) + return 1; + return 0; + }; + } + serverGroup(id) { + for (let group of this.serverGroups) + if (group.id == id) + return group; + return undefined; + } + channelGroup(id) { + for (let group of this.channelGroups) + if (group.id == id) + return group; + return undefined; + } + handle_grouplist(json) { + let target; + if (json[0]["sgid"]) + target = GroupTarget.SERVER; + else if (json[0]["cgid"]) + target = GroupTarget.CHANNEL; + else { + log.error(LogCategory.CLIENT, _translations.m_Pwx8sY || (_translations.m_Pwx8sY = tr("Could not resolve group target! => %o")), json[0]); + return; + } + if (target == GroupTarget.SERVER) + this.serverGroups = []; + else + this.channelGroups = []; + for (let groupData of json) { + let type; + switch (Number.parseInt(groupData["type"])) { + case 0: + type = GroupType.TEMPLATE; + break; + case 1: + type = GroupType.NORMAL; + break; + case 2: + type = GroupType.QUERY; + break; + default: + log.error(LogCategory.CLIENT, _translations.HQc_WylI || (_translations.HQc_WylI = tr("Invalid group type: %o for group %s")), groupData["type"], groupData["name"]); + continue; + } + let group = new Group(this, parseInt(target == GroupTarget.SERVER ? groupData["sgid"] : groupData["cgid"]), target, type, groupData["name"]); + for (let key in groupData) { + if (key == "sgid") + continue; + if (key == "cgid") + continue; + if (key == "type") + continue; + if (key == "name") + continue; + group.updateProperty(key, groupData[key]); + } + group.requiredMemberRemovePower = parseInt(groupData["n_member_removep"]); + group.requiredMemberAddPower = parseInt(groupData["n_member_addp"]); + group.requiredModifyPower = parseInt(groupData["n_modifyp"]); + if (target == GroupTarget.SERVER) + this.serverGroups.push(group); + else + this.channelGroups.push(group); + } + for (const client of this.handle.channelTree.clients) + client.update_displayed_client_groups(); + } + request_permissions(group) { + for (let request of this.requests_group_permissions) + if (request.group_id == group.id && request.promise.time() + 1000 > Date.now()) + return request.promise; + let req = new GroupPermissionRequest(); + req.group_id = group.id; + req.promise = new LaterPromise(); + this.requests_group_permissions.push(req); + this.handle.serverConnection.send_command(group.target == GroupTarget.SERVER ? "servergrouppermlist" : "channelgrouppermlist", { + cgid: group.id, + sgid: group.id + }).catch(error => { + if (error instanceof CommandResult && error.id == 0x0501) + req.promise.resolved([]); + else + req.promise.rejected(error); + }).then(() => { + //No notify handler + setTimeout(() => { + if (this.requests_group_permissions.remove(req)) + req.promise.rejected(_translations.ry6JbE1j || (_translations.ry6JbE1j = tr("no response"))); + }, 1000); + }); + return req.promise; + } + handle_group_permission_list(json) { + let group = json[0]["sgid"] ? this.serverGroup(parseInt(json[0]["sgid"])) : this.channelGroup(parseInt(json[0]["cgid"])); + if (!group) { + log.error(LogCategory.PERMISSIONS, _translations.lM4enCzV || (_translations.lM4enCzV = tr("Got group permissions for group %o/%o, but its not a registered group!")), json[0]["sgid"], json[0]["cgid"]); + return; + } + let requests = []; + for (let req of this.requests_group_permissions) + if (req.group_id == group.id) + requests.push(req); + if (requests.length == 0) { + log.warn(LogCategory.PERMISSIONS, _translations.OlcOY9Oi || (_translations.OlcOY9Oi = tr("Got group permissions for group %o/%o, but it was never requested!")), json[0]["sgid"], json[0]["cgid"]); + return; + } + let permissions = PermissionManager.parse_permission_bulk(json, this.handle.permissions); + for (let req of requests) { + this.requests_group_permissions.remove(req); + req.promise.resolved(permissions); + } + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["46781534c5b8744342d5e1cb470f54295d8e0415c5ff17abae7eb6ef80ec64fa"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["46781534c5b8744342d5e1cb470f54295d8e0415c5ff17abae7eb6ef80ec64fa"] = "46781534c5b8744342d5e1cb470f54295d8e0415c5ff17abae7eb6ef80ec64fa"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "DCMrKjN6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (5,21)" }, { name: "x4QFY8cw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (107,25)" }, { name: "dFGvDzKl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (125,35)" }, { name: "hmRPaeUZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (136,25)" }, { name: "PNcccgLx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (158,35)" }, { name: "R6SjfIQ9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (170,44)" }, { name: "COCcPP1_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (184,125)" }, { name: "t1qSGjoo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (185,84)" }, { name: "PerphGL8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (227,47)" }, { name: "Udn4hTMY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (227,68)" }, { name: "L2ki1Skp", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (276,34)" }, { name: "_RrVUjgX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (276,62)" }, { name: "sl496iCK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (292,71)" }, { name: "XC0NChHc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (302,34)" }, { name: "wz0RQ6W1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (302,57)" }, { name: "ySq4xxUe", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (460,43)" }, { name: "U5kKlwd8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (465,43)" }, { name: "i6XwJzrt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (466,46)" }, { name: "pwBc9du5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (466,106)" }, { name: "tYO7MhPX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (481,33)" }, { name: "TPswmcMR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (482,42)" }, { name: "P9SCZZQG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (493,35)" }, { name: "O6ifCkPV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (603,33)" }, { name: "ijGZQoJy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (659,50)" }, { name: "oPL5wq0y", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (660,75)" }, { name: "UBrvt3sv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (699,50)" }, { name: "phHj2aiZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (700,74)" }, { name: "XUo6cT78", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (710,50)" }, { name: "dCwo9upm", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (711,80)" }, { name: "AlGUCXNW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (722,50)" }, { name: "o2FZws2H", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (723,77)" }, { name: "urEnnhUv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (735,50)" }, { name: "RbO_12FB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (736,77)" }, { name: "He1Lhlei", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (748,50)" }, { name: "BsQvH7KD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (749,83)" }, { name: "dryBR96k", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (765,50)" }, { name: "NmI6OOx2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (766,83)" }, { name: "ISCwnQTE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (776,50)" }, { name: "gT4hzBi0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (777,85)" }, { name: "DaKCu_FT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (783,138)" }, { name: "D8X_oTbc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (790,85)" }, { name: "uLiVzH1c", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (797,50)" }, { name: "Zw07OLxG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (808,50)" }, { name: "NtGL5zmt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (809,88)" }, { name: "r6jWa2i3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (825,39)" }, { name: "sH3hrr8x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (826,92)" }, { name: "gtbrQF6_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (829,35)" }, { name: "DW7n4WE_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (830,88)" }, { name: "u_dYuuQh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (837,50)" }, { name: "C8qosC5x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (846,39)" }, { name: "hGsVw3C1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (861,50)" }, { name: "jvF1L94q", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (874,55)" }, { name: "ePInsAEF", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (882,50)" }, { name: "QE8mYeI3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (900,38)" }, { name: "c93vvmwR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (900,71)" }, { name: "W6aQuY5Q", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (920,104)" }, { name: "_aqNX0xB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (921,91)" }, { name: "e3VI8KGN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (924,90)" }, { name: "vjY9fepV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (951,81)" }, { name: "jedfrTq7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (985,70)" }, { name: "dJqFoWNu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1001,94)" }, { name: "QMbj98Jl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1010,83)" }, { name: "oXE3AOOj", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1010,108)" }, { name: "u3YjNURr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1010,150)" }, { name: "bdvaz2Fu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1024,54)" }, { name: "yZrWIhG1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1058,42)" }, { name: "OergtM4i", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1058,79)" }, { name: "AcQQmDNG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1058,157)" }, { name: "BIHNDdrV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1058,197)" }, { name: "LpQGcaVD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1071,36)" }, { name: "Odgcch92", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1071,57)" }, { name: "PKQe99Hv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1080,42)" }, { name: "uZOymAv8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1080,74)" }, { name: "iRIw6oQA", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1080,147)" }, { name: "uskvfZ0w", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1080,187)" }, { name: "w92k7IcM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1094,42)" }, { name: "aeeSZ1W6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1094,69)" }, { name: "x64gbqPE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1108,42)" }, { name: "f7eMoiYk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1108,74)" }, { name: "KRG7a6pk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1108,151)" }, { name: "WnIitvJ5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1108,191)" }, { name: "cgIp7Mq0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1117,74)" }, { name: "vmnyPmOT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1117,114)" }, { name: "il2IXXi7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1128,74)" }, { name: "eLGB6hfn", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1139,39)" }, { name: "rQQstdmx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1156,39)" }, { name: "JsYl7deR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1161,50)" }, { name: "V2dCo75J", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1161,87)" }, { name: "M_33FHbX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1188,115)" }, { name: "K95eJH8w", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1199,88)" }, { name: "uUD_P0G8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1227,39)" }, { name: "B2niZHis", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1232,50)" }, { name: "lglQPHRb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1232,83)" }, { name: "gjr1Hjwb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1267,82)" }, { name: "TQnO8NzW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1322,71)" }, { name: "UOYpbAz7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1323,75)" }, { name: "j_B1XMlg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1388,44)" }, { name: "LhMjT0D6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1388,64)" }, { name: "QXs1fW_l", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1400,50)" }, { name: "LvN8hZ1p", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1400,91)" }, { name: "mfkiBtNv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1405,45)" }, { name: "TgHz611r", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1405,71)" }, { name: "e35FyXnh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1413,44)" }, { name: "w9qoM29d", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1413,64)" }, { name: "hZrU5_SP", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1431,45)" }, { name: "K5k4dA5r", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1431,70)" }, { name: "pi0qV71w", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1438,46)" }, { name: "f_7UqLhu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1438,63)" }, { name: "vlf6z52a", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1488,46)" }, { name: "LbOr04qi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1501,35)" }, { name: "NFAJFMiD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1517,35)" }, { name: "TjAtodXU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1522,46)" }, { name: "hLnfEYZl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1522,75)" }, { name: "lghaKO0H", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1692,57)" }, { name: "JjcOzbzw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1723,53)" }, { name: "z4UK75Eu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1739,98)" }, { name: "am_OVcEM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1744,39)" }, { name: "pj9HuZR1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1747,53)" }, { name: "YwWBf7Xh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1776,103)" }, { name: "rqWJD5R2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1796,107)" }, { name: "w8SWxS4C", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1818,103)" }, { name: "S9WvyD7Z", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1889,46)" }, { name: "hceWH1fk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1889,109)" }, { name: "pqrGxJyU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1889,210)" }, { name: "cin0D3wS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1889,241)" }, { name: "ii2WV_Om", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1910,78)" }, { name: "zUPIX6BD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (1910,123)" }, { name: "cIzsOEm1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2022,46)" }, { name: "CPz2Dcn4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2022,106)" }, { name: "TqXWWyKK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2022,176)" }, { name: "IAvTTiJt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2022,207)" }, { name: "NZPKedF2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2124,109)" }, { name: "Fj0SrrS2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2139,45)" }, { name: "eE42rV0W", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2149,54)" }, { name: "Y9zzAYFy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2149,114)" }, { name: "EdjnX01y", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2149,184)" }, { name: "LJiFWJSt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2149,215)" }, { name: "jwSWNYxO", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2209,58)" }, { name: "UOWQDD_7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2209,126)" }, { name: "izNO4C23", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2209,204)" }, { name: "oRJMsyYz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2209,235)" }, { name: "FkXnr4XQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2267,58)" }, { name: "G2MNfANr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2267,120)" }, { name: "LUwfp1q6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2267,192)" }, { name: "Eckk8zqH", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2267,223)" }, { name: "Sz0yBwv1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2356,83)" }, { name: "nM_Detuu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2356,95)" }, { name: "tbSVCOB3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2393,35)" }, { name: "qJrtnF6R", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2409,69)" }, { name: "fMFyBIJx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2413,37)" }, { name: "m7tGEkDd", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2416,43)" }, { name: "HRWXI7ss", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2421,43)" }, { name: "_DgU0HsU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2428,69)" }, { name: "r6v8MZ09", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2431,35)" }, { name: "JslYmH78", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2432,38)" }, { name: "l0U3SilI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2432,65)" }, { name: "SCagvySD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2446,35)" }, { name: "N4jqwUqM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2447,38)" }, { name: "jeMb4g5m", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalSettings.ts (2447,93)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function spawnSettingsModal(default_page) { + let modal; + modal = createModal({ + header: _translations.DCMrKjN6 || (_translations.DCMrKjN6 = tr("Settings")), + body: () => { + const tag = $("#tmpl_settings").renderTag().dividerfy(); + /* general "tab" mechanic */ + const left = tag.find("> .left"); + const right = tag.find("> .right"); + { + left.find(".entry:not(.group)").on('click', event => { + const entry = $(event.target); + right.find("> .container").addClass("hidden"); + left.find(".selected").removeClass("selected"); + const target = entry.attr("container"); + if (!target) + return; + right.find("> .container." + target).removeClass("hidden"); + entry.addClass("selected"); + }); + } + /* initialize all tabs */ + /* enable one tab */ + { + left.find(".entry[container" + (default_page ? ("='" + default_page + "'") : "") + "]").first().trigger('click'); + } + return tag; + }, + footer: null + }); + modal.htmlTag.find(".modal-body").addClass("modal-settings"); + settings_general_application(modal.htmlTag.find(".right .container.general-application"), modal); + settings_general_language(modal.htmlTag.find(".right .container.general-language"), modal); + settings_general_chat(modal.htmlTag.find(".right .container.general-chat"), modal); + settings_audio_microphone(modal.htmlTag.find(".right .container.audio-microphone"), modal); + settings_audio_speaker(modal.htmlTag.find(".right .container.audio-speaker"), modal); + settings_audio_sounds(modal.htmlTag.find(".right .container.audio-sounds"), modal); + const update_profiles = settings_identity_profiles(modal.htmlTag.find(".right .container.identity-profiles"), modal); + settings_identity_forum(modal.htmlTag.find(".right .container.identity-forum"), modal, update_profiles); + modal.close_listener.push(() => { + if (profiles.requires_save()) + profiles.save(); + }); + modal.open(); + return modal; + } + Modals.spawnSettingsModal = spawnSettingsModal; + function settings_general_application(container, modal) { + /* hostbanner */ + { + const option = container.find(".option-hostbanner-background"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); + for (const sc of server_connections.server_connection_handlers()) + sc.hostbanner.update(); + }).prop("checked", settings.static_global(Settings.KEY_HOSTBANNER_BACKGROUND)); + } + /* font size */ + { + const current_size = parseInt(getComputedStyle(document.body).fontSize); //settings.static_global(Settings.KEY_FONT_SIZE, 12); + const select = container.find(".option-font-size"); + if (select.find("option[value='" + current_size + "']").length) + select.find("option[value='" + current_size + "']").prop("selected", true); + else + select.find("option[value='-1']").prop("selected", true); + select.on('change', event => { + const value = parseInt(select.val()); + settings.changeGlobal(Settings.KEY_FONT_SIZE, value); + console.log("Changed font size to %dpx", value); + $(document.body).css("font-size", value + "px"); + }); + } + /* all permissions */ + { + const option = container.find(".option-all-permissions"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); + }).prop("checked", settings.global(Settings.KEY_PERMISSIONS_SHOW_ALL)); + } + } + function settings_general_language(container, modal) { + const container_entries = container.find(".container-list .entries"); + const tag_loading = container.find(".cover-loading"); + const template = $("#settings-translations-list-entry"); + const restart_hint = container.find(".restart-note").hide(); + const display_repository_info = (repository) => { + const info_modal = createModal({ + header: _translations.x4QFY8cw || (_translations.x4QFY8cw = tr("Repository info")), + body: () => { + return $("#settings-translations-list-entry-info").renderTag({ + type: "repository", + name: repository.name, + url: repository.url, + contact: repository.contact, + translations: repository.translations || [] + }); + }, + footer: () => { + let footer = $.spawn("div"); + footer.addClass("modal-button-group"); + footer.css("margin-top", "5px"); + footer.css("margin-bottom", "5px"); + footer.css("text-align", "right"); + let buttonOk = $.spawn("button"); + buttonOk.text(_translations.dFGvDzKl || (_translations.dFGvDzKl = tr("Close"))); + buttonOk.click(() => info_modal.close()); + footer.append(buttonOk); + return footer; + } + }); + info_modal.open(); + }; + const display_translation_info = (translation, repository) => { + const info_modal = createModal({ + header: _translations.hmRPaeUZ || (_translations.hmRPaeUZ = tr("Translation info")), + body: () => { + const tag = $("#settings-translations-list-entry-info").renderTag({ + type: "translation", + name: translation.name, + url: translation.path, + repository_name: repository.name, + contributors: translation.contributors || [] + }); + tag.find(".button-info").on('click', () => display_repository_info(repository)); + return tag; + }, + footer: () => { + let footer = $.spawn("div"); + footer.addClass("modal-button-group"); + footer.css("margin-top", "5px"); + footer.css("margin-bottom", "5px"); + footer.css("text-align", "right"); + let buttonOk = $.spawn("button"); + buttonOk.text(_translations.PNcccgLx || (_translations.PNcccgLx = tr("Close"))); + buttonOk.click(() => info_modal.close()); + footer.append(buttonOk); + return footer; + } + }); + info_modal.open(); + }; + const update_current_selected = () => { + const container_current = container.find(".selected-language6"); + container_current.empty().text(_translations.R6SjfIQ9 || (_translations.R6SjfIQ9 = tr("Loading"))); + let current_translation; + i18n.iterate_repositories(repository => { + if (current_translation) + return; + for (const entry of repository.translations) + if (i18n.config.translation_config().current_translation_path == entry.path) { + current_translation = entry; + return; + } + }).then(() => { + container_current.empty(); + const language = current_translation ? current_translation.country_code : "gb"; + $.spawn("div").addClass("country flag-" + language.toLowerCase()).attr('title', i18n.country_name(language, _translations.COCcPP1_ || (_translations.COCcPP1_ = tr("Unknown language")))).appendTo(container_current); + $.spawn("a").text(current_translation ? current_translation.name : _translations.t1qSGjoo || (_translations.t1qSGjoo = tr("English (Default)"))).appendTo(container_current); + }).catch(error => { + /* This shall never happen */ + }); + }; + const initially_selected = i18n.config.translation_config().current_translation_url; + const update_list = () => { + container_entries.empty(); + const currently_selected = i18n.config.translation_config().current_translation_url; + //Default translation + { + const tag = template.renderTag({ + type: "default", + selected: !currently_selected || currently_selected == "default" + }); + tag.on('click', () => { + i18n.select_translation(undefined, undefined); + container_entries.find(".selected").removeClass("selected"); + tag.addClass("selected"); + update_current_selected(); + restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); + }); + tag.appendTo(container_entries); + } + { + tag_loading.show(); + i18n.iterate_repositories(repo => { + let repo_tag = container_entries.find("[repository=\"" + repo.unique_id + "\"]"); + if (repo_tag.length == 0) { + repo_tag = template.renderTag({ + type: "repository", + name: repo.name || repo.url, + id: repo.unique_id + }); + repo_tag.find(".button-delete").on('click', e => { + e.preventDefault(); + Modals.spawnYesNo(_translations.PerphGL8 || (_translations.PerphGL8 = tr("Are you sure?")), _translations.Udn4hTMY || (_translations.Udn4hTMY = tr("Do you really want to delete this repository?")), answer => { + if (answer) { + i18n.delete_repository(repo); + update_list(); + } + }); + }); + repo_tag.find(".button-info").on('click', e => { + e.preventDefault(); + display_repository_info(repo); + }); + container_entries.append(repo_tag); + } + for (const translation of repo.translations) { + const tag = template.renderTag({ + type: "translation", + name: translation.name || translation.path, + id: repo.unique_id, + country_code: translation.country_code, + selected: i18n.config.translation_config().current_translation_path == translation.path + }); + tag.find(".button-info").on('click', e => { + e.preventDefault(); + display_translation_info(translation, repo); + }); + tag.on('click', e => { + if (e.isDefaultPrevented()) + return; + i18n.select_translation(repo, translation); + container_entries.find(".selected").removeClass("selected"); + tag.addClass("selected"); + update_current_selected(); + restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); + }); + tag.insertAfter(repo_tag); + } + }).then(() => tag_loading.hide()).catch(error => { + console.error(error); + /* this should NEVER happen */ + }); + } + }; + /* button add repository */ + { + container.find(".button-add-repository").on('click', () => { + createInputModal(_translations.L2ki1Skp || (_translations.L2ki1Skp = tr("Enter repository URL")), _translations._RrVUjgX || (_translations._RrVUjgX = tr("Enter repository URL:")), text => { + try { + new URL(text); + return true; + } + catch (error) { + return false; + } + }, url => { + if (!url) + return; + tag_loading.show(); + i18n.load_repository(url).then(repository => { + i18n.register_repository(repository); + update_list(); + }).catch(error => { + tag_loading.hide(); + createErrorModal("Failed to load repository", (_translations.sl496iCK || (_translations.sl496iCK = tr("Failed to query repository.
Ensure that this repository is valid and reachable.
Error: "))) + error).open(); + }); + }).open(); + }); + } + container.find(".button-restart").on('click', () => { + if (app.is_web()) { + location.reload(); + } + else { + createErrorModal(_translations.XC0NChHc || (_translations.XC0NChHc = tr("Not implemented")), _translations.wz0RQ6W1 || (_translations.wz0RQ6W1 = tr("Client restart isn't implemented.
Please do it manually!"))).open(); + } + }); + update_list(); + update_current_selected(); + } + function settings_general_chat(container, modal) { + /* timestamp format */ + { + const option_fixed = container.find(".option-fixed-timestamps"); + const option_colloquial = container.find(".option-colloquial-timestamps"); + option_colloquial.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS, option_colloquial[0].checked); + }); + option_fixed.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_FIXED_TIMESTAMPS, option_fixed[0].checked); + option_colloquial + .prop("disabled", option_fixed[0].checked) + .parents("label").toggleClass("disabled", option_fixed[0].checked); + if (option_fixed[0].checked) { + option_colloquial.prop("checked", false); + } + else { + option_colloquial.prop("checked", settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)); + } + }).prop("checked", settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)).trigger('change'); + } + { + const option = container.find(".option-instant-channel-switch"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CHAT, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)); + } + { + const option = container.find(".option-instant-client-switch"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CLIENT, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)); + } + { + const option = container.find(".option-colored-emojies"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_COLORED_EMOJIES, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)); + } + const update_format_helper = () => server_connections.server_connection_handlers().map(e => e.side_bar).forEach(e => { + e.private_conversations().update_input_format_helper(); + e.channel_conversations().update_input_format_helper(); + }); + { + const option = container.find(".option-support-markdown"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_ENABLE_MARKDOWN, option[0].checked); + update_format_helper(); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)); + } + { + const option = container.find(".option-support-bbcode"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_ENABLE_BBCODE, option[0].checked); + update_format_helper(); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE)); + } + { + const option = container.find(".option-url-tagging"); + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_TAG_URLS, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_TAG_URLS)); + } + /* Icon size */ + { + const container_slider = container.find(".container-icon-size .container-slider"); + const container_value = container.find(".container-icon-size .value"); + sliderfy(container_slider, { + unit: '%', + min_value: 25, + max_value: 300, + step: 5, + initial_value: settings.static_global(Settings.KEY_ICON_SIZE), + value_field: container_value + }); + container_slider.on('change', event => { + const value = parseInt(container_slider.attr("value")); + settings.changeGlobal(Settings.KEY_ICON_SIZE, value); + console.log("Changed icon size to %sem", (value / 100).toFixed(2)); + MessageHelper.set_icon_size((value / 100).toFixed(2) + "em"); + }); + } + } + function settings_audio_microphone(container, modal) { + const registry = new events.Registry(); + registry.enable_debug("settings-microphone"); + modal_settings.initialize_audio_microphone_controller(registry); + modal_settings.initialize_audio_microphone_view(container, registry); + modal.close_listener.push(() => registry.fire_async("deinitialize")); + return; + } + function settings_identity_profiles(container, modal) { + const registry = new events.Registry(); + //registry.enable_debug("settings-identity"); + modal_settings.initialize_identity_profiles_controller(registry); + modal_settings.initialize_identity_profiles_view(container, registry, { + forum_setuppable: true + }); + registry.on("setup-forum-connection", event => { + modal.htmlTag.find('.entry[container="identity-forum"]').trigger('click'); + }); + return () => registry.fire("reload-profile"); + } + function settings_audio_speaker(container, modal) { + /* devices */ + { + const container_devices = container.find(".left .container-devices"); + const contianer_error = container.find(".left .container-error"); + const update_devices = () => { + container_devices.children().remove(); + const current_selected = audio.player.current_device(); + const generate_device = (device) => { + const selected = device === current_selected || (typeof (current_selected) !== "undefined" && typeof (device) !== "undefined" && current_selected.device_id == device.device_id); + const tag = $.spawn("div").addClass("device").toggleClass("selected", selected).append($.spawn("div").addClass("container-selected").append($.spawn("div").addClass("icon_em client-apply")), $.spawn("div").addClass("container-name").append($.spawn("div").addClass("device-driver").text(device ? (device.driver || "Unknown driver") : "No device"), $.spawn("div").addClass("device-name").text(device ? (device.name || "Unknown name") : "No device"))); + tag.on('click', event => { + if (tag.hasClass("selected")) + return; + const _old = container_devices.find(".selected"); + _old.removeClass("selected"); + tag.addClass("selected"); + audio.player.set_device(device ? device.device_id : null).then(() => { + console.debug(_translations.ySq4xxUe || (_translations.ySq4xxUe = tr("Changed default speaker device"))); + }).catch((error) => { + _old.addClass("selected"); + tag.removeClass("selected"); + console.error(_translations.U5kKlwd8 || (_translations.U5kKlwd8 = tr("Failed to change speaker to device %o: %o")), device, error); + createErrorModal(_translations.i6XwJzrt || (_translations.i6XwJzrt = tr("Failed to change speaker")), MessageHelper.formatMessage(_translations.pwBc9du5 || (_translations.pwBc9du5 = tr("Failed to change the speaker device to the target speaker{:br:}{}")), error)).open(); + }); + }); + return tag; + }; + generate_device(undefined).appendTo(container_devices); + audio.player.available_devices().then(result => { + contianer_error.text("").hide(); + result.forEach(e => generate_device(e).appendTo(container_devices)); + }).catch(error => { + if (typeof (error) === "string") + contianer_error.text(error).show(); + console.log(_translations.tYO7MhPX || (_translations.tYO7MhPX = tr("Failed to query available speaker devices: %o")), error); + contianer_error.text(_translations.TPswmcMR || (_translations.TPswmcMR = tr("Errors occurred (View console)"))).show(); + }); + }; + update_devices(); + const button_update = container.find(".button-update"); + button_update.on('click', (event) => __awaiter(this, void 0, void 0, function* () { + button_update.prop("disabled", true); + try { + update_devices(); + } + catch (error) { + console.error(_translations.P9SCZZQG || (_translations.P9SCZZQG = tr("Failed to build new speaker device list: %o")), error); + } + button_update.prop("disabled", false); + })); + } + /* slider */ + { + { + const container_master = container.find(".container-volume-master"); + const slider = container_master.find(".container-slider"); + sliderfy(slider, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: settings.static_global(Settings.KEY_SOUND_MASTER, 100), + value_field: [container_master.find(".container-value")] + }); + slider.on('change', event => { + const volume = parseInt(slider.attr('value')); + if (audio.player.set_master_volume) + audio.player.set_master_volume(volume / 100); + settings.changeGlobal(Settings.KEY_SOUND_MASTER, volume); + }); + } + { + const container_soundpack = container.find(".container-volume-soundpack"); + const slider = container_soundpack.find(".container-slider"); + sliderfy(slider, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: settings.static_global(Settings.KEY_SOUND_MASTER_SOUNDS, 100), + value_field: [container_soundpack.find(".container-value")] + }); + slider.on('change', event => { + const volume = parseInt(slider.attr('value')); + sound.set_master_volume(volume / 100); + settings.changeGlobal(Settings.KEY_SOUND_MASTER_SOUNDS, volume); + }); + } + } + /* button test sound */ + { + container.find(".button-test-sound").on('click', event => { + sound.manager.play(Sound.SOUND_TEST, { + default_volume: 1, + ignore_muted: true, + ignore_overlap: true + }); + }); + } + } + function settings_audio_sounds(contianer, modal) { + /* initialize sound list */ + { + const container_sounds = contianer.find(".container-sounds"); + const generate_sound = (_sound) => { + let tag_play_pause, tag_play, tag_pause, tag_input_muted; + let tag = $.spawn("div").addClass("sound").append(tag_play_pause = $.spawn("div").addClass("container-button-play_pause").append(tag_play = $.spawn("img").attr("src", "img/icon_sound_play.svg"), tag_pause = $.spawn("img").attr("src", "img/icon_sound_pause.svg")), $.spawn("div").addClass("container-name").text(_sound), $.spawn("label").addClass("container-button-toggle").append($.spawn("div").addClass("switch").append(tag_input_muted = $.spawn("input").attr("type", "checkbox"), $.spawn("span").addClass("slider").append($.spawn("div").addClass("dot"))))); + tag_play_pause.on('click', event => { + if (tag_pause.is(":visible")) + return; + tag_play.hide(); + tag_pause.show(); + const _done = flag => { + tag_pause.hide(); + tag_play.show(); + }; + const _timeout = setTimeout(() => _done(false), 10 * 1000); /* the sounds are not longer than 10 seconds */ + sound.manager.play(_sound, { + ignore_overlap: true, + ignore_muted: true, + default_volume: 1, + callback: flag => { + clearTimeout(_timeout); + _done(flag); + } + }); + }); + tag_pause.hide(); + tag_input_muted.prop("checked", sound.get_sound_volume(_sound, 1) > 0); + tag_input_muted.on('change', event => { + const volume = tag_input_muted.prop("checked") ? 1 : 0; + sound.set_sound_volume(_sound, volume); + console.log(_translations.O6ifCkPV || (_translations.O6ifCkPV = tr("Changed sound volume to %o for sound %o")), volume, _sound); + }); + return tag; + }; + //container-sounds + for (const sound_key in Sound) + generate_sound(Sound[sound_key]).appendTo(container_sounds); + /* the filter */ + const input_filter = contianer.find(".input-sounds-filter"); + input_filter.on('change keyup', event => { + const filter = input_filter.val(); + container_sounds.find(".sound").each((_, _element) => { + const element = $(_element); + element.toggle(filter.length == 0 || element.text().toLowerCase().indexOf(filter) !== -1); + }); + }); + } + const overlap_tag = contianer.find(".option-overlap-same"); + overlap_tag.on('change', event => { + const activated = event.target.checked; + sound.set_overlap_activated(activated); + }).prop("checked", sound.overlap_activated()); + const mute_tag = contianer.find(".option-mute-output"); + mute_tag.on('change', event => { + const activated = event.target.checked; + sound.set_ignore_output_muted(!activated); + }).prop("checked", !sound.ignore_output_muted()); + modal.close_listener.push(sound.save); + } + let modal_settings; + (function (modal_settings) { + function initialize_identity_profiles_controller(event_registry) { + const send_error = (event, profile, text) => event_registry.fire_async(event, { status: "error", profile_id: profile, error: text }); + event_registry.on("create-profile", event => { + const profile = profiles.create_new_profile(event.name); + profiles.mark_need_save(); + event_registry.fire_async("create-profile-result", { + status: "success", + name: event.name, + profile_id: profile.id + }); + }); + event_registry.on("delete-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.ijGZQoJy || (_translations.ijGZQoJy = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("delete-profile-result", event.profile_id, _translations.oPL5wq0y || (_translations.oPL5wq0y = tr("Unknown profile"))); + return; + } + profiles.delete_profile(profile); + event_registry.fire_async("delete-profile-result", { status: "success", profile_id: event.profile_id }); + }); + const build_profile_info = (profile) => { + const forum_data = profile.selected_identity(profiles.identities.IdentitifyType.TEAFORO); + const teamspeak_data = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK); + const nickname_data = profile.selected_identity(profiles.identities.IdentitifyType.NICKNAME); + return { + id: profile.id, + name: profile.profile_name, + nickname: profile.default_username, + identity_type: profile.selected_identity_type, + identity_forum: !forum_data ? undefined : { + valid: forum_data.valid(), + fallback_name: forum_data.fallback_name() + }, + identity_nickname: !nickname_data ? undefined : { + name: nickname_data.name(), + fallback_name: nickname_data.fallback_name() + }, + identity_teamspeak: !teamspeak_data ? undefined : { + unique_id: teamspeak_data.uid(), + fallback_name: teamspeak_data.fallback_name() + } + }; + }; + event_registry.on("query-profile-list", event => { + event_registry.fire_async("query-profile-list-result", { status: "success", profiles: profiles.profiles().map(e => build_profile_info(e)) }); + }); + event_registry.on("query-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.UBrvt3sv || (_translations.UBrvt3sv = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("query-profile-result", event.profile_id, _translations.phHj2aiZ || (_translations.phHj2aiZ = tr("Unknown profile"))); + return; + } + event_registry.fire_async("query-profile-result", { status: "success", profile_id: event.profile_id, info: build_profile_info(profile) }); + }); + event_registry.on("set-default-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.XUo6cT78 || (_translations.XUo6cT78 = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("set-default-profile-result", event.profile_id, _translations.dCwo9upm || (_translations.dCwo9upm = tr("Unknown profile"))); + return; + } + const old = profiles.set_default_profile(profile); + event_registry.fire_async("set-default-profile-result", { status: "success", old_profile_id: event.profile_id, new_profile_id: old.id }); + }); + event_registry.on("set-profile-name", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.AlGUCXNW || (_translations.AlGUCXNW = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("set-profile-name-result", event.profile_id, _translations.o2FZws2H || (_translations.o2FZws2H = tr("Unknown profile"))); + return; + } + profile.profile_name = event.name; + profiles.mark_need_save(); + event_registry.fire_async("set-profile-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + event_registry.on("set-default-name", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.urEnnhUv || (_translations.urEnnhUv = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("set-default-name-result", event.profile_id, _translations.RbO_12FB || (_translations.RbO_12FB = tr("Unknown profile"))); + return; + } + profile.default_username = event.name; + profiles.mark_need_save(); + event_registry.fire_async("set-default-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + event_registry.on("set-identity-name-name", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.He1Lhlei || (_translations.He1Lhlei = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("set-identity-name-name-result", event.profile_id, _translations.BsQvH7KD || (_translations.BsQvH7KD = tr("Unknown profile"))); + return; + } + let identity = profile.selected_identity(profiles.identities.IdentitifyType.NICKNAME); + if (!identity) + profile.set_identity(profiles.identities.IdentitifyType.NICKNAME, identity = new profiles.identities.NameIdentity()); + identity.set_name(event.name); + profiles.mark_need_save(); + event_registry.fire_async("set-identity-name-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + event_registry.on("query-profile-validity", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.dryBR96k || (_translations.dryBR96k = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("query-profile-validity-result", event.profile_id, _translations.NmI6OOx2 || (_translations.NmI6OOx2 = tr("Unknown profile"))); + return; + } + event_registry.fire_async("query-profile-validity-result", { status: "success", profile_id: event.profile_id, valid: profile.valid() }); + }); + event_registry.on("query-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.ISCwnQTE || (_translations.ISCwnQTE = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("query-identity-teamspeak-result", event.profile_id, _translations.gT4hzBi0 || (_translations.gT4hzBi0 = tr("Unknown profile"))); + return; + } + const ts = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK); + if (!ts) { + event_registry.fire_async("query-identity-teamspeak-result", { status: "error", profile_id: event.profile_id, error: _translations.DaKCu_FT || (_translations.DaKCu_FT = tr("Missing identity")) }); + return; + } + ts.level().then(level => { + event_registry.fire_async("query-identity-teamspeak-result", { status: "success", level: level, profile_id: event.profile_id }); + }).catch(error => { + send_error("query-identity-teamspeak-result", event.profile_id, _translations.D8X_oTbc || (_translations.D8X_oTbc = tr("failed to calculate level"))); + }); + }); + event_registry.on("select-identity-type", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.uLiVzH1c || (_translations.uLiVzH1c = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + return; + } + profile.selected_identity_type = event.identity_type; + profiles.mark_need_save(); + }); + event_registry.on("generate-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.Zw07OLxG || (_translations.Zw07OLxG = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + send_error("generate-identity-teamspeak-result", event.profile_id, _translations.NtGL5zmt || (_translations.NtGL5zmt = tr("Unknown profile"))); + return; + } + profiles.identities.TeaSpeakIdentity.generate_new().then(identity => { + profile.set_identity(profiles.identities.IdentitifyType.TEAMSPEAK, identity); + profiles.mark_need_save(); + identity.level().then(level => { + event_registry.fire_async("generate-identity-teamspeak-result", { + status: "success", + profile_id: event.profile_id, + unique_id: identity.uid(), + level: level + }); + }).catch(error => { + console.error(_translations.r6jWa2i3 || (_translations.r6jWa2i3 = tr("Failed to calculate level for a new identity. Error object: %o")), error); + send_error("generate-identity-teamspeak-result", event.profile_id, (_translations.sH3hrr8x || (_translations.sH3hrr8x = tr("failed to calculate level: "))) + error); + }); + }).catch(error => { + console.error(_translations.gtbrQF6_ || (_translations.gtbrQF6_ = tr("Failed to generate a new identity. Error object: %o")), error); + send_error("generate-identity-teamspeak-result", event.profile_id, (_translations.DW7n4WE_ || (_translations.DW7n4WE_ = tr("failed to generate identity: "))) + error); + }); + }); + event_registry.on("import-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.u_dYuuQh || (_translations.u_dYuuQh = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + return; + } + Modals.spawnTeamSpeakIdentityImport(identity => { + profile.set_identity(profiles.identities.IdentitifyType.TEAMSPEAK, identity); + profiles.mark_need_save(); + identity.level().catch(error => { + console.error(_translations.C8qosC5x || (_translations.C8qosC5x = tr("Failed to calculate level for a new imported identity. Error object: %o")), error); + return Promise.resolve(undefined); + }).then(level => { + event_registry.fire_async("import-identity-teamspeak-result", { + profile_id: event.profile_id, + unique_id: identity.uid(), + level: level + }); + }); + }); + }); + event_registry.on("improve-identity-teamspeak-level", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.hGsVw3C1 || (_translations.hGsVw3C1 = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + return; + } + const identity = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK); + if (!identity) + return; + Modals.spawnTeamSpeakIdentityImprove(identity, profile.profile_name).close_listener.push(() => { + profiles.mark_need_save(); + identity.level().then(level => { + event_registry.fire_async("improve-identity-teamspeak-level-update", { profile_id: event.profile_id, new_level: level }); + }).catch(error => { + log.error(LogCategory.CLIENT, _translations.jvF1L94q || (_translations.jvF1L94q = tr("Failed to calculate identity level after improvement (%o)")), error); + }); + }); + }); + event_registry.on("export-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if (!profile) { + log.warn(LogCategory.CLIENT, _translations.ePInsAEF || (_translations.ePInsAEF = tr("Received profile event with unknown profile id (event: %s, id: %s)")), event.type, event.profile_id); + return; + } + const identity = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK); + if (!identity) + return; + identity.export_ts(true).then(data => { + const element = $.spawn("a") + .text("donwload") + .attr("href", "data:test/plain;charset=utf-8," + encodeURIComponent(data)) + .attr("download", name + ".ini") + .css("display", "none") + .appendTo($("body")); + element[0].click(); + element.remove(); + }).catch(error => { + console.error(error); + createErrorModal(_translations.QE8mYeI3 || (_translations.QE8mYeI3 = tr("Failed to export identity")), (_translations.c93vvmwR || (_translations.c93vvmwR = tr("Failed to export and save identity.
Error: "))) + error).open(); + }); + }); + } + modal_settings.initialize_identity_profiles_controller = initialize_identity_profiles_controller; + function initialize_identity_profiles_view(container, event_registry, settings) { + /* profile list */ + { + const container_profiles = container.find(".container-profiles"); + let selected_profile; + const overlay_error = container_profiles.find(".overlay-error"); + const overlay_timeout = container_profiles.find(".overlay-timeout"); + const overlay_empty = container_profiles.find(".overlay-empty"); + const build_profile = (profile, selected) => { + let tag_avatar, tag_default; + let tag = $.spawn("div").addClass("profile").attr("profile-id", profile.id).append(tag_avatar = $.spawn("div").addClass("container-avatar"), $.spawn("div").addClass("container-info").append($.spawn("div").addClass("container-type").append($.spawn("div").addClass("identity-type").text(profile.identity_type || (_translations.W6aQuY5Q || (_translations.W6aQuY5Q = tr("Type unset")))), tag_default = $.spawn("div").addClass("tag-default").text(_translations._aqNX0xB || (_translations._aqNX0xB = tr("(Default)"))), $.spawn("div").addClass("icon_em icon-status").hide()), $.spawn("div").addClass("profile-name").text(profile.name || (_translations.e3VI8KGN || (_translations.e3VI8KGN = tr("Unnamed")))))); + tag_avatar.hide(); /* no avatars yet */ + tag.on('click', event => event_registry.fire("select-profile", { profile_id: profile.id })); + tag.toggleClass("selected", selected); + tag_default.toggle(profile.id === "default"); + event_registry.fire("query-profile-validity", { profile_id: profile.id }); + return tag; + }; + event_registry.on("select-profile", event => { + container_profiles.find(".profile").removeClass("selected"); + container_profiles.find(".profile[profile-id='" + event.profile_id + "']").addClass("selected"); + selected_profile = event.profile_id; + }); + event_registry.on("query-profile-list", event => { + container_profiles.find(".profile").remove(); + }); + event_registry.on("query-profile-list-result", event => { + container_profiles.find(".overlay").hide(); + if (event.status === "error") { + overlay_error.show().find(".error").text(event.error || (_translations.vjY9fepV || (_translations.vjY9fepV = tr("unknown error")))); + return; + } + else if (event.status === "timeout") { + overlay_timeout.show(); + return; + } + if (!event.profiles.length) { + overlay_empty.show(); + return; + } + container_profiles.find(".overlay").hide(); + container_profiles.find(".profile").remove(); + event.profiles.forEach(e => build_profile(e, e.id == selected_profile).appendTo(container_profiles)); + }); + event_registry.on("delete-profile-result", event => { + if (event.status !== "success") + return; + //TODO: Animate removal? + container_profiles.find(".profile[profile-id='" + event.profile_id + "']").remove(); + }); + event_registry.on('create-profile-result', event => { + if (event.status !== "success") + return; + event_registry.fire("query-profile-list"); + event_registry.one("query-profile-list-result", e => event_registry.fire("select-profile", { profile_id: event.profile_id })); + }); + event_registry.on("set-profile-name-result", event => { + if (event.status !== "success") + return; + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".profile-name").text(event.name || (_translations.jedfrTq7 || (_translations.jedfrTq7 = tr("Unnamed")))); + }); + event_registry.on("set-default-profile-result", event => { + if (event.status !== "success") + return; + const old_profile = container_profiles.find(".profile[profile-id='default']"); + const new_profile = container_profiles.find(".profile[profile-id='" + event.old_profile_id + "']"); + old_profile.attr("profile-id", event.new_profile_id).find(".tag-default").hide(); + new_profile.attr("profile-id", "default").find(".tag-default").show(); + }); + event_registry.on("select-identity-type", event => { + if (!event.identity_type) + return; + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".identity-type").text(event.identity_type.toUpperCase() || (_translations.dJqFoWNu || (_translations.dJqFoWNu = tr("Type unset")))); + }); + event_registry.on("query-profile-validity-result", event => { + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".icon-status") + .show() + .toggleClass("client-apply", event.status === "success" && event.valid) + .toggleClass("client-delete", event.status !== "success" || !event.valid) + .attr("title", event.status === "success" ? event.valid ? _translations.QMbj98Jl || (_translations.QMbj98Jl = tr("Profile is valid")) : _translations.oXE3AOOj || (_translations.oXE3AOOj = tr("Provile is invalid")) : event.error || (_translations.u3YjNURr || (_translations.u3YjNURr = tr("failed to query status")))); + }); + /* status indicator updaters */ + event_registry.on("select-identity-type", event => { + if (!event.profile_id) + return; + /* we need a short delay so everything could apply*/ + setTimeout(() => { + event_registry.fire("query-profile-validity", { profile_id: event.profile_id }); + }, 100); + }); + event_registry.on(["set-default-name-result", "set-profile-name-result", "set-identity-name-name-result", "generate-identity-teamspeak-result"], event => { + if (!('status' in event) || !('profile_id' in event)) { + log.warn(LogCategory.CLIENT, _translations.bdvaz2Fu || (_translations.bdvaz2Fu = tr("Profile status watcher encountered an unuseal event!"))); + return; + } + if (event.status !== "success") + return; + event_registry.fire("query-profile-validity", { profile_id: event.profile_id }); + }); + } + /* list buttons */ + { + /* reload */ + { + const button = container.find(".button-reload-list"); + button.on('click', event => event_registry.fire("query-profile-list")); + event_registry.on("query-profile-list", event => button.prop("disabled", true)); + event_registry.on("query-profile-list-result", event => button.prop("disabled", false)); + } + /* set default */ + { + const button = container.find(".button-set-default"); + let current_profile; + button.on('click', event => event_registry.fire("set-default-profile", { profile_id: current_profile })); + event_registry.on("select-profile", event => { + current_profile = event.profile_id; + button.prop("disabled", !event.profile_id || event.profile_id === "default"); + }); + event_registry.on("set-default-profile-result", event => { + if (event.status === "success") + return; + createErrorModal(_translations.yZrWIhG1 || (_translations.yZrWIhG1 = tr("Failed to set default profile")), (_translations.OergtM4i || (_translations.OergtM4i = tr("Failed to set default profile:"))) + "
" + (event.status === "timeout" ? _translations.AcQQmDNG || (_translations.AcQQmDNG = tr("request timeout")) : (event.error || (_translations.BIHNDdrV || (_translations.BIHNDdrV = tr("unknown error")))))).open(); + }); + button.prop("disabled", true); + } + /* delete button */ + { + const button = container.find(".button-delete"); + let current_profile; + button.on('click', event => { + if (!current_profile || current_profile === "default") + return; + Modals.spawnYesNo(_translations.LpQGcaVD || (_translations.LpQGcaVD = tr("Are you sure?")), _translations.Odgcch92 || (_translations.Odgcch92 = tr("Do you really want to delete this profile?")), result => { + if (result) + event_registry.fire("delete-profile", { profile_id: current_profile }); + }); + }); + event_registry.on("delete-profile-result", event => { + if (event.status === "success") + return; + createErrorModal(_translations.PKQe99Hv || (_translations.PKQe99Hv = tr("Failed to delete profile")), (_translations.uZOymAv8 || (_translations.uZOymAv8 = tr("Failed to delete profile:"))) + "
" + (event.status === "timeout" ? _translations.iRIw6oQA || (_translations.iRIw6oQA = tr("request timeout")) : (event.error || (_translations.uskvfZ0w || (_translations.uskvfZ0w = tr("unknown error")))))).open(); + }); + event_registry.on("select-profile", event => { + current_profile = event.profile_id; + button.prop("disabled", !event.profile_id || event.profile_id === "default"); + }); + } + /* create button */ + { + const button = container.find(".button-create"); + button.on('click', event => { + createInputModal(_translations.w92k7IcM || (_translations.w92k7IcM = tr("Please enter a name")), _translations.aeeSZ1W6 || (_translations.aeeSZ1W6 = tr("Please enter a name for the new profile:")), text => text.length >= 3 && !profiles.find_profile_by_name(text), value => { + if (value) + event_registry.fire("create-profile", { name: value }); + }).open(); + }); + event_registry.on('create-profile', event => button.prop("disabled", true)); + event_registry.on("create-profile-result", event => { + button.prop("disabled", false); + if (event.status === "success") { + event_registry.fire("select-profile", { profile_id: event.profile_id }); + return; + } + createErrorModal(_translations.x64gbqPE || (_translations.x64gbqPE = tr("Failed to create profile")), (_translations.f7eMoiYk || (_translations.f7eMoiYk = tr("Failed to create new profile:"))) + "
" + (event.status === "timeout" ? _translations.KRG7a6pk || (_translations.KRG7a6pk = tr("request timeout")) : (event.error || (_translations.WnIitvJ5 || (_translations.WnIitvJ5 = tr("unknown error")))))).open(); + }); + } + } + /* profile info */ + { + let current_profile; + const error_text = event => event.status === "timeout" ? _translations.cgIp7Mq0 || (_translations.cgIp7Mq0 = tr("request timeout")) : (event.error || (_translations.vmnyPmOT || (_translations.vmnyPmOT = tr("unknown error")))); + /* general info */ + { + /* profile name */ + { + const input = container.find(".profile-name"); + let last_name; + const update_name = () => input.prop("disabled", false) + .val(last_name) + .attr("placeholder", _translations.il2IXXi7 || (_translations.il2IXXi7 = tr("Profile name"))) + .parent().removeClass("is-invalid"); + const info_name = text => input.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + event_registry.on("query-profile", event => { + if (event.profile_id !== current_profile) + return; + info_name(_translations.eLGB6hfn || (_translations.eLGB6hfn = tr("loading"))); + }); + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status === "success") { + last_name = event.info.name; + update_name(); + } + else { + info_name(error_text(event)); + } + }); + event_registry.on("set-profile-name", event => { + if (event.profile_id !== current_profile) + return; + info_name(_translations.rQQstdmx || (_translations.rQQstdmx = tr("saving"))); + }); + event_registry.on("set-profile-name-result", event => { + if (event.status !== "success") { + createErrorModal(_translations.JsYl7deR || (_translations.JsYl7deR = tr("Failed to change profile name")), (_translations.V2dCo75J || (_translations.V2dCo75J = tr("Failed to create apply new name:"))) + "
" + error_text(event)).open(); + } + else { + last_name = event.name; + } + update_name(); + }); + input.on('keyup', event => { + const text = input.val(); + const profile = profiles.find_profile_by_name(text); + input.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); + }).on('change', event => { + const text = input.val(); + const profile = profiles.find_profile_by_name(text); + if (text.length < 3 || (profile && profile.id != current_profile)) + return; + event_registry.fire("set-profile-name", { profile_id: current_profile, name: text }); + }); + } + /* nickname name */ + { + const input = container.find(".profile-default-name"); + let last_name = null, fallback_names = {}, current_identity_type = ""; + const update_name = () => input.prop("disabled", false) + .val(last_name) + .attr("placeholder", fallback_names[current_identity_type] || (_translations.M_33FHbX || (_translations.M_33FHbX = tr("Another TeaSpeak user")))) + .parent().removeClass("is-invalid"); + const info_name = text => input.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + event_registry.on("query-profile", event => { + if (event.profile_id !== current_profile) + return; + input.prop("disabled", true).val(null).attr("placeholder", _translations.K95eJH8w || (_translations.K95eJH8w = tr("loading"))); + }); + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status === "success") { + current_identity_type = event.info.identity_type; + fallback_names["nickname"] = event.info.identity_nickname ? event.info.identity_nickname.fallback_name : undefined; + fallback_names["teaforo"] = event.info.identity_forum ? event.info.identity_forum.fallback_name : undefined; + fallback_names["teamspeak"] = event.info.identity_teamspeak ? event.info.identity_teamspeak.fallback_name : undefined; + last_name = event.info.nickname; + update_name(); + } + else { + info_name(error_text(event)); + } + }); + event_registry.on("select-identity-type", event => { + if (current_identity_type === event.identity_type) + return; + current_identity_type = event.identity_type; + update_name(); + }); + event_registry.on("set-default-name", event => { + if (event.profile_id !== current_profile) + return; + info_name(_translations.uUD_P0G8 || (_translations.uUD_P0G8 = tr("saving"))); + }); + event_registry.on("set-default-name-result", event => { + if (event.status !== "success") { + createErrorModal(_translations.B2niZHis || (_translations.B2niZHis = tr("Failed to change nickname")), (_translations.lglQPHRb || (_translations.lglQPHRb = tr("Failed to create apply new nickname:"))) + "
" + error_text(event)).open(); + } + else { + last_name = event.name; + } + update_name(); + }); + input.on('keyup', event => { + const text = input.val(); + input.parent().toggleClass("is-invalid", text.length != 0 && text.length < 3); + }).on('change', event => { + const text = input.val(); + if (text.length != 0 && text.length < 3) + return; + event_registry.fire("set-default-name", { profile_id: current_profile, name: text }); + }); + } + /* identity type */ + { + const select_identity_type = container.find(".profile-identity-type"); + const show_message = (text, is_invalid) => select_identity_type + .toggleClass("is-invalid", is_invalid) + .prop("disabled", true) + .find("option[value=error]") + .text(text) + .prop("selected", true); + const set_type = type => select_identity_type + .toggleClass("is-invalid", type === "unset") + .prop("disabled", false) + .find("option[value=" + type + "]") + .prop("selected", true); + event_registry.on("query-profile", event => show_message(_translations.gjr1Hjwb || (_translations.gjr1Hjwb = tr("loading")), false)); + event_registry.on("select-identity-type", event => { + if (event.profile_id !== current_profile) + return; + set_type(event.identity_type || "unset"); + }); + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status === "success") + event_registry.fire("select-identity-type", { profile_id: event.profile_id, identity_type: event.info.identity_type }); + else + show_message(error_text(event), false); + }); + select_identity_type.on('change', event => { + const type = select_identity_type.val().toLowerCase(); + if (type === "error" || type == "unset") + return; + event_registry.fire("select-identity-type", { profile_id: current_profile, identity_type: type }); + }); + } + /* avatar */ + { + container.find(".button-change-avatar").hide(); + } + } + /* special info TeamSpeak */ + { + const container_settings = container.find(".container-teamspeak"); + const container_valid = container_settings.find(".container-valid"); + const container_invalid = container_settings.find(".container-invalid"); + const input_current_level = container_settings.find(".current-level"); + const input_unique_id = container_settings.find(".unique-id"); + const button_new = container_settings.find(".button-new"); + const button_improve = container_settings.find(".button-improve"); + const button_import = container_settings.find(".button-import"); + const button_export = container_settings.find(".button-export"); + let is_profile_generated = false; + event_registry.on("select-identity-type", event => { + if (event.profile_id !== current_profile) + return; + container_settings.toggle(event.identity_type === "teamspeak"); + }); + event_registry.on("query-profile", event => { + input_unique_id.val(null).attr("placeholder", _translations.TQnO8NzW || (_translations.TQnO8NzW = tr("loading"))); + input_current_level.val(null).attr("placeholder", _translations.UOYpbAz7 || (_translations.UOYpbAz7 = tr("loading"))); + button_new.prop("disabled", true); + button_improve.prop("disabled", true); + button_import.prop("disabled", true); + button_export.prop("disabled", true); + }); + const update_identity = (state, unique_id, level) => { + if (state === "not-created") { + container_invalid.show(); + container_valid.hide(); + button_improve.prop("disabled", true); + button_export.prop("disabled", true); + } + else { + container_invalid.hide(); + container_valid.show(); + input_unique_id.val(unique_id).attr("placeholder", null); + if (typeof level !== "number") + event_registry.fire("query-identity-teamspeak", { profile_id: current_profile }); + else + input_current_level.val(level).attr("placeholder", null); + button_improve.prop("disabled", false); + button_export.prop("disabled", false); + } + is_profile_generated = state === "created"; + button_new.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); + button_import.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); + button_new.prop("disabled", false); + button_import.prop("disabled", false); + }; + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status !== "success") { + input_unique_id.val(null).attr("placeholder", error_text(event)); + return; + } + if (!event.info.identity_teamspeak) + update_identity("not-created"); + else + update_identity("created", event.info.identity_teamspeak.unique_id); + }); + event_registry.on("query-identity-teamspeak-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status === "success") { + input_current_level.val(event.level).attr("placeholder", null); + } + else { + input_current_level.val(null).attr("placeholder", error_text(event)); + } + }); + /* the new button */ + { + button_new.on('click', event => { + if (is_profile_generated) { + Modals.spawnYesNo(_translations.j_B1XMlg || (_translations.j_B1XMlg = tr("Are you sure")), _translations.LhMjT0D6 || (_translations.LhMjT0D6 = tr("Do you really want to generate a new identity and override the old identity?")), result => { + if (result) + event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); + }); + } + else { + event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); + } + }); + event_registry.on("generate-identity-teamspeak-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status !== "success") { + createErrorModal(_translations.QXs1fW_l || (_translations.QXs1fW_l = tr("Failed to generate a new identity")), (_translations.LvN8hZ1p || (_translations.LvN8hZ1p = tr("Failed to create a new identity:"))) + "
" + error_text(event)).open(); + return; + } + update_identity("created", event.unique_id, event.level); + createInfoModal(_translations.mfkiBtNv || (_translations.mfkiBtNv = tr("Identity generated")), _translations.TgHz611r || (_translations.TgHz611r = tr("A new identity had been successfully generated"))).open(); + }); + } + /* the import identity */ + { + button_import.on('click', event => { + if (is_profile_generated) { + Modals.spawnYesNo(_translations.e35FyXnh || (_translations.e35FyXnh = tr("Are you sure")), _translations.w9qoM29d || (_translations.w9qoM29d = tr("Do you really want to import a new identity and override the old identity?")), result => { + if (result) + event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); + }); + } + else { + event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); + } + }); + event_registry.on("improve-identity-teamspeak-level-update", event => { + if (event.profile_id !== current_profile) + return; + input_current_level.val(event.new_level).attr("placeholder", null); + }); + event_registry.on("import-identity-teamspeak-result", event => { + if (event.profile_id !== current_profile) + return; + event_registry.fire_async("query-profile", { profile_id: event.profile_id }); /* we do it like this so the default nickname changes as well */ + createInfoModal(_translations.hZrU5_SP || (_translations.hZrU5_SP = tr("Identity imported")), _translations.K5k4dA5r || (_translations.K5k4dA5r = tr("Your identity had been successfully imported generated"))).open(); + }); + } + /* identity export */ + { + button_export.on('click', event => { + createInputModal(_translations.pi0qV71w || (_translations.pi0qV71w = tr("File name")), _translations.f_7UqLhu || (_translations.f_7UqLhu = tr("Please enter the file name")), text => !!text, name => { + if (name) + event_registry.fire("export-identity-teamspeak", { profile_id: current_profile, filename: name }); + }).open(); + }); + } + /* the improve button */ + button_improve.on('click', event => event_registry.fire("improve-identity-teamspeak-level", { profile_id: current_profile })); + } + /* special info TeaSpeak - Forum */ + { + const container_settings = container.find(".container-teaforo"); + const container_valid = container_settings.find(".container-valid"); + const container_invalid = container_settings.find(".container-invalid"); + const button_setup = container_settings.find(".button-setup"); + event_registry.on("select-identity-type", event => { + if (event.profile_id !== current_profile) + return; + container_settings.toggle(event.identity_type === "teaforo"); + }); + event_registry.on("query-profile", event => { + container_valid.toggle(false); + container_invalid.toggle(false); + }); + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + const valid = event.status === "success" && event.info.identity_forum && event.info.identity_forum.valid; + container_valid.toggle(!!valid); + container_invalid.toggle(!valid); + }); + button_setup.on('click', event => event_registry.fire_async("setup-forum-connection")); + button_setup.toggle(settings.forum_setuppable); + } + /* special info nickname */ + { + const container_settings = container.find(".container-nickname"); + const input_nickname = container_settings.find(".nickname"); + let last_name; + const update_name = () => input_nickname.prop("disabled", false) + .val(last_name) + .attr("placeholder", _translations.vlf6z52a || (_translations.vlf6z52a = tr("Identity base name"))) + .parent().removeClass("is-invalid"); + const show_info = text => input_nickname.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + event_registry.on("select-identity-type", event => event.profile_id === current_profile && container_settings.toggle(event.identity_type === "nickname")); + event_registry.on("query-profile", event => { + if (event.profile_id !== current_profile) + return; + show_info(_translations.LbOr04qi || (_translations.LbOr04qi = tr("loading"))); + }); + event_registry.on("query-profile-result", event => { + if (event.profile_id !== current_profile) + return; + if (event.status === "success") { + last_name = event.info.identity_nickname ? event.info.identity_nickname.name : null; + update_name(); + } + else { + show_info(error_text(event)); + } + }); + event_registry.on("set-identity-name-name", event => { + if (event.profile_id !== current_profile) + return; + show_info(_translations.NFAJFMiD || (_translations.NFAJFMiD = tr("saving"))); + }); + event_registry.on("set-identity-name-name-result", event => { + if (event.status !== "success") { + createErrorModal(_translations.TjAtodXU || (_translations.TjAtodXU = tr("Failed to change name")), (_translations.hLnfEYZl || (_translations.hLnfEYZl = tr("Failed to create new name:"))) + "
" + error_text(event)).open(); + } + else { + last_name = event.name; + } + update_name(); + }); + input_nickname.on('keyup', event => { + const text = input_nickname.val(); + const profile = profiles.find_profile_by_name(text); + input_nickname.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); + }).on('change', event => { + const text = input_nickname.val(); + const profile = profiles.find_profile_by_name(text); + if (text.length < 3 || (profile && profile.id != current_profile)) + return; + event_registry.fire("set-identity-name-name", { profile_id: current_profile, name: text }); + }); + } + event_registry.on("select-profile", e => current_profile = e.profile_id); + } + /* timeouts */ + { + /* profile list */ + { + let timeout; + event_registry.on("query-profile-list", event => timeout = setTimeout(() => event_registry.fire("query-profile-list-result", { status: "timeout" }), 5000)); + event_registry.on("query-profile-list-result", event => { + clearTimeout(timeout); + timeout = undefined; + }); + } + /* profile create */ + { + const timeouts = {}; + event_registry.on("create-profile", event => { + clearTimeout(timeouts[event.name]); + timeouts[event.name] = setTimeout(() => { + event_registry.fire("create-profile-result", { name: event.name, status: "timeout" }); + }, 5000); + }); + event_registry.on("create-profile-result", event => { + clearTimeout(timeouts[event.name]); + delete timeouts[event.name]; + }); + } + /* profile set default create */ + { + const timeouts = {}; + event_registry.on("set-default-profile", event => { + clearTimeout(timeouts[event.profile_id]); + timeouts[event.profile_id] = setTimeout(() => { + event_registry.fire("set-default-profile-result", { old_profile_id: event.profile_id, status: "timeout" }); + }, 5000); + }); + event_registry.on("set-default-profile-result", event => { + clearTimeout(timeouts[event.old_profile_id]); + delete timeouts[event.old_profile_id]; + }); + } + const create_standard_timeout = (event, response_event, key) => { + const timeouts = {}; + event_registry.on(event, event => { + clearTimeout(timeouts[event[key]]); + timeouts[event[key]] = setTimeout(() => { + const timeout_event = { status: "timeout" }; + timeout_event[key] = event[key]; + event_registry.fire(response_event, timeout_event); + }, 5000); + }); + event_registry.on(response_event, event => { + clearTimeout(timeouts[event[key]]); + delete timeouts[event[key]]; + }); + }; + create_standard_timeout("query-profile", "query-profile-result", "profile_id"); + create_standard_timeout("query-identity-teamspeak", "query-identity-teamspeak-result", "profile_id"); + create_standard_timeout("delete-profile", "delete-profile-result", "profile_id"); + create_standard_timeout("set-profile-name", "set-profile-name-result", "profile_id"); + create_standard_timeout("set-default-name", "set-default-name-result", "profile_id"); + create_standard_timeout("query-profile-validity", "query-profile-validity-result", "profile_id"); + create_standard_timeout("set-identity-name-name", "set-identity-name-name-result", "profile_id"); + create_standard_timeout("generate-identity-teamspeak", "generate-identity-teamspeak-result", "profile_id"); + } + /* some view semantics */ + { + let selected_profile; + event_registry.on("delete-profile-result", event => { + if (event.status !== "success") + return; + if (event.profile_id !== selected_profile) + return; + /* the selected profile has been deleted, so we need to select another one */ + event_registry.fire("select-profile", { profile_id: "default" }); + }); + /* reselect the default profile or the new default profile */ + event_registry.on("set-default-profile-result", event => { + if (event.status !== "success") + return; + if (selected_profile === "default") + event_registry.fire("select-profile", { profile_id: event.new_profile_id }); + else if (selected_profile === event.old_profile_id) + event_registry.fire("select-profile", { profile_id: "default" }); + }); + event_registry.on("select-profile", event => { + selected_profile = event.profile_id; + event_registry.fire("query-profile", { profile_id: event.profile_id }); + }); + event_registry.on("reload-profile", event => { + event_registry.fire("query-profile-list"); + event_registry.fire("select-profile", event.profile_id || selected_profile); + }); + } + event_registry.fire("query-profile-list"); + event_registry.fire("select-profile", { profile_id: "default" }); + event_registry.fire("select-identity-type", { profile_id: "default", identity_type: undefined }); + } + modal_settings.initialize_identity_profiles_view = initialize_identity_profiles_view; + function initialize_audio_microphone_controller(event_registry) { + /* level meters */ + { + const level_meters = {}; + const level_info = {}; + let level_update_task; + const destroy_meters = () => { + Object.keys(level_meters).forEach(e => { + const meter = level_meters[e]; + delete level_meters[e]; + meter.then(e => e.destory()); + }); + Object.keys(level_info).forEach(e => delete level_info[e]); + }; + const update_level_meter = () => { + destroy_meters(); + for (const device of audio.recorder.devices()) { + let promise = audio.recorder.create_levelmeter(device).then(meter => { + meter.set_observer(level => { + if (level_meters[device.unique_id] !== promise) + return; /* old level meter */ + level_info[device.unique_id] = { + device_id: device.unique_id, + status: "success", + level: level + }; + }); + return Promise.resolve(meter); + }).catch(error => { + if (level_meters[device.unique_id] !== promise) + return; /* old level meter */ + level_info[device.unique_id] = { + device_id: device.unique_id, + status: "error", + error: error + }; + log.warn(LogCategory.AUDIO, _translations.lghaKO0H || (_translations.lghaKO0H = tr("Failed to initialize a level meter for device %s (%s): %o")), device.unique_id, device.driver + ":" + device.name, error); + return Promise.reject(error); + }); + level_meters[device.unique_id] = promise; + } + }; + level_update_task = setInterval(() => { + event_registry.fire("update-device-level", { + devices: Object.keys(level_info).map(e => level_info[e]) + }); + }, 50); + event_registry.on("query-device-result", event => { + if (event.status !== "success") + return; + update_level_meter(); + }); + event_registry.on("deinitialize", event => { + destroy_meters(); + clearInterval(level_update_task); + }); + } + /* device list */ + { + event_registry.on("query-devices", event => { + Promise.resolve().then(() => { + return audio.recorder.device_refresh_available() && event.refresh_list ? audio.recorder.refresh_devices() : Promise.resolve(); + }).catch(error => { + log.warn(LogCategory.AUDIO, _translations.JjcOzbzw || (_translations.JjcOzbzw = tr("Failed to refresh device list: %o")), error); + return Promise.resolve(); + }).then(() => { + const devices = audio.recorder.devices(); + event_registry.fire_async("query-device-result", { + status: "success", + active_device: default_recorder.current_device() ? default_recorder.current_device().unique_id : "none", + devices: devices.map(e => { return { id: e.unique_id, name: e.name, driver: e.driver }; }) + }); + }); + }); + event_registry.on("set-device", event => { + const device = audio.recorder.devices().find(e => e.unique_id === event.device_id); + if (!device && event.device_id !== "none") { + event_registry.fire_async("set-device-result", { status: "error", error: _translations.z4UK75Eu || (_translations.z4UK75Eu = tr("Invalid device id")), device_id: event.device_id }); + return; + } + default_recorder.set_device(device).then(() => { + console.debug(_translations.am_OVcEM || (_translations.am_OVcEM = tr("Changed default microphone device"))); + event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); + }).catch((error) => { + log.warn(LogCategory.AUDIO, _translations.pj9HuZR1 || (_translations.pj9HuZR1 = tr("Failed to change microphone to device %s: %o")), device ? device.unique_id : "none", error); + event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); + }); + }); + } + /* settings */ + { + event_registry.on("query-settings", event => { + event_registry.fire_async("query-settings-result", { + status: "success", + info: { + volume: default_recorder.get_volume(), + vad_type: default_recorder.get_vad_type(), + vad_ppt: { + key: default_recorder.get_vad_ppt_key(), + release_delay: Math.abs(default_recorder.get_vad_ppt_delay()), + release_delay_active: default_recorder.get_vad_ppt_delay() >= 0 + }, + vad_threshold: { + threshold: default_recorder.get_vad_threshold() + } + } + }); + }); + event_registry.on("set-setting", event => { + const ensure_type = (type) => { + if (typeof event.value !== type) { + event_registry.fire_async("set-setting-result", { status: "error", error: (_translations.YwWBf7Xh || (_translations.YwWBf7Xh = tr("Invalid value type for key"))) + " (expected: " + type + ", received: " + typeof event.value + ")", setting: event.setting }); + return false; + } + return true; + }; + switch (event.setting) { + case "volume": + if (!ensure_type("number")) + return; + default_recorder.set_volume(event.value); + break; + case "threshold-threshold": + if (!ensure_type("number")) + return; + default_recorder.set_vad_threshold(event.value); + break; + case "vad-type": + if (!ensure_type("string")) + return; + if (!default_recorder.set_vad_type(event.value)) { + event_registry.fire_async("set-setting-result", { status: "error", error: _translations.rqWJD5R2 || (_translations.rqWJD5R2 = tr("Unknown VAD type")), setting: event.setting }); + return; + } + break; + case "ppt-key": + if (!ensure_type("object")) + return; + default_recorder.set_vad_ppt_key(event.value); + break; + case "ppt-release-delay": + if (!ensure_type("number")) + return; + const sign = default_recorder.get_vad_ppt_delay() >= 0 ? 1 : -1; + default_recorder.set_vad_ppt_delay(sign * event.value); + break; + case "ppt-release-delay-active": + if (!ensure_type("boolean")) + return; + default_recorder.set_vad_ppt_delay(Math.abs(default_recorder.get_vad_ppt_delay()) * (event.value ? 1 : -1)); + break; + default: + event_registry.fire_async("set-setting-result", { status: "error", error: _translations.w8SWxS4C || (_translations.w8SWxS4C = tr("Invalid setting key")), setting: event.setting }); + return; + } + event_registry.fire_async("set-setting-result", { status: "success", setting: event.setting, value: event.value }); + }); + } + audio.player.on_ready(() => event_registry.fire_async("audio-initialized", {})); + } + modal_settings.initialize_audio_microphone_controller = initialize_audio_microphone_controller; + function initialize_audio_microphone_view(container, event_registry) { + /* device list */ + { + /* actual list */ + { + const container_devices = container.find(".container-devices"); + const volume_bar_tags = {}; + let pending_changes = 0; + let default_device_id; + const build_device = (device, selected) => { + let tag_volume, tag_volume_error; + const tag = $.spawn("div").attr("device-id", device ? device.id : "none").addClass("device").toggleClass("selected", selected).append($.spawn("div").addClass("container-selected").append($.spawn("div").addClass("icon_em client-apply"), $.spawn("div").addClass("icon-loading").append($.spawn("img").attr("src", "img/icon_settings_loading.svg"))), $.spawn("div").addClass("container-name").append($.spawn("div").addClass("device-driver").text(device ? (device.driver || "Unknown driver") : "No device"), $.spawn("div").addClass("device-name").text(device ? (device.name || "Unknown name") : "No device")), $.spawn("div").addClass("container-activity").append($.spawn("div").addClass("container-activity-bar").append(tag_volume = $.spawn("div").addClass("bar-hider"), tag_volume_error = $.spawn("div").addClass("bar-error")))); + tag_volume.css('width', '100%'); /* initially hide the bar */ + if (device) + volume_bar_tags[device.id] = { volume: tag_volume, error: tag_volume_error }; + tag.on('click', event => { + if (tag.hasClass("selected") || pending_changes > 0) + return; + event_registry.fire("set-device", { device_id: device ? device.id : "none" }); + }); + return tag; + }; + event_registry.on("set-device", event => { + pending_changes++; + const default_device = container_devices.find(".selected"); + default_device_id = default_device.attr("device-id"); + default_device.removeClass("selected"); + const new_device = container_devices.find(".device[device-id='" + event.device_id + "']"); + new_device.addClass("loading"); + }); + event_registry.on("set-device-result", event => { + pending_changes--; + container_devices.find(".loading").removeClass("loading"); + if (event.status !== "success") { + createErrorModal(_translations.S9WvyD7Z || (_translations.S9WvyD7Z = tr("Failed to change microphone")), MessageHelper.formatMessage(_translations.hceWH1fk || (_translations.hceWH1fk = tr("Failed to change the microphone to the target microphone{:br:}{}")), event.status === "timeout" ? _translations.pqrGxJyU || (_translations.pqrGxJyU = tr("Timeout")) : event.error || (_translations.cin0D3wS || (_translations.cin0D3wS = tr("Unknown error"))))).open(); + } + else { + default_device_id = event.device_id; + } + container_devices.find(".device[device-id='" + default_device_id + "']").addClass("selected"); + }); + event_registry.on('query-devices', event => { + Object.keys(volume_bar_tags).forEach(e => delete volume_bar_tags[e]); + container_devices.find(".device").remove(); + container_devices.find(".overlay").hide(); + container_devices.find(".overlay.overlay-loading").show(); + }); + event_registry.on("query-device-result", event => { + container_devices.find(".device").remove(); + container_devices.find(".overlay").hide(); + if (event.status !== "success") { + const container_text = container_devices.find(".overlay.overlay-error").show().find(".error-text"); + container_text.text(event.status === "timeout" ? _translations.ii2WV_Om || (_translations.ii2WV_Om = tr("Timeout while loading")) : event.error || (_translations.zUPIX6BD || (_translations.zUPIX6BD = tr("An unknown error happened")))); + return; + } + build_device(undefined, event.active_device === "none").appendTo(container_devices); + for (const device of event.devices) + build_device(device, event.active_device === device.id).appendTo(container_devices); + }); + event_registry.on("update-device-level", event => { + for (const device of event.devices) { + const tags = volume_bar_tags[device.device_id]; + if (!tags) + continue; + let level = typeof device.level === "number" ? device.level : 100; + if (level > 100) + level = 100; + else if (level < 0) + level = 0; + tags.error.attr('title', device.error || null).text(device.error || null); + tags.volume.css('width', (100 - level) + '%'); + } + }); + } + /* device list update button */ + { + const button_update = container.find(".button-update"); + event_registry.on(["query-devices", "set-device"], event => button_update.prop("disabled", true)); + event_registry.on(["query-device-result", "set-device-result"], event => button_update.prop("disabled", false)); + button_update.on("click", event => event_registry.fire("query-devices", { refresh_list: true })); + } + } + /* settings */ + { + /* TODO: Query settings error handling */ + /* volume */ + { + const container_volume = container.find(".container-volume"); + const slider_tag = container_volume.find(".container-slider"); + let triggered_events = 0; + let last_value = -1; + const slider = sliderfy(slider_tag, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: 0 + }); + slider_tag.on('change', event => { + const value = parseInt(slider_tag.attr("value")); + if (last_value === value) + return; + triggered_events++; + event_registry.fire("set-setting", { setting: "volume", value: value }); + }); + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + last_value = event.info.volume; + slider.value(event.info.volume); + }); + event_registry.on("set-setting-result", event => { + if (event.setting !== "volume") + return; + if (triggered_events > 0) { + triggered_events--; + return; + } + if (event.status !== "success") + return; + last_value = event.value; + slider.value(event.value); + }); + } + /* vad type */ + { + const container_select = container.find(".container-select-vad"); + let last_value; + container_select.find("input").on('change', event => { + if (!event.target.checked) + return; + const mode = event.target.value; + if (mode === last_value) + return; + event_registry.fire("set-setting", { setting: "vad-type", value: mode }); + }); + const select_vad_type = type => { + let elements = container_select.find('input[value="' + type + '"]'); + if (elements.length < 1) + elements = container_select.find('input[value]'); + elements.first().trigger('click'); + }; + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + last_value = event.info.vad_type; + select_vad_type(event.info.vad_type); + }); + event_registry.on("set-setting-result", event => { + if (event.setting !== "vad-type") + return; + if (event.status !== "success") { + createErrorModal(_translations.cIzsOEm1 || (_translations.cIzsOEm1 = tr("Failed to change setting")), MessageHelper.formatMessage(_translations.CPz2Dcn4 || (_translations.CPz2Dcn4 = tr("Failed to change vad type{:br:}{}")), event.status === "timeout" ? _translations.TqXWWyKK || (_translations.TqXWWyKK = tr("Timeout")) : event.error || (_translations.IAvTTiJt || (_translations.IAvTTiJt = tr("Unknown error"))))).open(); + } + else { + last_value = event.value; + } + select_vad_type(last_value); + }); + } + /* Sensitivity */ + { + const container_sensitivity = container.find(".container-sensitivity"); + const container_bar = container_sensitivity.find(".container-activity-bar"); + const bar_hider = container_bar.find(".bar-hider"); + let last_value; + let triggered_events = 0; + let enabled; + const slider = sliderfy(container_bar, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: 0 + }); + const set_enabled = value => { + if (enabled === value) + return; + enabled = value; + container_sensitivity.toggleClass("disabled", !value); + }; + container_bar.on('change', event => { + const value = parseInt(container_bar.attr("value")); + if (last_value === value) + return; + triggered_events++; + event_registry.fire("set-setting", { setting: "threshold-threshold", value: value }); + }); + event_registry.on("query-settings", event => set_enabled(false)); + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + last_value = event.info.vad_threshold.threshold; + slider.value(event.info.vad_threshold.threshold); + set_enabled(event.info.vad_type === "threshold"); + }); + event_registry.on("set-setting-result", event => { + if (event.setting === "threshold-threshold") { + if (event.status !== "success") + return; + if (triggered_events > 0) { + triggered_events--; + return; + } + last_value = event.value; + slider.value(event.value); + } + else if (event.setting === "vad-type") { + if (event.status !== "success") + return; + set_enabled(event.value === "threshold"); + } + }); + let selected_device; + event_registry.on("query-device-result", event => { + if (event.status !== "success") + return; + selected_device = event.active_device; + }); + event_registry.on("set-device-result", event => { + if (event.status !== "success") + return; + selected_device = event.device_id; + }); + bar_hider.css("width", "100%"); + event_registry.on("update-device-level", event => { + if (!enabled) + return; + const data = event.devices.find(e => e.device_id === selected_device); + let level = data && typeof data.level === "number" ? data.level : 0; + if (level > 100) + level = 100; + else if (level < 0) + level = 0; + bar_hider.css("width", (100 - level) + "%"); + }); + set_enabled(false); + } + /* ppt settings */ + { + /* PPT Key */ + { + const button_key = container.find(".container-ppt button"); + event_registry.on("query-settings", event => button_key.prop("disabled", true).text(_translations.NZPKedF2 || (_translations.NZPKedF2 = tr("loading")))); + let last_value; + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + button_key.prop('disabled', event.info.vad_type !== "push_to_talk"); + button_key.text(last_value = ppt.key_description(event.info.vad_ppt.key)); + }); + event_registry.on("set-setting", event => { + if (event.setting !== "ppt-key") + return; + button_key.prop("enabled", false); + button_key.text(_translations.Fj0SrrS2 || (_translations.Fj0SrrS2 = tr("applying"))); + }); + event_registry.on("set-setting-result", event => { + if (event.setting === "vad-type") { + if (event.status !== "success") + return; + button_key.prop('disabled', event.value !== "push_to_talk"); + } + else if (event.setting === "ppt-key") { + if (event.status !== "success") { + createErrorModal(_translations.eE42rV0W || (_translations.eE42rV0W = tr("Failed to change PPT key")), MessageHelper.formatMessage(_translations.Y9zzAYFy || (_translations.Y9zzAYFy = tr("Failed to change PPT key:{:br:}{}")), event.status === "timeout" ? _translations.EdjnX01y || (_translations.EdjnX01y = tr("Timeout")) : event.error || (_translations.LJiFWJSt || (_translations.LJiFWJSt = tr("Unknown error"))))).open(); + } + else { + last_value = ppt.key_description(event.value); + } + button_key.text(last_value); + } + }); + button_key.on('click', event => { + Modals.spawnKeySelect(key => { + if (!key) + return; + event_registry.fire("set-setting", { setting: "ppt-key", value: key }); + }); + }); + } + /* delay */ + { + const container_delay = container.find(".container-ppt-delay"); + /* toggle button */ + { + const input_enabled = container_delay.find("input.delay-enabled"); + const update_enabled_state = () => { + const value = !loading && !applying && ppt_selected; + input_enabled.prop("disabled", !value).parent().toggleClass("disabled", !value); + }; + let last_state; + let loading = true, applying = false, ppt_selected = false; + event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + loading = false; + ppt_selected = event.info.vad_type === "push_to_talk"; + update_enabled_state(); + input_enabled.prop("checked", last_state = event.info.vad_ppt.release_delay_active); + }); + event_registry.on("set-setting", event => { + if (event.setting !== "ppt-release-delay-active") + return; + applying = true; + update_enabled_state(); + }); + event_registry.on("set-setting-result", event => { + if (event.setting === "vad-type") { + if (event.status !== "success") + return; + ppt_selected = event.value === "push_to_talk"; + update_enabled_state(); + } + else if (event.setting === "ppt-release-delay-active") { + applying = false; + update_enabled_state(); + if (event.status !== "success") { + createErrorModal(_translations.jwSWNYxO || (_translations.jwSWNYxO = tr("Failed to change PPT delay state")), MessageHelper.formatMessage(_translations.UOWQDD_7 || (_translations.UOWQDD_7 = tr("Failed to change PPT delay state:{:br:}{}")), event.status === "timeout" ? _translations.izNO4C23 || (_translations.izNO4C23 = tr("Timeout")) : event.error || (_translations.oRJMsyYz || (_translations.oRJMsyYz = tr("Unknown error"))))).open(); + } + else { + last_state = event.value; + } + input_enabled.prop("checked", last_state); + } + }); + input_enabled.on('change', event => { + event_registry.fire("set-setting", { setting: "ppt-release-delay-active", value: input_enabled.prop("checked") }); + }); + } + /* delay input */ + { + const input_time = container_delay.find("input.delay-time"); + const update_enabled_state = () => { + const value = !loading && !applying && ppt_selected && delay_active; + input_time.prop("disabled", !value).parent().toggleClass("disabled", !value); + }; + let last_state; + let loading = true, applying = false, ppt_selected = false, delay_active = false; + event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); + event_registry.on("query-settings-result", event => { + if (event.status !== "success") + return; + loading = false; + ppt_selected = event.info.vad_type === "push_to_talk"; + delay_active = event.info.vad_ppt.release_delay_active; + update_enabled_state(); + input_time.val(last_state = event.info.vad_ppt.release_delay); + }); + event_registry.on("set-setting", event => { + if (event.setting !== "ppt-release-delay") + return; + applying = true; + update_enabled_state(); + }); + event_registry.on("set-setting-result", event => { + if (event.setting === "vad-type") { + if (event.status !== "success") + return; + ppt_selected = event.value === "push_to_talk"; + update_enabled_state(); + } + else if (event.setting === "ppt-release-delay-active") { + if (event.status !== "success") + return; + delay_active = event.value; + update_enabled_state(); + } + else if (event.setting === "ppt-release-delay") { + applying = false; + update_enabled_state(); + if (event.status !== "success") { + createErrorModal(_translations.FkXnr4XQ || (_translations.FkXnr4XQ = tr("Failed to change PPT delay")), MessageHelper.formatMessage(_translations.G2MNfANr || (_translations.G2MNfANr = tr("Failed to change PPT delay:{:br:}{}")), event.status === "timeout" ? _translations.LUwfp1q6 || (_translations.LUwfp1q6 = tr("Timeout")) : event.error || (_translations.Eckk8zqH || (_translations.Eckk8zqH = tr("Unknown error"))))).open(); + } + else { + last_state = event.value; + } + input_time.val(last_state); + } + }); + input_time.on('change', event => { + event_registry.fire("set-setting", { setting: "ppt-release-delay", value: parseInt(input_time.val()) }); + }); + } + } + } + } + /* timeouts */ + { + /* device query */ + { + let timeout; + event_registry.on('query-devices', event => { + clearTimeout(timeout); + timeout = setTimeout(() => { + event_registry.fire("query-device-result", { status: "timeout" }); + }, 5000); + }); + event_registry.on("query-device-result", event => clearTimeout(timeout)); + } + /* device set */ + { + let timeouts = {}; + event_registry.on('set-device', event => { + clearTimeout(timeouts[event.device_id]); + timeouts[event.device_id] = setTimeout(() => { + event_registry.fire("set-device-result", { status: "timeout", device_id: event.device_id }); + }, 5000); + }); + event_registry.on("set-device-result", event => clearTimeout(timeouts[event.device_id])); + } + /* settings query */ + { + let timeout; + event_registry.on('query-settings', event => { + clearTimeout(timeout); + timeout = setTimeout(() => { + event_registry.fire("query-settings-result", { status: "timeout" }); + }, 5000); + }); + event_registry.on("query-settings-result", event => clearTimeout(timeout)); + } + /* settings change */ + { + let timeouts = {}; + event_registry.on('set-setting', event => { + clearTimeout(timeouts[event.setting]); + timeouts[event.setting] = setTimeout(() => { + event_registry.fire("set-setting-result", { status: "timeout", setting: event.setting }); + }, 5000); + }); + event_registry.on("set-setting-result", event => clearTimeout(timeouts[event.setting])); + } + } + event_registry.on("audio-initialized", () => { + event_registry.fire("query-settings"); + event_registry.fire("query-devices", { refresh_list: false }); + }); + } + modal_settings.initialize_audio_microphone_view = initialize_audio_microphone_view; + })(modal_settings = Modals.modal_settings || (Modals.modal_settings = {})); + function settings_identity_forum(container, modal, update_profiles) { + const containers_connected = container.find(".show-connected"); + const containers_disconnected = container.find(".show-disconnected"); + const update_state = () => { + const logged_in = forum.logged_in(); + containers_connected.toggle(logged_in); + containers_disconnected.toggle(!logged_in); + if (logged_in) { + container.find(".forum-username").text(forum.data().name()); + container.find(".forum-premium").text(forum.data().is_premium() ? _translations.Sz0yBwv1 || (_translations.Sz0yBwv1 = tr("Yes")) : _translations.nM_Detuu || (_translations.nM_Detuu = tr("No"))); + } + }; + /* login */ + { + const button_login = container.find(".button-login"); + const input_username = container.find(".input-username"); + const input_password = container.find(".input-password"); + const container_error = container.find(".container-login .container-error"); + const container_captcha_g = container.find(".g-recaptcha"); + let captcha = false; + const update_button_state = () => { + let enabled = true; + enabled = enabled && !!input_password.val(); + enabled = enabled && !!input_username.val(); + enabled = enabled && (typeof (captcha) === "boolean" ? !captcha : !!captcha); + button_login.prop("disabled", !enabled); + }; + /* username */ + input_username.on('change keyup', update_button_state); + /* password */ + input_password.on('change keyup', update_button_state); + button_login.on('click', event => { + input_username.prop("disabled", true); + input_password.prop("disabled", true); + button_login.prop("disabled", true); + container_error.removeClass("shown"); + forum.login(input_username.val(), input_password.val(), typeof (captcha) === "string" ? captcha : undefined).then(state => { + captcha = false; + console.debug(_translations.tbSVCOB3 || (_translations.tbSVCOB3 = tr("Forum login result: %o")), state); + if (state.status === "success") { + update_state(); + update_profiles(); + return; + } + setTimeout(() => { + if (!!state.error_message) /* clear password if we have an error */ + input_password.val(""); + input_password.focus(); + update_button_state(); + }, 0); + if (state.status === "captcha") { + //TODO Works currently only with localhost! + button_login.hide(); + container_error.text(state.error_message || (_translations.qJrtnF6R || (_translations.qJrtnF6R = tr("Captcha required")))).addClass("shown"); + captcha = ""; + console.log(_translations.fMFyBIJx || (_translations.fMFyBIJx = tr("Showing captcha for site-key: %o")), state.captcha.data); + forum.gcaptcha.spawn(container_captcha_g, state.captcha.data, token => { + captcha = token; + console.debug(_translations.m7tGEkDd || (_translations.m7tGEkDd = tr("Got captcha token: %o")), token); + container_captcha_g.hide(); + button_login.show(); + update_button_state(); + }).catch(error => { + console.error(_translations.HRWXI7ss || (_translations.HRWXI7ss = tr("Failed to initialize forum captcha: %o")), error); + container_error.text("Failed to initialize GReCaptcha! No authentication possible.").addClass("shown"); + container_captcha_g.hide(); + button_login.hide(); + }); + container_captcha_g.show(); + } + else { + container_error.text(state.error_message || (_translations._DgU0HsU || (_translations._DgU0HsU = tr("Unknown error")))).addClass("shown"); + } + }).catch(error => { + console.error(_translations.r6v8MZ09 || (_translations.r6v8MZ09 = tr("Failed to login within the forum. Error: %o")), error); + createErrorModal(_translations.JslYmH78 || (_translations.JslYmH78 = tr("Forum login failed.")), _translations.l0U3SilI || (_translations.l0U3SilI = tr("Forum login failed. Lookup the console for more information"))).open(); + }).then(() => { + input_username.prop("disabled", false); + input_password.prop("disabled", false); + update_button_state(); + }); + }); + update_button_state(); + } + /* logout */ + { + container.find(".button-logout").on('click', event => { + forum.logout().catch(error => { + console.error(_translations.SCagvySD || (_translations.SCagvySD = tr("Failed to logout from forum: %o")), error); + createErrorModal(_translations.N4jqwUqM || (_translations.N4jqwUqM = tr("Forum logout failed")), MessageHelper.formatMessage(_translations.jeMb4g5m || (_translations.jeMb4g5m = tr("Failed to logout from forum account.{:br:}Error: {}")), error)).open(); + }).then(() => { + if (modal.shown) + update_state(); + update_profiles(); + }); + }); + } + update_state(); + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["92ac35d52ad1f42fa5193ac1e4f38303a97206528f82d8c2341c7735d2f8759b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["92ac35d52ad1f42fa5193ac1e4f38303a97206528f82d8c2341c7735d2f8759b"] = "92ac35d52ad1f42fa5193ac1e4f38303a97206528f82d8c2341c7735d2f8759b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "jClfZ0h9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (174,21)" }, { name: "dKMx7HBk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (243,21)" }, { name: "TaMwKYk3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (244,23)" }, { name: "jj794ggs", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (249,21)" }, { name: "PCTwVWW5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (250,23)" }, { name: "cJf25JfU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (255,21)" }, { name: "aVE1R8Ij", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (256,23)" }, { name: "FtxS8aPv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (261,21)" }, { name: "_iRssxDc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (262,23)" }, { name: "fZs9KsQ7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (306,43)" }, { name: "K_txnzdc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (316,43)" }, { name: "cqChs9Oa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (333,76)" }, { name: "CsmcENyr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (349,76)" }, { name: "LfL6QN9W", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (364,156)" }, { name: "_9iGfBfM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (392,51)" }, { name: "uxwU2nG6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (395,38)" }, { name: "lpipbMzX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (395,94)" }, { name: "Ma1zisV8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (437,63)" }, { name: "sDgA4pks", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (445,64)" }, { name: "TA3MTtDq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (449,46)" }, { name: "McoQVATK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (451,69)" }, { name: "sRJ6ujQf", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (452,67)" }, { name: "cuxjhYl0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (478,61)" }, { name: "lMroAnEz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (479,59)" }, { name: "TEivpBwN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (480,55)" }, { name: "CfdgV9De", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (481,57)" }, { name: "OZoex2JI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (511,67)" }, { name: "jwxGIkmy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (522,68)" }, { name: "jFQD0Gmp", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (527,46)" }, { name: "ozlD0jd5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (529,69)" }, { name: "vm8uLmSc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (530,71)" }, { name: "hlQqRdbg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (637,55)" }, { name: "l6HhHM6Z", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (639,55)" }, { name: "fjEVRGtV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (643,66)" }, { name: "QdbjApBs", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (644,51)" }, { name: "TFLC6YL4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (724,109)" }, { name: "mHWmn0e_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (726,56)" }, { name: "_P5v1n6D", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (727,66)" }, { name: "E92mYEjR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (781,37)" }, { name: "EEwz6h5e", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (781,68)" }, { name: "LQLYXfno", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (783,51)" }, { name: "bOiC47ix", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (786,38)" }, { name: "EcDuYmlv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (786,94)" }, { name: "u2qYFkJb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (849,37)" }, { name: "aab8fpL2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (849,67)" }, { name: "VRhtN9SF", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (851,51)" }, { name: "zu2R85Jy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (854,38)" }, { name: "iG8KyFbG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (854,91)" }, { name: "mM6I4PAT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (878,75)" }, { name: "KT1iYZJ7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanList.ts (900,79)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/// +var Modals; +(function (Modals) { + function openBanList(client) { + let modal; + let _callback_bans; + let _callback_triggers; + const single_ban_handler = { + command: "notifybanlist", + function: command => { + const json = command.arguments; + let bans = []; + for (const entry of json) { + bans.push({ + server_id: parseInt(entry["sid"]), + banid: parseInt(entry["banid"]), + ip: entry["ip"], + name: entry["name"], + unique_id: entry["uid"], + hardware_id: entry["hwid"], + timestamp_created: (parseInt(entry["created"]) * 1000), + timestamp_expire: (parseInt(entry["duration"]) > 0 ? parseInt(entry["created"]) * 1000 + parseInt(entry["duration"]) * 1000 : 0), + invoker_name: entry["invokername"], + invoker_database_id: parseInt(entry["invokercldbid"]), + invoker_unique_id: entry["invokeruid"], + reason: entry["reason"], + enforcements: parseInt(entry["enforcements"]), + flag_own: entry["invokeruid"] == client.getClient().properties.client_unique_identifier + }); + } + _callback_bans(bans); + return false; /* do not remove me */ + } + }; + const single_trigger_handler = { + command: "notifybantriggerlist", + function: command => { + //TODO: Test the server id in the response? + const json = command.arguments; + let triggers = []; + for (const entry of json) { + triggers.push({ + unique_id: entry["client_unique_identifier"], + client_nickname: entry["client_nickname"], + hardware_id: entry["client_hardware_identifier"], + connection_ip: entry["connection_client_ip"], + timestamp: parseInt(entry["timestamp"]) + }); + } + _callback_triggers(triggers); + return false; /* do not remove me */ + } + }; + const controller = { + request_list(_callback) { + _callback_bans = _callback; + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 2500); + const cleanup = () => { + clearTimeout(timeout); + _callback_bans = undefined; + }; + Promise.all([ + client.serverConnection.send_command("banlist", { sid: 0 }, { process_result: false }).catch(error => { + //TODO: May lookup for permissions + }), + client.serverConnection.send_command("banlist").catch((error) => __awaiter(this, void 0, void 0, function* () { + if (error instanceof CommandResult) + if (error.id === ErrorID.EMPTY_RESULT) + return; + throw error; + })) + ]).then(() => { + if (_callback_bans) + resolve(); + cleanup(); + }).catch(error => { + if (_callback_bans) + reject(error); + cleanup(); + }); + }); + }, + request_trigger_list(ban, _callback) { + _callback_triggers = _callback; + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 2500); + const cleanup = () => { + clearTimeout(timeout); + _callback_triggers = undefined; + }; + const data = { banid: ban.ban_id }; + if (typeof ban.server_id !== "undefined") + data["sid"] = ban.server_id; + client.serverConnection.send_command("bantriggerlist", data).catch((error) => __awaiter(this, void 0, void 0, function* () { + if (error instanceof CommandResult) + if (error.id === ErrorID.EMPTY_RESULT) + return; + throw error; + })).then(() => { + if (_callback_triggers) + resolve(); + cleanup(); + }).catch(error => { + if (_callback_triggers) + reject(error); + cleanup(); + }); + }); + }, + max_bantime() { + return __awaiter(this, void 0, void 0, function* () { + const value = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value || 0; + return value == -2 ? 0 : value; + }); + }, + permission_add() { + return __awaiter(this, void 0, void 0, function* () { + return [ + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE).granted(1), + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE_GLOBAL).granted(1) + ]; + }); + }, + permission_edit() { + return __awaiter(this, void 0, void 0, function* () { + return [ + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT).granted(1), + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT_GLOBAL).granted(1) && false + ]; + }); + }, + add_ban(entry) { + const data = {}; + if (entry.ip) + data["ip"] = entry.ip; + if (entry.name) + data["name"] = entry.name; + if (entry.unique_id) + data["uid"] = entry.unique_id; + if (entry.hardware_id) + data["hwid"] = entry.hardware_id; + if (entry.reason) + data["banreason"] = entry.reason; + if (entry.timestamp_expire) + data["time"] = Math.floor((entry.timestamp_expire - entry.timestamp_created) / 1000); + if (typeof (entry.server_id) === "number") + data["sid"] = entry.server_id; + return client.serverConnection.send_command("banadd", data).then(e => { + if (!e.success) + throw e; + }); + }, + edit_ban(data) { + return client.serverConnection.send_command("banedit", data).then(e => { + if (!e.success) + throw e; + }); + }, + delete_ban(entry_id, server_id) { + const data = { + banid: entry_id + }; + if (typeof (server_id) === "number") + data["sid"] = server_id; + return client.serverConnection.send_command("bandel", data).then(e => { + if (!e.success) + throw e; + }); + } + }; + modal = createModal({ + header: _translations.jClfZ0h9 || (_translations.jClfZ0h9 = tr("Server Banlist")), + body: () => generate_dom(controller), + footer: null, + width: '60em' + }); + client.serverConnection.command_handler_boss().register_single_handler(single_ban_handler); + client.serverConnection.command_handler_boss().register_single_handler(single_trigger_handler); + modal.close_listener.push(() => { + client.serverConnection.command_handler_boss().remove_single_handler(single_ban_handler); + client.serverConnection.command_handler_boss().remove_single_handler(single_trigger_handler); + }); + //TODO: Test without dividerfy! + modal.htmlTag.dividerfy(); + modal.htmlTag.find(".modal-body").addClass("modal-ban-list"); + modal.open(); + } + Modals.openBanList = openBanList; + //Note: This object must be sorted (from shortest to longest)! + Modals.duration_data = { + "sec": { + "text": _translations.dKMx7HBk || (_translations.dKMx7HBk = tr("Seconds")), + "1-text": _translations.TaMwKYk3 || (_translations.TaMwKYk3 = tr("Second")), + scale: 1 + }, + "min": { + "text": _translations.jj794ggs || (_translations.jj794ggs = tr("Minutes")), + "1-text": _translations.PCTwVWW5 || (_translations.PCTwVWW5 = tr("Minute")), + scale: 60 + }, + "hours": { + "text": _translations.cJf25JfU || (_translations.cJf25JfU = tr("Hours")), + "1-text": _translations.aVE1R8Ij || (_translations.aVE1R8Ij = tr("Hour")), + scale: 3600 + }, + "days": { + "text": _translations.FtxS8aPv || (_translations.FtxS8aPv = tr("Days")), + "1-text": _translations._iRssxDc || (_translations._iRssxDc = tr("Day")), + scale: 86400 + }, + }; + function generate_dom(controller) { + const template = $("#tmpl_ban_list").renderTag(); + let callback_ban_filter = []; + let callback_trigger_filter = []; + let selected_ban; + let update_edit_window; + let update_ban_filter; + let update_trigger_filter; + const container_ban = template.find(".container-banlist"); + const container_ban_entries = container_ban.find(".container-list .body"); + const container_ban_entries_empty = container_ban.find(".container-list .container-empty"); + const container_ban_entries_error = container_ban.find(".container-list .container-error"); + const container_trigger = template.find(".container-triggerlist").hide(); + const container_trigger_entries = container_trigger.find(".container-list .body"); + const container_trigger_entries_empty = container_trigger.find(".container-list .container-empty"); + const container_trigger_entries_error = container_trigger.find(".container-list .container-error"); + const button_apply = template.find(".button-apply"); + let button_apply_state = [false, false]; /* first index is add; second index is edit */ + let update_category_inputs = [undefined, undefined]; + let button_apply_state_index = 1; + const category_add = template.find(".left .head .category-add"); + const category_edit = template.find(".left .head .category-edit"); + const container_add = template.find(".left .container-add"); + const container_add_no_permissions = template.find(".left .container-add .container-no-permissions"); + const container_edit = template.find(".left .container-edit"); + const seperator_top = template.find(".container-seperator .top"); + /* [local; global] */ + let permission_edit = [false, false], permission_add = [false, false]; + container_add_no_permissions.hide(); + controller.permission_add().then(result => permission_add = result).catch(error => { + log.error(LogCategory.CLIENT, _translations.fZs9KsQ7 || (_translations.fZs9KsQ7 = tr("Failed to query ban add permissions: %o")), error); + }).then(() => { + if (permission_add[0] !== permission_add[1]) { + const input_global = container_add.find(".group-global input"); + input_global.prop("checked", permission_add[1]).prop("disabled", true).firstParent(".checkbox").addClass("disabled"); + } + else if (!permission_add[0]) + container_add_no_permissions.show(); + }); + controller.permission_edit().then(result => permission_edit = result).catch(error => { + log.error(LogCategory.CLIENT, _translations.K_txnzdc || (_translations.K_txnzdc = tr("Failed to query ban edit permissions: %o")), error); + }).then(() => { + if (selected_ban) + update_edit_window(false); + }); + /* category switch */ + { + category_add.on('click', event => { + container_add.removeClass("hidden"); + category_add.addClass("selected"); + container_edit.addClass("hidden"); + category_edit.removeClass("selected"); + seperator_top.css({ opacity: 1 }); + button_apply_state_index = 0; + button_apply.prop("disabled", !button_apply_state[0]).text(_translations.cqChs9Oa || (_translations.cqChs9Oa = tr("Add ban"))); + update_category_inputs[button_apply_state_index](); + }); + category_edit.on('click', event => { + if (!selected_ban) + return; + container_add.addClass("hidden"); + category_add.removeClass("selected"); + container_edit.removeClass("hidden"); + category_edit.addClass("selected"); + seperator_top.css({ opacity: 0 }); + button_apply_state_index = 1; + button_apply.prop("disabled", !button_apply_state[1]).text(_translations.CsmcENyr || (_translations.CsmcENyr = tr("Save ban"))); + update_category_inputs[button_apply_state_index](); + }); + } + const build_ban_entry = (entry, selected) => { + let button_delete; + const tag = $.spawn("div").addClass("entry" + (entry.server_id > 0 ? "" : " global") + (selected ? " selected" : "")).append($.spawn("div").addClass("column column-key").append(entry.name ? $.spawn("div").append(entry.name) : undefined, entry.ip ? $.spawn("div").append(entry.ip) : undefined, entry.unique_id ? $.spawn("div").append(entry.unique_id) : undefined, entry.hardware_id ? $.spawn("div").append(entry.hardware_id) : undefined), $.spawn("div").addClass("column column-reason").text(entry.reason), $.spawn("div").addClass("column column-expires").text(entry.timestamp_expire ? moment(entry.timestamp_expire).format('DD.MM.YYYY hh:mm') : _translations.LfL6QN9W || (_translations.LfL6QN9W = tr("Never"))), $.spawn("div").addClass("column column-delete").append(button_delete = $.spawn("div").addClass("button-delete").append($.spawn("div").addClass("icon_em client-delete")))); + tag.on('click', event => { + if (selected_ban === entry || event.isDefaultPrevented()) + return; + selected_ban = entry; + container_ban_entries.find(".entry.selected").removeClass("selected"); + tag.addClass("selected"); + update_edit_window(true); + }); + button_delete.on('click', event => { + event.preventDefault(); + controller.delete_ban(entry.banid, entry.server_id).then(() => { + tag.css({ opacity: 1 }).animate({ opacity: 0 }, 250, () => tag.animate({ "max-height": 0 }, 250, () => tag.remove())); + if (entry === selected_ban) { + selected_ban = undefined; + update_edit_window(false); + } + }).catch(error => { + log.error(LogCategory.CLIENT, _translations._9iGfBfM || (_translations._9iGfBfM = tr("Failed to delete ban: %o")), error); + if (error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(_translations.uxwU2nG6 || (_translations.uxwU2nG6 = tr("Failed to delete ban")), MessageHelper.formatMessage(_translations.lpipbMzX || (_translations.lpipbMzX = tr("Failed to delete ban. {:br:}Error: {}")), error)).open(); + }); + }); + if (selected) { + selected_ban = entry; + update_edit_window(false); + } + const lower_mesh = (entry.reason || "").toLowerCase() + " " + + (entry.unique_id || "").toLowerCase() + " " + + (entry.name || "").toLowerCase() + " " + + (entry.ip || "").toLowerCase() + " " + + (entry.hardware_id || "").toLowerCase(); + callback_ban_filter.push((text, flag_own, highlight_own) => { + if (text && lower_mesh.indexOf(text) == -1) { + tag.hide(); + return false; + } + if (flag_own && !entry.flag_own) { + tag.hide(); + return false; + } + tag.show().toggleClass("highlight", highlight_own && + entry.flag_own); + return true; + }); + return tag; + }; + const update_banlist = (selected_ban) => { + callback_ban_filter = []; + container_ban_entries.find(".entry").remove(); + container_ban_entries_error.hide(); + container_ban_entries_empty.show().find("a").text(_translations.Ma1zisV8 || (_translations.Ma1zisV8 = tr("Loading..."))); + let bans = []; + controller.request_list(_bans => bans.push(..._bans)).then(() => { + if (bans.length) { + container_ban_entries.append(...bans.map(e => build_ban_entry(e, e.banid === selected_ban))); + container_ban_entries_empty.hide(); + } + else { + container_ban_entries_empty.find("a").text(_translations.sDgA4pks || (_translations.sDgA4pks = tr("No bans registered"))); + } + update_ban_filter(); + }).catch(error => { + log.info(LogCategory.CLIENT, _translations.TA3MTtDq || (_translations.TA3MTtDq = tr("Failed to update ban list: %o")), error); + if (error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? _translations.McoQVATK || (_translations.McoQVATK = tr("no permissions")) : error.extra_message || error.message; + container_ban_entries_error.show().find("a").text((_translations.sRJ6ujQf || (_translations.sRJ6ujQf = tr("Failed to receive banlist: "))) + error); + container_ban_entries_empty.hide(); + }); + }; + const build_trigger_entry = (entry) => { + const spawn_key_value = (key, value, reason) => { + return $.spawn("div").addClass("property").toggleClass("highlighted", reason).append($.spawn("div").addClass("key").text(key + ": "), $.spawn("div").addClass("value").text(value)); + }; + let cause_name = !!selected_ban.name && !!entry.client_nickname.match(selected_ban.name); + let cause_uid = !cause_name && !!selected_ban.unique_id && selected_ban.unique_id.toLowerCase() === (entry.unique_id || "").toLowerCase(); + let cause_ip = !cause_uid && !!selected_ban.ip && selected_ban.ip.toLowerCase() === (entry.connection_ip || "").toLowerCase(); + let cause_hwid = !cause_ip && !!selected_ban.hardware_id && selected_ban.hardware_id.toLowerCase() === (entry.hardware_id || "").toLowerCase(); + /* we guess that IP is the cause because we dont see the IP and there is no other reason */ + if (!cause_name && !cause_uid && !cause_ip && !cause_hwid && entry.connection_ip === "hidden") + cause_ip = true; + const time_str = moment(entry.timestamp).format('DD.MM.YYYY hh:mm'); + const tag = $.spawn("div").addClass("entry").append($.spawn("div").addClass("column column-properties").append(entry.client_nickname ? spawn_key_value(_translations.cuxjhYl0 || (_translations.cuxjhYl0 = tr("Nickname")), entry.client_nickname, cause_name) : undefined, entry.connection_ip ? spawn_key_value(_translations.lMroAnEz || (_translations.lMroAnEz = tr("IP")), entry.connection_ip, cause_ip) : undefined, entry.unique_id ? spawn_key_value(_translations.TEivpBwN || (_translations.TEivpBwN = tr("Unique ID")), entry.unique_id, cause_uid) : undefined, entry.hardware_id ? spawn_key_value(_translations.CfdgV9De || (_translations.CfdgV9De = tr("Hardware ID")), entry.hardware_id, cause_hwid) : undefined), $.spawn("div").addClass("column column-timestamp").text(time_str)); + const lower_mesh = (entry.unique_id || "").toLowerCase() + " " + + (entry.client_nickname || "").toLowerCase() + " " + + (entry.connection_ip || "").toLowerCase() + " " + + (entry.hardware_id || "").toLowerCase() + " " + + time_str + " " + + entry.timestamp; + callback_trigger_filter.push(text => { + if (text && lower_mesh.indexOf(text) == -1) { + tag.hide(); + return false; + } + tag.show(); + return true; + }); + return tag; + }; + const update_triggerlist = () => { + callback_trigger_filter = []; + container_trigger_entries.find(".entry").remove(); + container_trigger_entries_error.hide(); + container_trigger_entries_empty.show().find("a").text(_translations.OZoex2JI || (_translations.OZoex2JI = tr("Loading..."))); + let triggers = []; + controller.request_trigger_list({ + ban_id: selected_ban.banid, + server_id: selected_ban.server_id + }, _triggers => triggers.push(..._triggers)).then(() => { + if (triggers.length) { + container_trigger_entries.append(...triggers.sort((a, b) => b.timestamp - a.timestamp).map(e => build_trigger_entry(e))); + container_trigger_entries_empty.hide(); + } + else { + container_trigger_entries_empty.find("a").text(_translations.jwxGIkmy || (_translations.jwxGIkmy = tr("No triggers logged"))); + } + update_trigger_filter(); + }).catch(error => { + log.info(LogCategory.CLIENT, _translations.jFQD0Gmp || (_translations.jFQD0Gmp = tr("Failed to update trigger list: %o")), error); + if (error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? _translations.ozlD0jd5 || (_translations.ozlD0jd5 = tr("no permissions")) : error.extra_message || error.message; + container_trigger_entries_error.show().find("a").text((_translations.vm8uLmSc || (_translations.vm8uLmSc = tr("Failed to receive trigger list: "))) + error); + container_trigger_entries_empty.hide(); + }); + }; + const show_triggerlist = () => { + container_trigger.show(); + }; + /* general input field rules */ + const initialize_fields = (tag, index) => { + const input_name = tag.find(".group-name input").on('change keyup', () => update_category_inputs[index]()); + const input_ip = tag.find(".group-ip input").on('change keyup', () => update_category_inputs[index]()); + const input_uid = tag.find(".group-unique-id input").on('change keyup', () => update_category_inputs[index]()); + const input_hwid = tag.find(".group-hwid input").on('change keyup', () => update_category_inputs[index]()); + const input_reason = tag.find(".group-reason textarea").on('change keyup', () => update_category_inputs[index]()); + //const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input").on('change keyup', () => update_category_inputs[index]()); + const input_duration_type = tag.find(".group-duration select").on('change keyup', () => update_category_inputs[index]()); + const tooltip_duration_max = tag.find(".tooltip-max-time a.max"); + update_category_inputs[index] = () => { + let _criteria_set = false; + let _input_invalid = false; + { + //TODO: Check if in regex mode or not + const value = input_name.val() || ""; + if (value.length > 255) { + _input_invalid = true; + input_name.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + _criteria_set = _criteria_set || !!value; + input_name.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + { + //TODO: Check if in regex mode or not + const value = input_ip.val() || ""; + if (value.length > 255) { + _input_invalid = true; + input_ip.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + _criteria_set = _criteria_set || !!value; + input_ip.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + { + const value = input_uid.val() || ""; + try { + if (value && atob(value).length != 20) + throw ""; + _criteria_set = _criteria_set || !!value; + input_uid.firstParent(".input-boxed").removeClass("is-invalid"); + } + catch (e) { + _input_invalid = true; + input_uid.firstParent(".input-boxed").addClass("is-invalid"); + } + } + { + const value = input_hwid.val() || ""; + if (value.length > 255) { + _input_invalid = true; + input_hwid.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + _criteria_set = _criteria_set || !!value; + input_hwid.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + { + const value = input_reason.val() || ""; + if (value.length > 512) { + _input_invalid = true; + input_reason.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + input_reason.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + { + const type = input_duration_type.val(); + const value = parseInt(input_duration_value.val()); + const disabled = input_duration_type.prop("disabled"); + input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); + if (type !== "perm") { + if (input_duration_value.attr("x-saved-value")) { + input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); + input_duration_value.attr("x-saved-value", null); + } + const selected_option = input_duration_type.find("option[value='" + type + "']"); + const max = parseInt(selected_option.attr("duration-max")); + input_duration_value.attr("max", max); + if ((value > max && max != -1) || value < 1) { + _input_invalid = true; + input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); + } + if (max != -1) + tooltip_duration_max.html((_translations.hlQqRdbg || (_translations.hlQqRdbg = tr("You're allowed to ban a maximum of "))) + "" + max + " " + Modals.duration_data[type][max == 1 ? "1-text" : "text"] + ""); + else + tooltip_duration_max.html(_translations.l6HhHM6Z || (_translations.l6HhHM6Z = tr("You're allowed to ban permanent."))); + } + else { + if (value && !Number.isNaN(value)) + input_duration_value.attr("x-saved-value", value); + input_duration_value.attr("placeholder", _translations.fjEVRGtV || (_translations.fjEVRGtV = tr("for ever"))).val(null); + tooltip_duration_max.html(_translations.QdbjApBs || (_translations.QdbjApBs = tr("You're allowed to ban permanent."))); + } + } + button_apply.prop("disabled", !(button_apply_state[button_apply_state_index] = _criteria_set && !_input_invalid)); + }; + /* initialize ban time */ + controller.max_bantime().catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { + let unlimited = max_time == 0 || max_time == -1; + if (unlimited) + max_time = 0; + for (const value of Object.keys(Modals.duration_data)) { + input_duration_type.find("option[value='" + value + "']") + .prop("disabled", !unlimited && max_time >= Modals.duration_data[value].scale) + .attr("duration-scale", Modals.duration_data[value].scale) + .attr("duration-max", unlimited ? -1 : Math.floor(max_time / Modals.duration_data[value].scale)); + } + input_duration_type.find("option[value='perm']") + .prop("disabled", !unlimited) + .attr("duration-scale", 0) + .attr("duration-max", -1); + }); + }; + initialize_fields(container_add, 0); + initialize_fields(container_edit, 1); + /* the edit "handler" */ + { + const tag = container_edit; + const input_name = tag.find(".group-name input"); + const input_ip = tag.find(".group-ip input"); + const input_interpret = tag.find(".group-interpret select"); + const input_uid = tag.find(".group-unique-id input"); + const input_hwid = tag.find(".group-hwid input"); + const input_reason = tag.find(".group-reason textarea"); + const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input"); + const input_duration_type = tag.find(".group-duration select"); + const tooltip_duration_detailed = tag.find(".tooltip-max-time a.detailed"); + const label_enforcement_count = tag.find(".group-enforcements .value a"); + const button_enforcement_list = tag.find(".button-enforcement-list"); + const container_creator = tag.find(".group-creator .value"); + update_edit_window = (switch_to) => { + category_edit.toggleClass("disabled", !selected_ban); + const editable = selected_ban && selected_ban.server_id === 0 ? permission_edit[1] : permission_edit[0]; + input_name.val(selected_ban ? selected_ban.name : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_ip.val(selected_ban ? selected_ban.ip : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_uid.val(selected_ban ? selected_ban.unique_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_hwid.val(selected_ban ? selected_ban.hardware_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_reason.val(selected_ban ? selected_ban.reason : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_interpret.find("option").eq(selected_ban && typeof (selected_ban.name_type) === "number" ? selected_ban.name_type : 2).prop("selected", true).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + label_enforcement_count.text((selected_ban ? selected_ban.enforcements : 0) || 0); + button_enforcement_list.prop("disabled", !selected_ban || selected_ban.enforcements == 0); + input_global.prop("checked", selected_ban && selected_ban.server_id == 0); + input_duration_type.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_duration_value.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + if (selected_ban) { + if (selected_ban.timestamp_expire > selected_ban.timestamp_created) { + const duration = Math.ceil((selected_ban.timestamp_expire - selected_ban.timestamp_created) / 1000); + const periods = Object.keys(Modals.duration_data); + let index; + for (index = 0; index < periods.length; index++) { + if (Modals.duration_data[periods[index]].scale > duration + 1 || ((duration + 1) % Modals.duration_data[periods[index]].scale) > 1.9) + break; + } + if (index > 0) + index--; + input_duration_type.find("option[value='" + periods[index] + "']").prop("selected", true); + input_duration_value.val(Math.ceil(duration / Modals.duration_data[periods[index]].scale)); + tooltip_duration_detailed.text($.spawn("div").append(...MessageHelper.formatMessage(_translations.TFLC6YL4 || (_translations.TFLC6YL4 = tr("The ban lasts for exact {}.")), MessageHelper.format_time(duration * 1000, "never"))).text()); + } + else { + tooltip_duration_detailed.text(_translations.mHWmn0e_ || (_translations.mHWmn0e_ = tr("The ban is forever."))); + input_duration_value.attr("placeholder", _translations._P5v1n6D || (_translations._P5v1n6D = tr("for ever"))).val(null).prop('disabled', true); + input_duration_type.find("option[value='perm']").prop("selected", true); + } + } + container_creator.empty(); + if (selected_ban) { + container_creator.append(htmltags.generate_client_object({ + client_id: 0, + client_unique_id: selected_ban.invoker_unique_id, + client_name: selected_ban.invoker_name, + add_braces: false + })); + } + if (switch_to) + category_edit.trigger('click'); + }; + button_apply.on('click', event => { + if (!button_apply_state[1] || button_apply_state_index != 1) + return; + const data = { banid: selected_ban.banid }; + if (input_ip.val() != selected_ban.ip) + data["ip"] = input_ip.val(); + if (input_name.val() != selected_ban.name) + data["name"] = input_name.val(); + if (input_uid.val() != selected_ban.unique_id) + data["uid"] = input_uid.val(); + if (input_hwid.val() != selected_ban.hardware_id) + data["hwid"] = input_hwid.val(); + if (input_reason.val() != selected_ban.reason) + data["banreason"] = input_reason.val(); + if (input_reason.val() != selected_ban.reason) + data["reason"] = input_reason.val(); + const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val())); + if (selected_ban.timestamp_expire > 0 ? (selected_ban.timestamp_expire - selected_ban.timestamp_created != duration) : duration != 0) + data["time"] = Math.floor(duration / 1000); + controller.edit_ban(data).then(() => { + update_banlist(selected_ban ? selected_ban.banid : undefined); + selected_ban = undefined; + update_edit_window(false); + createInfoModal(_translations.E92mYEjR || (_translations.E92mYEjR = tr("Ban successfully edited")), _translations.EEwz6h5e || (_translations.EEwz6h5e = tr("Your ban has been successfully edited."))).open(); + }).catch(error => { + log.error(LogCategory.CLIENT, _translations.LQLYXfno || (_translations.LQLYXfno = tr("Failed to edited ban: %o")), error); + if (error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(_translations.bOiC47ix || (_translations.bOiC47ix = tr("Failed to edited ban")), MessageHelper.formatMessage(_translations.EcDuYmlv || (_translations.EcDuYmlv = tr("Failed to edited ban. {:br:}Error: {}")), error)).open(); + }); + }); + button_enforcement_list.on('click', () => { + update_triggerlist(); + show_triggerlist(); + }); + } + /* the create "handler" */ + { + const tag = container_add; + const input_name = tag.find(".group-name input"); + const input_ip = tag.find(".group-ip input"); + const input_interpret = tag.find(".group-interpret select"); + const input_uid = tag.find(".group-unique-id input"); + const input_hwid = tag.find(".group-hwid input"); + const input_reason = tag.find(".group-reason textarea"); + const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input"); + const input_duration_type = tag.find(".group-duration select"); + button_apply.on('click', event => { + if (!button_apply_state[0] || button_apply_state_index != 0) + return; + const data = { + banid: 0, + enforcements: 0, + }; + if (input_global.prop('checked')) + data.server_id = 0; + if (input_ip.val()) + data.ip = input_ip.val(); + if (input_name.val()) + data.name = input_name.val(); + if (input_uid.val()) + data.unique_id = input_uid.val(); + if (input_hwid.val()) + data.hardware_id = input_hwid.val(); + if (input_reason.val()) + data.reason = input_reason.val(); + data.timestamp_created = Date.now(); + data.timestamp_expire = input_duration_type.val() === "perm" ? 0 : (data.timestamp_created + 1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val())); + //TODO: input_interpret (Currently not supported by TeaSpeak) + controller.add_ban(data).then(() => { + input_name.val(null); + input_ip.val(null); + input_uid.val(null); + input_hwid.val(null); + input_reason.val(null); + input_duration_value.val(1); + update_banlist(); + createInfoModal(_translations.u2qYFkJb || (_translations.u2qYFkJb = tr("Ban successfully added")), _translations.aab8fpL2 || (_translations.aab8fpL2 = tr("Your ban has been successfully added."))).open(); + }).catch(error => { + log.error(LogCategory.CLIENT, _translations.VRhtN9SF || (_translations.VRhtN9SF = tr("Failed to add ban: %o")), error); + if (error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(_translations.zu2R85Jy || (_translations.zu2R85Jy = tr("Failed to add ban")), MessageHelper.formatMessage(_translations.iG8KyFbG || (_translations.iG8KyFbG = tr("Failed to add ban. {:br:}Error: {}")), error)).open(); + }); + }); + } + /* the banlist filter */ + { + const input_filter = container_ban.find(".container-filter input").on('change keyup', () => update_ban_filter()); + const option_show_own = container_ban.find(".option-show-own").on('change keyup', () => update_ban_filter()); + const option_hightlight_own = container_ban.find(".option-highlight-own").on('change keyup', () => update_ban_filter()); + update_ban_filter = () => { + const text = (input_filter.val() || "").toLowerCase(); + const flag_show_own = option_show_own.prop('checked'); + const flag_hightlight_own = option_hightlight_own.prop('checked'); + let count = 0; + for (const entry of callback_ban_filter) + if (entry(text, flag_show_own, flag_hightlight_own)) + count++; + if (callback_ban_filter.length != 0) { + if (count > 0) + container_ban_entries_empty.hide(); + else + container_ban_entries_empty.show().find("a").text(_translations.mM6I4PAT || (_translations.mM6I4PAT = tr("No bans found"))); + } + }; + } + /* the trigger list filter */ + { + const input_filter = container_trigger.find(".container-filter input").on('change keyup', () => update_trigger_filter()); + const option_hightlight_cause = container_trigger.find(".option-highlight-cause").on('change keyup', () => update_trigger_filter()); + const button_close = container_trigger.find(".container-close"); + update_trigger_filter = () => { + const text = (input_filter.val() || "").toLowerCase(); + let count = 0; + for (const entry of callback_trigger_filter) + if (entry(text)) + count++; + if (callback_trigger_filter.length != 0) { + if (count > 0) + container_trigger_entries_empty.hide(); + else + container_trigger_entries_empty.show().find("a").text(_translations.KT1iYZJ7 || (_translations.KT1iYZJ7 = tr("No trigger events found"))); + } + container_trigger.find(".container-list").toggleClass('highlight', option_hightlight_cause.prop('checked')); + }; + button_close.on('click', () => container_trigger.hide()); + } + template.find(".button-refresh-banlist").on('click', event => update_banlist(selected_ban ? selected_ban.banid : undefined)); + template.find(".button-refresh-triggerlist").on('click', event => update_triggerlist()); + /* initialize */ + category_add.trigger('click'); + update_edit_window(false); + update_banlist(); + tooltip(template); + return template.children(); + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["a425129a15439d8284d4458d8207b7cfbe3454239f761f5a81b6d3cfdae013e2"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["a425129a15439d8284d4458d8207b7cfbe3454239f761f5a81b6d3cfdae013e2"] = "a425129a15439d8284d4458d8207b7cfbe3454239f761f5a81b6d3cfdae013e2"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Zs_7r6YN", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (250,36)" }, { name: "nAZZvi4s", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (252,36)" }, { name: "XGdxflOT", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (254,36)" }, { name: "OLN_TF22", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (276,36)" }, { name: "WZoW8if5", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (278,36)" }, { name: "pvBk1ajE", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (302,45)" }, { name: "Y2HsuYX1", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (304,45)" }, { name: "n34NFanP", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (330,26)" }, { name: "dbxgzCQU", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (330,50)" }, { name: "Ur57HLbf", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (352,26)" }, { name: "f_X73CLe", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (352,57)" }, { name: "kgkM1V3H", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (477,26)" }, { name: "fwl0c4IH", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (477,43)" }, { name: "gTbMD5iW", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (483,37)" }, { name: "VeWm3kcQ", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (483,54)" }, { name: "K906LIA6", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (486,38)" }, { name: "vh_OTXUb", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (486,83)" }, { name: "cowCUjHx", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (492,26)" }, { name: "OJx9INaZ", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (492,49)" }, { name: "AF4Y3IBj", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (502,34)" }, { name: "vPX9C6Hk", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (502,66)" }, { name: "HHWNZCf4", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (513,30)" }, { name: "c6ki08OD", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (513,66)" }, { name: "UFVzWw9i", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (564,47)" }, { name: "FtoXJtTk", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (569,47)" }, { name: "Sqmplp9n", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (626,30)" }, { name: "e3uQNqK1", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (626,66)" }, { name: "uubmt2N7", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (635,30)" }, { name: "cHeIpZbR", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (635,62)" }, { name: "CunPp0Zv", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (643,30)" }, { name: "vHCggytV", path: "D:/TeaSpeak/web/shared/js/ui/frames/ControlBar.ts (643,62)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/* + client_output_hardware Value: '1' + client_output_muted Value: '0' + client_outputonly_muted Value: '0' + + client_input_hardware Value: '1' + client_input_muted Value: '0' + + client_away Value: '0' + client_away_message Value: '' + */ +let control_bar; /* global variable to access the control bar */ +class ControlBar { + constructor(htmlTag) { + this.htmlTag = htmlTag; + } + initialize_connection_handler_state(handler) { + /* setup the state like the last displayed one */ + handler.client_status.output_muted = this._button_speakers === "muted"; + handler.client_status.input_muted = this._button_microphone === "muted"; + handler.client_status.channel_subscribe_all = this._button_subscribe_all; + handler.client_status.queries_visible = this._button_query_visible; + } + set_connection_handler(handler) { + if (this.connection_handler == handler) + return; + this.connection_handler = handler; + this.apply_server_state(); + this.update_connection_state(); + } + apply_server_state() { + if (!this.connection_handler) + return; + const flag_away = typeof (this.connection_handler.client_status.away) === "string" || this.connection_handler.client_status.away; + if (!flag_away) + this.button_away_active = "online"; + else if (flag_away && this._button_away_active === "online") + this.button_away_active = "away"; + this.button_query_visible = this.connection_handler.client_status.queries_visible; + this.button_subscribe_all = this.connection_handler.client_status.channel_subscribe_all; + this.apply_server_hostbutton(); + this.apply_server_voice_state(); + } + apply_server_hostbutton() { + const server = this.connection_handler.channelTree.server; + if (server && server.properties.virtualserver_hostbutton_gfx_url) { + this._button_hostbanner + .attr("title", server.properties.virtualserver_hostbutton_tooltip || server.properties.virtualserver_hostbutton_gfx_url) + .attr("href", server.properties.virtualserver_hostbutton_url); + this._button_hostbanner.find("img").attr("src", server.properties.virtualserver_hostbutton_gfx_url); + this._button_hostbanner.each((_, e) => { e.style.display = null; }); + } + else { + this._button_hostbanner.each((_, e) => { e.style.display = "none"; }); + } + } + apply_server_voice_state() { + if (!this.connection_handler) + return; + this.button_microphone = !this.connection_handler.client_status.input_hardware ? "disabled" : this.connection_handler.client_status.input_muted ? "muted" : "enabled"; + this.button_speaker = this.connection_handler.client_status.output_muted ? "muted" : "enabled"; + top_menu.update_state(); //TODO: Only run "small" update? + } + current_connection_handler() { + return this.connection_handler; + } + initialise() { + let dropdownify = (tag) => { + tag.find(".dropdown-arrow").on('click', () => { + tag.addClass("displayed"); + }).hover(() => { + tag.addClass("displayed"); + }, () => { + if (tag.find(".dropdown:hover").length > 0) + return; + tag.removeClass("displayed"); + }); + tag.on('mouseleave', () => { + tag.removeClass("displayed"); + }); + }; + this.htmlTag.find(".btn_connect").on('click', this.on_open_connect.bind(this)); + this.htmlTag.find(".btn_connect_new_tab").on('click', this.on_open_connect_new_tab.bind(this)); + this.htmlTag.find(".btn_disconnect").on('click', this.on_execute_disconnect.bind(this)); + this.htmlTag.find(".btn_mute_input").on('click', this.on_toggle_microphone.bind(this)); + this.htmlTag.find(".btn_mute_output").on('click', this.on_toggle_sound.bind(this)); + this.htmlTag.find(".button-subscribe-mode").on('click', this.on_toggle_channel_subscribe.bind(this)); + this.htmlTag.find(".btn_query_toggle").on('click', this.on_toggle_query_view.bind(this)); + this.htmlTag.find(".btn_open_settings").on('click', this.on_open_settings.bind(this)); + this.htmlTag.find(".btn_permissions").on('click', this.on_open_permissions.bind(this)); + this.htmlTag.find(".btn_banlist").on('click', this.on_open_banslist.bind(this)); + this.htmlTag.find(".button-playlist-manage").on('click', this.on_open_playlist_manage.bind(this)); + this.htmlTag.find(".btn_token_use").on('click', this.on_token_use.bind(this)); + this.htmlTag.find(".btn_token_list").on('click', this.on_token_list.bind(this)); + (this._button_hostbanner = this.htmlTag.find(".button-hostbutton")).hide().on('click', () => { + if (!this.connection_handler) + return; + const server = this.connection_handler.channelTree.server; + if (!server || !server.properties.virtualserver_hostbutton_url) + return; + window.open(server.properties.virtualserver_hostbutton_url, '_blank'); + }); + { + this.htmlTag.find(".btn_away_disable").on('click', this.on_away_disable.bind(this)); + this.htmlTag.find(".btn_away_disable_global").on('click', this.on_away_disable_global.bind(this)); + this.htmlTag.find(".btn_away_enable").on('click', this.on_away_enable.bind(this)); + this.htmlTag.find(".btn_away_enable_global").on('click', this.on_away_enable_global.bind(this)); + this.htmlTag.find(".btn_away_message").on('click', this.on_away_set_message.bind(this)); + this.htmlTag.find(".btn_away_message_global").on('click', this.on_away_set_message_global.bind(this)); + this.htmlTag.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this)); + } + dropdownify(this.htmlTag.find(".container-connect")); + dropdownify(this.htmlTag.find(".container-disconnect")); + dropdownify(this.htmlTag.find(".btn_token")); + dropdownify(this.htmlTag.find(".btn_away")); + dropdownify(this.htmlTag.find(".btn_bookmark")); + dropdownify(this.htmlTag.find(".btn_query")); + dropdownify(this.htmlTag.find(".dropdown-audio")); + dropdownify(this.htmlTag.find(".dropdown-servertools")); + { + } + { + this.htmlTag.find(".btn_bookmark_list").on('click', this.on_bookmark_manage.bind(this)); + this.htmlTag.find(".btn_bookmark_add").on('click', this.on_bookmark_server_add.bind(this)); + } + { + /* search for query buttons not only on the large device button */ + this.htmlTag.find(".btn_query_create").on('click', this.on_open_query_create.bind(this)); + this.htmlTag.find(".btn_query_manage").on('click', this.on_open_query_manage.bind(this)); + } + this.update_bookmarks(); + this.update_bookmark_status(); + //Need an initialise + this.button_speaker = settings.static_global(Settings.KEY_CONTROL_MUTE_OUTPUT, false) ? "muted" : "enabled"; + this.button_microphone = settings.static_global(Settings.KEY_CONTROL_MUTE_INPUT, false) ? "muted" : "enabled"; + this.button_subscribe_all = true; + this.button_query_visible = false; + } + /* Update the UI */ + set button_away_active(flag) { + if (this._button_away_active === flag) + return; + this._button_away_active = flag; + this.update_button_away(); + } + update_button_away() { + const button_away_enable = this.htmlTag.find(".btn_away_enable"); + const button_away_disable = this.htmlTag.find(".btn_away_disable"); + const button_away_toggle = this.htmlTag.find(".btn_away_toggle"); + const button_away_disable_global = this.htmlTag.find(".btn_away_disable_global"); + const button_away_enable_global = this.htmlTag.find(".btn_away_enable_global"); + const button_away_message_global = this.htmlTag.find(".btn_away_message_global"); + button_away_toggle.toggleClass("activated", this._button_away_active !== "online"); + button_away_enable.toggle(this._button_away_active === "online"); + button_away_disable.toggle(this._button_away_active !== "online"); + const connections = server_connections.server_connection_handlers(); + if (connections.length <= 1) { + button_away_disable_global.hide(); + button_away_enable_global.hide(); + button_away_message_global.hide(); + } + else { + button_away_message_global.show(); + button_away_enable_global.toggle(server_connections.server_connection_handlers().filter(e => !e.client_status.away).length > 0); + button_away_disable_global.toggle(this._button_away_active === "away-global" || + server_connections.server_connection_handlers().filter(e => typeof (e.client_status.away) === "string" || e.client_status.away).length > 0); + } + } + set button_microphone(state) { + if (this._button_microphone === state) + return; + this._button_microphone = state; + let tag = this.htmlTag.find(".btn_mute_input"); + const tag_icon = tag.find(".icon_em, .icon"); + tag.toggleClass('activated', state === "muted"); + /* + tag_icon + .toggleClass('client-input_muted', state === "muted") + .toggleClass('client-capture', state === "enabled") + .toggleClass('client-activate_microphone', state === "disabled"); + */ + tag_icon + .toggleClass('client-input_muted', state !== "disabled") + .toggleClass('client-capture', false) + .toggleClass('client-activate_microphone', state === "disabled"); + if (state === "disabled") + tag_icon.attr('title', _translations.Zs_7r6YN || (_translations.Zs_7r6YN = tr("Enable your microphone on this server"))); + else if (state === "enabled") + tag_icon.attr('title', _translations.nAZZvi4s || (_translations.nAZZvi4s = tr("Mute microphone"))); + else + tag_icon.attr('title', _translations.XGdxflOT || (_translations.XGdxflOT = tr("Unmute microphone"))); + } + set button_speaker(state) { + if (this._button_speakers === state) + return; + this._button_speakers = state; + let tag = this.htmlTag.find(".btn_mute_output"); + const tag_icon = tag.find(".icon_em, .icon"); + tag.toggleClass('activated', state === "muted"); + /* + tag_icon + .toggleClass('client-output_muted', state !== "enabled") + .toggleClass('client-volume', state === "enabled"); + */ + tag_icon + .toggleClass('client-output_muted', true) + .toggleClass('client-volume', false); + if (state === "enabled") + tag_icon.attr('title', _translations.OLN_TF22 || (_translations.OLN_TF22 = tr("Mute sound"))); + else + tag_icon.attr('title', _translations.WZoW8if5 || (_translations.WZoW8if5 = tr("Unmute sound"))); + } + set button_subscribe_all(state) { + if (this._button_subscribe_all === state) + return; + this._button_subscribe_all = state; + this.htmlTag + .find(".button-subscribe-mode") + .toggleClass('activated', this._button_subscribe_all) + .find('.icon_em') + .toggleClass('client-unsubscribe_from_all_channels', !this._button_subscribe_all) + .toggleClass('client-subscribe_to_all_channels', this._button_subscribe_all); + } + set button_query_visible(state) { + if (this._button_query_visible === state) + return; + this._button_query_visible = state; + const button = this.htmlTag.find(".btn_query_toggle"); + button.toggleClass('activated', this._button_query_visible); + if (this._button_query_visible) + button.find(".query-text").text(_translations.pvBk1ajE || (_translations.pvBk1ajE = tr("Hide server queries"))); + else + button.find(".query-text").text(_translations.Y2HsuYX1 || (_translations.Y2HsuYX1 = tr("Show server queries"))); + } + /* UI listener */ + on_away_toggle() { + if (this._button_away_active === "away" || this._button_away_active === "away-global") + this.button_away_active = "online"; + else + this.button_away_active = "away"; + if (this.connection_handler) + this.connection_handler.set_away_status(this._button_away_active !== "online"); + } + on_away_enable() { + this.button_away_active = "away"; + if (this.connection_handler) + this.connection_handler.set_away_status(true); + } + on_away_disable() { + this.button_away_active = "online"; + if (this.connection_handler) + this.connection_handler.set_away_status(false); + } + on_away_set_message() { + createInputModal(_translations.n34NFanP || (_translations.n34NFanP = tr("Set away message")), _translations.dbxgzCQU || (_translations.dbxgzCQU = tr("Please enter your away message")), message => true, message => { + if (typeof (message) === "string") { + this.button_away_active = "away"; + if (this.connection_handler) + this.connection_handler.set_away_status(message); + } + }).open(); + } + on_away_enable_global() { + this.button_away_active = "away-global"; + for (const connection of server_connections.server_connection_handlers()) + connection.set_away_status(true); + } + on_away_disable_global() { + this.button_away_active = "online"; + for (const connection of server_connections.server_connection_handlers()) + connection.set_away_status(false); + } + on_away_set_message_global() { + createInputModal(_translations.Ur57HLbf || (_translations.Ur57HLbf = tr("Set global away message")), _translations.f_X73CLe || (_translations.f_X73CLe = tr("Please enter your global away message")), message => true, message => { + if (typeof (message) === "string") { + this.button_away_active = "away"; + for (const connection of server_connections.server_connection_handlers()) + connection.set_away_status(message); + } + }).open(); + } + on_toggle_microphone() { + if (this._button_microphone === "disabled" || this._button_microphone === "muted") { + this.button_microphone = "enabled"; + sound.manager.play(Sound.MICROPHONE_ACTIVATED); + } + else { + this.button_microphone = "muted"; + sound.manager.play(Sound.MICROPHONE_MUTED); + } + if (this.connection_handler) { + this.connection_handler.client_status.input_muted = this._button_microphone !== "enabled"; + if (!this.connection_handler.client_status.input_hardware) + this.connection_handler.acquire_recorder(default_recorder, true); /* acquire_recorder already updates the voice status */ + else + this.connection_handler.update_voice_status(undefined); + /* just update the last changed value */ + settings.changeGlobal(Settings.KEY_CONTROL_MUTE_INPUT, this.connection_handler.client_status.input_muted); + } + } + on_toggle_sound() { + if (this._button_speakers === "muted") { + this.button_speaker = "enabled"; + sound.manager.play(Sound.SOUND_ACTIVATED); + } + else { + this.button_speaker = "muted"; + sound.manager.play(Sound.SOUND_MUTED); + } + if (this.connection_handler) { + this.connection_handler.client_status.output_muted = this._button_speakers !== "enabled"; + this.connection_handler.update_voice_status(undefined); + /* just update the last changed value */ + settings.changeGlobal(Settings.KEY_CONTROL_MUTE_OUTPUT, this.connection_handler.client_status.output_muted); + } + } + on_toggle_channel_subscribe() { + this.button_subscribe_all = !this._button_subscribe_all; + if (this.connection_handler) { + this.connection_handler.client_status.channel_subscribe_all = this._button_subscribe_all; + if (this._button_subscribe_all) + this.connection_handler.channelTree.subscribe_all_channels(); + else + this.connection_handler.channelTree.unsubscribe_all_channels(true); + this.connection_handler.settings.changeServer(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, this._button_subscribe_all); + } + } + on_toggle_query_view() { + this.button_query_visible = !this._button_query_visible; + if (this.connection_handler) { + this.connection_handler.client_status.queries_visible = this._button_query_visible; + this.connection_handler.channelTree.toggle_server_queries(this._button_query_visible); + this.connection_handler.settings.changeServer(Settings.KEY_CONTROL_SHOW_QUERIES, this._button_subscribe_all); + } + } + on_open_settings() { + Modals.spawnSettingsModal(); + } + on_open_connect() { + if (this.connection_handler) + this.connection_handler.cancel_reconnect(true); + Modals.spawnConnectModal({}, { + url: "ts.TeaSpeak.de", + enforce: false + }); + } + on_open_connect_new_tab() { + Modals.spawnConnectModal({ + default_connect_new_tab: true + }, { + url: "ts.TeaSpeak.de", + enforce: false + }); + } + update_connection_state() { + if (this.connection_handler.serverConnection && this.connection_handler.serverConnection.connected()) { + this.htmlTag.find(".container-disconnect").show(); + this.htmlTag.find(".container-connect").hide(); + } + else { + this.htmlTag.find(".container-disconnect").hide(); + this.htmlTag.find(".container-connect").show(); + } + /* + switch (this.connection_handler.serverConnection ? this.connection_handler.serverConnection.connected() : ConnectionState.UNCONNECTED) { + case ConnectionState.CONNECTED: + case ConnectionState.CONNECTING: + case ConnectionState.INITIALISING: + this.htmlTag.find(".container-disconnect").show(); + this.htmlTag.find(".container-connect").hide(); + break; + default: + this.htmlTag.find(".container-disconnect").hide(); + this.htmlTag.find(".container-connect").show(); + } + */ + } + on_execute_disconnect() { + this.connection_handler.cancel_reconnect(true); + this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message? + this.update_connection_state(); + this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); + this.connection_handler.log.log(log.server.Type.DISCONNECTED, {}); + } + on_token_use() { + createInputModal(_translations.kgkM1V3H || (_translations.kgkM1V3H = tr("Use token")), _translations.fwl0c4IH || (_translations.fwl0c4IH = tr("Please enter your token/privilege key")), message => message.length > 0, result => { + if (!result) + return; + if (this.connection_handler.serverConnection.connected) + this.connection_handler.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(_translations.gTbMD5iW || (_translations.gTbMD5iW = tr("Use token")), _translations.VeWm3kcQ || (_translations.VeWm3kcQ = tr("Toke successfully used!"))).open(); + }).catch(error => { + //TODO tr + createErrorModal(_translations.K906LIA6 || (_translations.K906LIA6 = tr("Use token")), MessageHelper.formatMessage(_translations.vh_OTXUb || (_translations.vh_OTXUb = tr("Failed to use token: {}")), error instanceof CommandResult ? error.message : error)).open(); + }); + }).open(); + } + on_token_list() { + createErrorModal(_translations.cowCUjHx || (_translations.cowCUjHx = tr("Not implemented")), _translations.OJx9INaZ || (_translations.OJx9INaZ = tr("Token list is not implemented yet!"))).open(); + } + on_open_permissions() { + let button = this.htmlTag.find(".btn_permissions"); + button.addClass("activated"); + setTimeout(() => { + if (this.connection_handler) + Modals.spawnPermissionEdit(this.connection_handler).open(); + else + createErrorModal(_translations.AF4Y3IBj || (_translations.AF4Y3IBj = tr("You have to be connected")), _translations.vPX9C6Hk || (_translations.vPX9C6Hk = tr("You have to be connected!"))).open(); + button.removeClass("activated"); + }, 0); + } + on_open_banslist() { + if (!this.connection_handler.serverConnection) + return; + if (this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { + Modals.openBanList(this.connection_handler); + } + else { + createErrorModal(_translations.HHWNZCf4 || (_translations.HHWNZCf4 = tr("You dont have the permission")), _translations.c6ki08OD || (_translations.c6ki08OD = tr("You dont have the permission to view the ban list"))).open(); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + on_bookmark_server_add() { + bookmarks.add_current_server(); + } + update_bookmark_status() { + this.htmlTag.find(".btn_bookmark_add").removeClass("hidden").addClass("disabled"); + this.htmlTag.find(".btn_bookmark_remove").addClass("hidden"); + } + update_bookmarks() { + // + let tag_bookmark = this.htmlTag.find(".btn_bookmark > .dropdown"); + tag_bookmark.find(".bookmark, .directory").remove(); + const build_entry = (bookmark) => { + if (bookmark.type == bookmarks.BookmarkType.ENTRY) { + const mark = bookmark; + const bookmark_connect = (new_tab) => { + this.htmlTag.find(".btn_bookmark").find(".dropdown").removeClass("displayed"); //FIXME Not working + bookmarks.boorkmak_connect(mark, new_tab); + }; + return $.spawn("div") + .addClass("bookmark") + .append( + //$.spawn("div").addClass("icon client-server") + IconManager.generate_tag(IconManager.load_cached_icon(mark.last_icon_id || 0), { animate: false }) /* must be false */) + .append($.spawn("div") + .addClass("name") + .text(bookmark.display_name) + .on('click', event => { + if (event.isDefaultPrevented()) + return; + bookmark_connect(false); + }) + .on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.UFVzWw9i || (_translations.UFVzWw9i = tr("Connect")), + icon_class: 'client-connect', + callback: () => bookmark_connect(false) + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.FtoXJtTk || (_translations.FtoXJtTk = tr("Connect in a new tab")), + icon_class: 'client-connect', + callback: () => bookmark_connect(true), + visible: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION) + }, contextmenu.Entry.CLOSE(() => { + setTimeout(() => { + this.htmlTag.find(".btn_bookmark.dropdown-arrow").removeClass("force-show"); + }, 250); + })); + this.htmlTag.find(".btn_bookmark.dropdown-arrow").addClass("force-show"); + })); + } + else { + const mark = bookmark; + const container = $.spawn("div").addClass("sub-menu dropdown"); + const result = $.spawn("div") + .addClass("directory") + .append($.spawn("div").addClass("icon client-folder")) + .append($.spawn("div") + .addClass("name") + .text(bookmark.display_name)) + .append($.spawn("div").addClass("arrow right")) + .append($.spawn("div").addClass("sub-container") + .append(container)); + /* we've to keep it this order because we're then keeping the reference of the loading icons... */ + for (const member of mark.content) + container.append(build_entry(member)); + return result; + } + }; + for (const bookmark of bookmarks.bookmarks().content) { + const entry = build_entry(bookmark); + tag_bookmark.append(entry); + } + } + on_bookmark_manage() { + Modals.spawnBookmarkModal(); + } + on_open_query_create() { + if (this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) { + Modals.spawnQueryCreate(this.connection_handler); + } + else { + createErrorModal(_translations.Sqmplp9n || (_translations.Sqmplp9n = tr("You dont have the permission")), _translations.e3uQNqK1 || (_translations.e3uQNqK1 = tr("You dont have the permission to create a server query login"))).open(); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + on_open_query_manage() { + if (this.connection_handler && this.connection_handler.connected) { + Modals.spawnQueryManage(this.connection_handler); + } + else { + createErrorModal(_translations.uubmt2N7 || (_translations.uubmt2N7 = tr("You have to be connected")), _translations.cHeIpZbR || (_translations.cHeIpZbR = tr("You have to be connected!"))).open(); + } + } + on_open_playlist_manage() { + if (this.connection_handler && this.connection_handler.connected) { + Modals.spawnPlaylistManage(this.connection_handler); + } + else { + createErrorModal(_translations.CunPp0Zv || (_translations.CunPp0Zv = tr("You have to be connected")), _translations.vHCggytV || (_translations.vHCggytV = tr("You have to be connected to use this function!"))).open(); + } + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["197b7bafaa38d18270b5766fa93a5f6ad2093450fd9dc6608670cc211a33712d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["197b7bafaa38d18270b5766fa93a5f6ad2093450fd9dc6608670cc211a33712d"] = "197b7bafaa38d18270b5766fa93a5f6ad2093450fd9dc6608670cc211a33712d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "ZUXymrm7", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (161,31)" }, { name: "T1UFxXzN", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (173,27)" }, { name: "HS4rmli5", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (194,38)" }, { name: "kOLFxw_8", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (212,47)" }, { name: "zi0vuWHk", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (213,34)" }, { name: "slvMdYzx", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (213,70)" }, { name: "n8ME6kUb", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (280,38)" }, { name: "YBuSiAT5", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (354,39)" }, { name: "MQsgbKfE", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (377,51)" }, { name: "hTv3H6ip", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (394,51)" }, { name: "Wcp4Jl2M", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (412,27)" }, { name: "RjYBRqhz", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (425,47)" }, { name: "TTZmhO6X", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (438,51)" }, { name: "UvN4ZYoz", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (440,51)" }, { name: "yKOp1KDQ", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (444,25)" }, { name: "BAtStX1r", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (445,25)" }, { name: "EaTMLkfC", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (454,25)" }, { name: "YWdTGt0C", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (464,47)" }, { name: "os_PbxTh", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (466,21)" }, { name: "mWEfrIGh", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (467,21)" }, { name: "sKz9M0J1", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (472,21)" }, { name: "QmW2FhnQ", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (473,49)" }, { name: "c82V0qqg", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (480,21)" }, { name: "vIrDdie6", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (481,49)" }, { name: "DPauFd4a", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (488,47)" }, { name: "tPvfpJri", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (491,25)" }, { name: "yAUGk0u6", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (492,25)" }, { name: "XM3HFZtb", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (500,47)" }, { name: "SPp3hxky", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (503,21)" }, { name: "Ez6pR12l", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (504,21)" }, { name: "oeX7ep3Y", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (512,21)" }, { name: "qlxKgog8", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (523,34)" }, { name: "QTjxbWyV", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (523,57)" }, { name: "GL3SU8Zv", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (542,21)" }, { name: "L6t34lZs", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (544,40)" }, { name: "zo49e7He", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (544,100)" }, { name: "eTgdGWXl", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (575,47)" }, { name: "m_2kmrli", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (576,47)" }, { name: "gthobtoh", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (592,50)" }, { name: "OfYIEKF3", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (597,46)" }, { name: "mgD_hUkm", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (604,50)" }, { name: "E6Pxj7O_", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (667,51)" }, { name: "myFdKome", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (668,74)" }, { name: "yhxSmGy4", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (690,31)" }, { name: "FhG1RYUu", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (692,31)" }, { name: "tl9gg03_", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (694,31)" }, { name: "H8fSS_0Z", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (696,38)" }, { name: "IU0hK4NF", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (716,57)" }, { name: "Ak0MoTq5", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (719,50)" }, { name: "CdNixUn2", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (719,111)" }, { name: "ifNLFsmx", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (743,47)" }, { name: "ZAcARduu", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (744,70)" }, { name: "j356bpkL", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (763,43)" }, { name: "Hquv820o", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (764,66)" }, { name: "Nhi8Zrg7", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (778,41)" }, { name: "kwfqGPH6", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (805,46)" }, { name: "N3fwN_XS", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (811,37)" }, { name: "dWko40aF", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (811,59)" }, { name: "_o7eTDux", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (813,52)" }, { name: "q6MV9BJ6", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (817,63)" }, { name: "l2z4u5iq", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (819,63)" }, { name: "SERPbLsV", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (820,38)" }, { name: "Uwwz13se", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (824,46)" }, { name: "p6ktJjDH", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (837,56)" }, { name: "DpzOU8kx", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (843,71)" }, { name: "GSHnaSaC", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (845,71)" }, { name: "HuAordM2", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (849,67)" }, { name: "SqHGgSbh", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (850,42)" }, { name: "cCwgYPgQ", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (857,56)" }, { name: "Jkzl_W58", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (861,67)" }, { name: "HfLNjuHd", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (864,67)" }, { name: "b_WF3dmV", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (865,42)" }, { name: "PkrE3ZLe", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (873,56)" }, { name: "Yw4Xjm9C", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (877,67)" }, { name: "ZNppOyOZ", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (879,67)" }, { name: "n1Io657i", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (880,42)" }, { name: "dVyA2a02", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (884,37)" }, { name: "Wrvy2Obl", path: "D:/TeaSpeak/web/shared/js/ConnectionHandler.ts (884,73)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/// +/// +/// +/// +/// +/// +var DisconnectReason; +(function (DisconnectReason) { + DisconnectReason[DisconnectReason["HANDLER_DESTROYED"] = 0] = "HANDLER_DESTROYED"; + DisconnectReason[DisconnectReason["REQUESTED"] = 1] = "REQUESTED"; + DisconnectReason[DisconnectReason["DNS_FAILED"] = 2] = "DNS_FAILED"; + DisconnectReason[DisconnectReason["CONNECT_FAILURE"] = 3] = "CONNECT_FAILURE"; + DisconnectReason[DisconnectReason["CONNECTION_CLOSED"] = 4] = "CONNECTION_CLOSED"; + DisconnectReason[DisconnectReason["CONNECTION_FATAL_ERROR"] = 5] = "CONNECTION_FATAL_ERROR"; + DisconnectReason[DisconnectReason["CONNECTION_PING_TIMEOUT"] = 6] = "CONNECTION_PING_TIMEOUT"; + DisconnectReason[DisconnectReason["CLIENT_KICKED"] = 7] = "CLIENT_KICKED"; + DisconnectReason[DisconnectReason["CLIENT_BANNED"] = 8] = "CLIENT_BANNED"; + DisconnectReason[DisconnectReason["HANDSHAKE_FAILED"] = 9] = "HANDSHAKE_FAILED"; + DisconnectReason[DisconnectReason["HANDSHAKE_TEAMSPEAK_REQUIRED"] = 10] = "HANDSHAKE_TEAMSPEAK_REQUIRED"; + DisconnectReason[DisconnectReason["HANDSHAKE_BANNED"] = 11] = "HANDSHAKE_BANNED"; + DisconnectReason[DisconnectReason["SERVER_CLOSED"] = 12] = "SERVER_CLOSED"; + DisconnectReason[DisconnectReason["SERVER_REQUIRES_PASSWORD"] = 13] = "SERVER_REQUIRES_PASSWORD"; + DisconnectReason[DisconnectReason["SERVER_HOSTMESSAGE"] = 14] = "SERVER_HOSTMESSAGE"; + DisconnectReason[DisconnectReason["IDENTITY_TOO_LOW"] = 15] = "IDENTITY_TOO_LOW"; + DisconnectReason[DisconnectReason["UNKNOWN"] = 16] = "UNKNOWN"; +})(DisconnectReason || (DisconnectReason = {})); +var ConnectionState; +(function (ConnectionState) { + ConnectionState[ConnectionState["UNCONNECTED"] = 0] = "UNCONNECTED"; + ConnectionState[ConnectionState["CONNECTING"] = 1] = "CONNECTING"; + ConnectionState[ConnectionState["INITIALISING"] = 2] = "INITIALISING"; + ConnectionState[ConnectionState["CONNECTED"] = 3] = "CONNECTED"; + ConnectionState[ConnectionState["DISCONNECTING"] = 4] = "DISCONNECTING"; +})(ConnectionState || (ConnectionState = {})); +var ViewReasonId; +(function (ViewReasonId) { + ViewReasonId[ViewReasonId["VREASON_USER_ACTION"] = 0] = "VREASON_USER_ACTION"; + ViewReasonId[ViewReasonId["VREASON_MOVED"] = 1] = "VREASON_MOVED"; + ViewReasonId[ViewReasonId["VREASON_SYSTEM"] = 2] = "VREASON_SYSTEM"; + ViewReasonId[ViewReasonId["VREASON_TIMEOUT"] = 3] = "VREASON_TIMEOUT"; + ViewReasonId[ViewReasonId["VREASON_CHANNEL_KICK"] = 4] = "VREASON_CHANNEL_KICK"; + ViewReasonId[ViewReasonId["VREASON_SERVER_KICK"] = 5] = "VREASON_SERVER_KICK"; + ViewReasonId[ViewReasonId["VREASON_BAN"] = 6] = "VREASON_BAN"; + ViewReasonId[ViewReasonId["VREASON_SERVER_STOPPED"] = 7] = "VREASON_SERVER_STOPPED"; + ViewReasonId[ViewReasonId["VREASON_SERVER_LEFT"] = 8] = "VREASON_SERVER_LEFT"; + ViewReasonId[ViewReasonId["VREASON_CHANNEL_UPDATED"] = 9] = "VREASON_CHANNEL_UPDATED"; + ViewReasonId[ViewReasonId["VREASON_EDITED"] = 10] = "VREASON_EDITED"; + ViewReasonId[ViewReasonId["VREASON_SERVER_SHUTDOWN"] = 11] = "VREASON_SERVER_SHUTDOWN"; +})(ViewReasonId || (ViewReasonId = {})); +class ConnectionHandler { + constructor() { + this._clientId = 0; + this._reconnect_attempt = false; + this._connect_initialize_id = 1; + this.client_status = { + input_hardware: false, + input_muted: false, + output_muted: false, + away: false, + channel_subscribe_all: true, + queries_visible: false, + sound_playback_supported: undefined, + sound_record_supported: undefined, + channel_codec_encoding_supported: undefined, + channel_codec_decoding_supported: undefined + }; + this.invoke_resized_on_activate = false; + this.settings = new ServerSettings(); + this.log = new log.ServerLog(this); + this.channelTree = new ChannelTree(this); + this.side_bar = new chat.Frame(this); + this.sound = new sound.SoundManager(this); + this.hostbanner = new Hostbanner(this); + this.serverConnection = connection.spawn_server_connection(this); + this.serverConnection.onconnectionstatechanged = this.on_connection_state_changed.bind(this); + this.fileManager = new FileManager(this); + this.permissions = new PermissionManager(this); + this.side_bar.channel_conversations().initialize_needed_listener(); + this.groups = new GroupManager(this); + this._local_client = new LocalClientEntry(this); + /* initialize connection handler tab entry */ + { + this.tag_connection_handler = $.spawn("div").addClass("connection-container"); + $.spawn("div").addClass("server-icon icon client-server_green").appendTo(this.tag_connection_handler); + $.spawn("div").addClass("server-name").appendTo(this.tag_connection_handler); + $.spawn("div").addClass("button-close icon client-tab_close_button").appendTo(this.tag_connection_handler); + this.tag_connection_handler.on('click', event => { + if (event.isDefaultPrevented()) + return; + server_connections.set_active_connection_handler(this); + }); + this.tag_connection_handler.find(".button-close").on('click', event => { + server_connections.destroy_server_connection_handler(this); + event.preventDefault(); + }); + this.tab_set_name(_translations.ZUXymrm7 || (_translations.ZUXymrm7 = tr("Not connected"))); + } + } + tab_set_name(name) { + this.tag_connection_handler.toggleClass('cutoff-name', name.length > 30); + this.tag_connection_handler.find(".server-name").text(name); + } + setup() { } + startConnection(addr, profile, user_action, parameters) { + return __awaiter(this, void 0, void 0, function* () { + this.tab_set_name(_translations.T1UFxXzN || (_translations.T1UFxXzN = tr("Connecting"))); + this.cancel_reconnect(false); + this._reconnect_attempt = parameters.auto_reconnect_attempt || false; + if (this.serverConnection) + this.handleDisconnect(DisconnectReason.REQUESTED); + let server_address = { + host: "", + port: -1 + }; + { + let _v6_end = addr.indexOf(']'); + let idx = addr.lastIndexOf(':'); + if (idx != -1 && idx > _v6_end) { + server_address.port = parseInt(addr.substr(idx + 1)); + server_address.host = addr.substr(0, idx); + } + else { + server_address.host = addr; + server_address.port = 9987; + } + } + log.info(LogCategory.CLIENT, _translations.HS4rmli5 || (_translations.HS4rmli5 = tr("Start connection to %s:%d")), server_address.host, server_address.port); + this.log.log(log.server.Type.CONNECTION_BEGIN, { + address: { + server_hostname: server_address.host, + server_port: server_address.port + }, + client_nickname: parameters.nickname + }); + this.channelTree.initialiseHead(addr, server_address); + if (parameters.password && !parameters.password.hashed) { + try { + const password = yield helpers.hashPassword(parameters.password.password); + parameters.password = { + hashed: true, + password: password + }; + } + catch (error) { + log.error(LogCategory.CLIENT, _translations.kOLFxw_8 || (_translations.kOLFxw_8 = tr("Failed to hash connect password: %o")), error); + createErrorModal(_translations.zi0vuWHk || (_translations.zi0vuWHk = tr("Error while hashing password")), (_translations.slvMdYzx || (_translations.slvMdYzx = tr("Failed to hash server password!
"))) + error).open(); + } + } + if (parameters.password) { + connection_log.update_address_password({ + hostname: server_address.host, + port: server_address.port + }, parameters.password.password); + } + const original_address = { host: server_address.host, port: server_address.port }; + if (dns.supported() && !server_address.host.match(Modals.Regex.IP_V4) && !server_address.host.match(Modals.Regex.IP_V6)) { + const id = ++this._connect_initialize_id; + this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE, {}); + try { + const resolved = (yield dns.resolve_address(server_address, { timeout: 5000 })) || {}; + if (id != this._connect_initialize_id) + return; /* we're old */ + server_address.host = typeof (resolved.target_ip) === "string" ? resolved.target_ip : server_address.host; + server_address.port = typeof (resolved.target_port) === "number" ? resolved.target_port : server_address.port; + this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVED, { + address: { + server_port: server_address.port, + server_hostname: server_address.host + } + }); + } + catch (error) { + if (id != this._connect_initialize_id) + return; /* we're old */ + this.handleDisconnect(DisconnectReason.DNS_FAILED, error); + } + } + yield this.serverConnection.connect(server_address, new connection.HandshakeHandler(profile, parameters)); + setTimeout(() => { + const connected = this.serverConnection.connected(); + if (user_action && connected) { + connection_log.log_connect({ + hostname: original_address.host, + port: original_address.port + }); + } + }, 50); + }); + } + getClient() { return this._local_client; } + getClientId() { return this._clientId; } + set clientId(id) { + this._clientId = id; + this._local_client["_clientId"] = id; + } + get clientId() { + return this._clientId; + } + getServerConnection() { return this.serverConnection; } + /** + * LISTENER + */ + onConnected() { + log.info(LogCategory.CLIENT, _translations.n8ME6kUb || (_translations.n8ME6kUb = tr("Client connected"))); + this.permissions.requestPermissionList(); + if (this.groups.serverGroups.length == 0) + this.groups.requestGroups(); + this.initialize_server_settings(); + /* apply the server settings */ + if (this.client_status.channel_subscribe_all) + this.channelTree.subscribe_all_channels(); + else + this.channelTree.unsubscribe_all_channels(); + this.channelTree.toggle_server_queries(this.client_status.queries_visible); + this.sync_status_with_server(); + this.channelTree.server.updateProperties(); + /* + No need to update the voice stuff because as soon we see ourself we're doing it + this.update_voice_status(); + if(control_bar.current_connection_handler() === this) + control_bar.apply_server_voice_state(); + */ + } + initialize_server_settings() { + let update_control = false; + this.settings.setServer(this.channelTree.server.properties.virtualserver_unique_identifier); + { + const flag_subscribe = this.settings.server(Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL, true); + if (this.client_status.channel_subscribe_all != flag_subscribe) { + this.client_status.channel_subscribe_all = flag_subscribe; + update_control = true; + } + } + { + const flag_query = this.settings.server(Settings.KEY_CONTROL_SHOW_QUERIES, false); + if (this.client_status.queries_visible != flag_query) { + this.client_status.queries_visible = flag_query; + update_control = true; + } + } + if (update_control && server_connections.active_connection_handler() === this) { + control_bar.apply_server_state(); + } + } + get connected() { + return this.serverConnection && this.serverConnection.connected(); + } + generate_ssl_certificate_accept() { + const properties = { + connect_default: true, + connect_profile: this.serverConnection.handshake_handler().profile.id, + connect_address: this.serverConnection.remote_address().host + (this.serverConnection.remote_address().port !== 9987 ? ":" + this.serverConnection.remote_address().port : "") + }; + const build_url = (base, search, props) => { + const parameters = []; + for (const key of Object.keys(props)) + parameters.push(key + "=" + encodeURIComponent(props[key])); + let callback = base + search; /* don't use document.URL because it may contains a #! */ + if (!search) + callback += "?" + parameters.join("&"); + else + callback += "&" + parameters.join("&"); + return "https://" + this.serverConnection.remote_address().host + ":" + this.serverConnection.remote_address().port + "/?forward_url=" + encodeURIComponent(callback); + }; + /* generate the tag */ + const tag = $.spawn("a").text(_translations.YBuSiAT5 || (_translations.YBuSiAT5 = tr("here"))); + let pathname = document.location.pathname; + if (pathname.endsWith(".php")) + pathname = pathname.substring(0, pathname.lastIndexOf("/")); + if (bipc.supported()) { + tag.attr('href', "#"); + let popup; + tag.on('click', event => { + const features = { + status: "no", + location: "no", + toolbar: "no", + menubar: "no", + width: 600, + height: 400 + }; + if (popup) + popup.close(); + properties["certificate_callback"] = bipc.get_handler().register_certificate_accept_callback(() => { + log.info(LogCategory.GENERAL, _translations.MQsgbKfE || (_translations.MQsgbKfE = tr("Received notification that the certificate has been accepted! Attempting reconnect!"))); + if (this._certificate_modal) + this._certificate_modal.close(); + popup.close(); /* no need, but nicer */ + const profile = profiles.find_profile(properties.connect_profile) || profiles.default_profile(); + const cprops = this.reconnect_properties(profile); + this.startConnection(properties.connect_address, profile, true, cprops); + }); + const url = build_url(document.location.origin + pathname + "/popup/certaccept/", "", properties); + const features_string = [...Object.keys(features)].map(e => e + "=" + features[e]).reduce((a, b) => a + "," + b); + popup = window.open(url, "TeaWeb certificate accept", features_string); + try { + popup.focus(); + } + catch (e) { + log.warn(LogCategory.GENERAL, _translations.hTv3H6ip || (_translations.hTv3H6ip = tr("Certificate accept popup has been blocked. Trying a blank page and replacing href"))); + window.open(url, "TeaWeb certificate accept"); /* trying without features */ + tag.attr("target", "_blank"); + tag.attr("href", url); + tag.unbind('click'); + } + }); + } + else { + tag.attr('href', build_url(document.location.origin + pathname, document.location.search, properties)); + } + return tag; + } + handleDisconnect(type, data = {}) { + this._connect_initialize_id++; + this.tab_set_name(_translations.Wcp4Jl2M || (_translations.Wcp4Jl2M = tr("Not connected"))); + let auto_reconnect = false; + switch (type) { + case DisconnectReason.REQUESTED: + case DisconnectReason.SERVER_HOSTMESSAGE: /* already handled */ + break; + case DisconnectReason.HANDLER_DESTROYED: + if (data) { + this.sound.play(Sound.CONNECTION_DISCONNECTED); + this.log.log(log.server.Type.DISCONNECTED, {}); + } + break; + case DisconnectReason.DNS_FAILED: + log.error(LogCategory.CLIENT, _translations.RjYBRqhz || (_translations.RjYBRqhz = tr("Failed to resolve hostname: %o")), data); + this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, { + message: data + }); + this.sound.play(Sound.CONNECTION_REFUSED); + break; + case DisconnectReason.CONNECT_FAILURE: + if (this._reconnect_attempt) { + auto_reconnect = true; + this.log.log(log.server.Type.CONNECTION_FAILED, {}); + break; + } + if (data) + log.error(LogCategory.CLIENT, _translations.TTZmhO6X || (_translations.TTZmhO6X = tr("Could not connect to remote host! Extra data: %o")), data); + else + log.error(LogCategory.CLIENT, _translations.UvN4ZYoz || (_translations.UvN4ZYoz = tr("Could not connect to remote host!")), data); + if (native_client) { + createErrorModal(_translations.yKOp1KDQ || (_translations.yKOp1KDQ = tr("Could not connect")), _translations.BAtStX1r || (_translations.BAtStX1r = tr("Could not connect to remote host (Connection refused)"))).open(); + } + else { + const error_message_format = "Could not connect to remote host (Connection refused)\n" + + "If you're sure that the remote host is up, than you may not allow unsigned certificates.\n" + + "Click {0} to accept the remote certificate"; + this._certificate_modal = createErrorModal(_translations.EaTMLkfC || (_translations.EaTMLkfC = tr("Could not connect")), MessageHelper.formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept())); + this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined); + this._certificate_modal.open(); + } + this.sound.play(Sound.CONNECTION_REFUSED); + break; + case DisconnectReason.HANDSHAKE_FAILED: + //TODO sound + log.error(LogCategory.CLIENT, _translations.YWdTGt0C || (_translations.YWdTGt0C = tr("Failed to process handshake: %o")), data); + createErrorModal(_translations.os_PbxTh || (_translations.os_PbxTh = tr("Could not connect")), (_translations.mWEfrIGh || (_translations.mWEfrIGh = tr("Failed to process handshake: "))) + data).open(); + break; + case DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED: + createErrorModal(_translations.sKz9M0J1 || (_translations.sKz9M0J1 = tr("Target server is a TeamSpeak server")), MessageHelper.formatMessage(_translations.QmW2FhnQ || (_translations.QmW2FhnQ = tr("The target server is a TeamSpeak 3 server!{:br:}Only TeamSpeak 3 based identities are able to connect.{:br:}Please select another profile or change the identify type.")))).open(); + this.sound.play(Sound.CONNECTION_DISCONNECTED); + auto_reconnect = false; + break; + case DisconnectReason.IDENTITY_TOO_LOW: + createErrorModal(_translations.c82V0qqg || (_translations.c82V0qqg = tr("Identity level is too low")), MessageHelper.formatMessage(_translations.vIrDdie6 || (_translations.vIrDdie6 = tr("You've been disconnected, because your Identity level is too low.{:br:}You need at least a level of {0}")), data["extra_message"])).open(); + this.sound.play(Sound.CONNECTION_DISCONNECTED); + auto_reconnect = false; + break; + case DisconnectReason.CONNECTION_CLOSED: + log.error(LogCategory.CLIENT, _translations.DPauFd4a || (_translations.DPauFd4a = tr("Lost connection to remote server!"))); + if (!this._reconnect_attempt) { + createErrorModal(_translations.tPvfpJri || (_translations.tPvfpJri = tr("Connection closed")), _translations.yAUGk0u6 || (_translations.yAUGk0u6 = tr("The connection was closed by remote host"))).open(); + } + this.sound.play(Sound.CONNECTION_DISCONNECTED); + auto_reconnect = true; + break; + case DisconnectReason.CONNECTION_PING_TIMEOUT: + log.error(LogCategory.CLIENT, _translations.XM3HFZtb || (_translations.XM3HFZtb = tr("Connection ping timeout"))); + this.sound.play(Sound.CONNECTION_DISCONNECTED_TIMEOUT); + createErrorModal(_translations.SPp3hxky || (_translations.SPp3hxky = tr("Connection lost")), _translations.Ez6pR12l || (_translations.Ez6pR12l = tr("Lost connection to remote host (Ping timeout)
Even possible?"))).open(); + break; + case DisconnectReason.SERVER_CLOSED: + this.log.log(log.server.Type.SERVER_CLOSED, { message: data.reasonmsg }); + createErrorModal(_translations.oeX7ep3Y || (_translations.oeX7ep3Y = tr("Server closed")), "The server is closed.
" + //TODO tr + "Reason: " + data.reasonmsg).open(); + this.sound.play(Sound.CONNECTION_DISCONNECTED); + auto_reconnect = true; + break; + case DisconnectReason.SERVER_REQUIRES_PASSWORD: + this.log.log(log.server.Type.SERVER_REQUIRES_PASSWORD, {}); + createInputModal(_translations.qlxKgog8 || (_translations.qlxKgog8 = tr("Server password")), _translations.QTjxbWyV || (_translations.QTjxbWyV = tr("Enter server password:")), password => password.length != 0, password => { + if (!(typeof password === "string")) + return; + const profile = this.serverConnection.handshake_handler().profile; + const cprops = this.reconnect_properties(profile); + cprops.password = { password: password, hashed: false }; + connection_log.update_address_info({ + port: this.channelTree.server.remote_address.port, + hostname: this.channelTree.server.remote_address.host + }, { + flag_password: true + }); + this.startConnection(this.channelTree.server.remote_address.host + ":" + this.channelTree.server.remote_address.port, profile, false, cprops); + }).open(); + break; + case DisconnectReason.CLIENT_KICKED: + const have_invoker = typeof (data["invokerid"]) !== "undefined" && parseInt(data["invokerid"]) !== 0; + const modal = createErrorModal(_translations.GL3SU8Zv || (_translations.GL3SU8Zv = tr("You've been kicked")), MessageHelper.formatMessage(have_invoker ? _translations.L6t34lZs || (_translations.L6t34lZs = tr("You've been kicked from the server by {0}:{:br:}{1}")) : _translations.zo49e7He || (_translations.zo49e7He = tr("You've been kicked from the server:{:br:}{1}")), have_invoker ? + htmltags.generate_client_object({ client_id: parseInt(data["invokerid"]), client_unique_id: data["invokeruid"], client_name: data["invokername"] }) : + "", data["extra_message"] || data["reasonmsg"] || "")); + modal.htmlTag.find(".modal-body").addClass("modal-disconnect-kick"); + modal.open(); + this.sound.play(Sound.SERVER_KICKED); + auto_reconnect = false; + break; + case DisconnectReason.HANDSHAKE_BANNED: + //Reason message already printed because of the command error handling + this.sound.play(Sound.CONNECTION_BANNED); + break; + case DisconnectReason.CLIENT_BANNED: + this.log.log(log.server.Type.SERVER_BANNED, { + invoker: { + client_name: data["invokername"], + client_id: parseInt(data["invokerid"]), + client_unique_id: data["invokeruid"] + }, + message: data["reasonmsg"], + time: parseInt(data["time"]) + }); + this.sound.play(Sound.CONNECTION_BANNED); + break; + default: + log.error(LogCategory.CLIENT, _translations.eTgdGWXl || (_translations.eTgdGWXl = tr("Got uncaught disconnect!"))); + log.error(LogCategory.CLIENT, _translations.m_2kmrli || (_translations.m_2kmrli = tr("Type: %o Data: %o")), type, data); + break; + } + this.channelTree.unregisterClient(this._local_client); /* if we dont unregister our client here the client will be destroyed */ + this.channelTree.reset(); + if (this.serverConnection) + this.serverConnection.disconnect(); + if (control_bar.current_connection_handler() == this) + control_bar.update_connection_state(); + this.side_bar.private_conversations().clear_client_ids(); + this.hostbanner.update(); + if (auto_reconnect) { + if (!this.serverConnection) { + log.info(LogCategory.NETWORKING, _translations.gthobtoh || (_translations.gthobtoh = tr("Allowed to auto reconnect but cant reconnect because we dont have any information left..."))); + return; + } + this.log.log(log.server.Type.RECONNECT_SCHEDULED, { timeout: 5000 }); + log.info(LogCategory.NETWORKING, _translations.OfYIEKF3 || (_translations.OfYIEKF3 = tr("Allowed to auto reconnect. Reconnecting in 5000ms"))); + const server_address = this.serverConnection.remote_address(); + const profile = this.serverConnection.handshake_handler().profile; + this._reconnect_timer = setTimeout(() => { + this._reconnect_timer = undefined; + this.log.log(log.server.Type.RECONNECT_EXECUTE, {}); + log.info(LogCategory.NETWORKING, _translations.mgD_hUkm || (_translations.mgD_hUkm = tr("Reconnecting..."))); + this.startConnection(server_address.host + ":" + server_address.port, profile, false, Object.assign(this.reconnect_properties(profile), { auto_reconnect_attempt: true })); + }, 5000); + } + } + cancel_reconnect(log_event) { + if (this._reconnect_timer) { + if (log_event) + this.log.log(log.server.Type.RECONNECT_CANCELED, {}); + clearTimeout(this._reconnect_timer); + this._reconnect_timer = undefined; + } + } + on_connection_state_changed() { + if (control_bar.current_connection_handler() == this) + control_bar.update_connection_state(); + } + update_voice_status(targetChannel) { + if (!this._local_client) + return; /* we've been destroyed */ + targetChannel = targetChannel || this.getClient().currentChannel(); + const vconnection = this.serverConnection.voice_connection(); + const basic_voice_support = this.serverConnection.support_voice() && vconnection.connected() && targetChannel; + const support_record = basic_voice_support && (!targetChannel || vconnection.encoding_supported(targetChannel.properties.channel_codec)); + const support_playback = basic_voice_support && (!targetChannel || vconnection.decoding_supported(targetChannel.properties.channel_codec)); + const property_update = { + client_input_muted: this.client_status.input_muted, + client_output_muted: this.client_status.output_muted + }; + if (support_record && basic_voice_support) + vconnection.set_encoder_codec(targetChannel.properties.channel_codec); + if (!this.serverConnection.support_voice() || !this.serverConnection.connected() || !vconnection.connected()) { + property_update["client_input_hardware"] = false; + property_update["client_output_hardware"] = false; + this.client_status.input_hardware = true; /* IDK if we have input hardware or not, but it dosn't matter at all so */ + /* no icons are shown so no update at all */ + } + else { + const audio_source = vconnection.voice_recorder(); + const recording_supported = typeof (audio_source) !== "undefined" && audio_source.record_supported && (!targetChannel || vconnection.encoding_supported(targetChannel.properties.channel_codec)); + const playback_supported = !targetChannel || vconnection.decoding_supported(targetChannel.properties.channel_codec); + property_update["client_input_hardware"] = recording_supported; + property_update["client_output_hardware"] = playback_supported; + this.client_status.input_hardware = recording_supported; + /* update icons */ + const client_properties = this.getClient().properties; + for (const key of Object.keys(property_update)) { + if (client_properties[key] === property_update[key]) + delete property_update[key]; + } + if (Object.keys(property_update).length > 0) { + this.serverConnection.send_command("clientupdate", property_update).catch(error => { + log.warn(LogCategory.GENERAL, _translations.E6Pxj7O_ || (_translations.E6Pxj7O_ = tr("Failed to update client audio hardware properties. Error: %o")), error); + this.log.log(log.server.Type.ERROR_CUSTOM, { message: _translations.myFdKome || (_translations.myFdKome = tr("Failed to update audio hardware properties.")) }); + /* Update these properties anyways (for case the server fails to handle the command) */ + const updates = []; + for (const key of Object.keys(property_update)) + updates.push({ key: key, value: (property_update[key]) + "" }); + this.getClient().updateVariables(...updates); + }); + } + } + if (targetChannel && basic_voice_support) { + const encoding_supported = vconnection && vconnection.encoding_supported(targetChannel.properties.channel_codec); + const decoding_supported = vconnection && vconnection.decoding_supported(targetChannel.properties.channel_codec); + if (this.client_status.channel_codec_decoding_supported !== decoding_supported || this.client_status.channel_codec_encoding_supported !== encoding_supported) { + this.client_status.channel_codec_decoding_supported = decoding_supported; + this.client_status.channel_codec_encoding_supported = encoding_supported; + let message; + if (!encoding_supported && !decoding_supported) + message = _translations.yhxSmGy4 || (_translations.yhxSmGy4 = tr("This channel has an unsupported codec.
You cant speak or listen to anybody within this channel!")); + else if (!encoding_supported) + message = _translations.FhG1RYUu || (_translations.FhG1RYUu = tr("This channel has an unsupported codec.
You cant speak within this channel!")); + else if (!decoding_supported) + message = _translations.tl9gg03_ || (_translations.tl9gg03_ = tr("This channel has an unsupported codec.
You listen to anybody within this channel!")); /* implies speaking does not work as well */ + if (message) + createErrorModal(_translations.H8fSS_0Z || (_translations.H8fSS_0Z = tr("Channel codec unsupported")), message).open(); + } + } + this.client_status = this.client_status || {}; + this.client_status.sound_record_supported = support_record; + this.client_status.sound_playback_supported = support_playback; + if (vconnection && vconnection.voice_recorder() && vconnection.voice_recorder().record_supported) { + const active = !this.client_status.input_muted && !this.client_status.output_muted; + /* No need to start the microphone when we're not even connected */ + const input = vconnection.voice_recorder().input; + if (input) { + if (active && this.serverConnection.connected()) { + if (input.current_state() === audio.recorder.InputState.PAUSED) { + input.start().then(result => { + if (result != audio.recorder.InputStartResult.EOK) + throw result; + }).catch(error => { + log.warn(LogCategory.VOICE, _translations.IU0hK4NF || (_translations.IU0hK4NF = tr("Failed to start microphone input (%s).")), error); + if (Date.now() - (this._last_record_error_popup || 0) > 10 * 1000) { + this._last_record_error_popup = Date.now(); + createErrorModal(_translations.Ak0MoTq5 || (_translations.Ak0MoTq5 = tr("Failed to start recording")), MessageHelper.formatMessage(_translations.CdNixUn2 || (_translations.CdNixUn2 = tr("Microphone start failed.{:br:}Error: {}")), error)).open(); + } + }); + } + } + else { + input.stop(); + } + } + } + if (control_bar.current_connection_handler() === this) + control_bar.apply_server_voice_state(); + } + sync_status_with_server() { + if (this.serverConnection.connected()) + this.serverConnection.send_command("clientupdate", { + client_input_muted: this.client_status.input_muted, + client_output_muted: this.client_status.output_muted, + client_away: typeof (this.client_status.away) === "string" || this.client_status.away, + client_away_message: typeof (this.client_status.away) === "string" ? this.client_status.away : "", + client_input_hardware: this.client_status.sound_record_supported && this.client_status.input_hardware, + client_output_hardware: this.client_status.sound_playback_supported + }).catch(error => { + log.warn(LogCategory.GENERAL, _translations.ifNLFsmx || (_translations.ifNLFsmx = tr("Failed to sync handler state with server. Error: %o")), error); + this.log.log(log.server.Type.ERROR_CUSTOM, { message: _translations.ZAcARduu || (_translations.ZAcARduu = tr("Failed to sync handler state with server.")) }); + }); + } + set_away_status(state) { + if (this.client_status.away === state) + return; + if (state) { + this.sound.play(Sound.AWAY_ACTIVATED); + } + else { + this.sound.play(Sound.AWAY_DEACTIVATED); + } + this.client_status.away = state; + this.serverConnection.send_command("clientupdate", { + client_away: typeof (this.client_status.away) === "string" || this.client_status.away, + client_away_message: typeof (this.client_status.away) === "string" ? this.client_status.away : "", + }).catch(error => { + log.warn(LogCategory.GENERAL, _translations.j356bpkL || (_translations.j356bpkL = tr("Failed to update away status. Error: %o")), error); + this.log.log(log.server.Type.ERROR_CUSTOM, { message: _translations.Hquv820o || (_translations.Hquv820o = tr("Failed to update away status.")) }); + }); + control_bar.update_button_away(); + } + resize_elements() { + this.channelTree.handle_resized(); + this.invoke_resized_on_activate = false; + } + acquire_recorder(voice_recoder, update_control_bar) { + const vconnection = this.serverConnection.voice_connection(); + (vconnection ? vconnection.acquire_voice_recorder(voice_recoder) : Promise.resolve()).catch(error => { + log.warn(LogCategory.VOICE, _translations.Nhi8Zrg7 || (_translations.Nhi8Zrg7 = tr("Failed to acquire recorder (%o)")), error); + }).then(() => { + this.update_voice_status(undefined); + }); + } + reconnect_properties(profile) { + const name = (this.getClient() ? this.getClient().clientNickName() : "") || + (this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") || + settings.static_global(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) || + "Another TeaSpeak user"; + const channel = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().channelId : 0) || + (this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {}).target : ""); + const channel_password = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().cached_password() : "") || + (this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {}).password : ""); + return { + channel: channel ? { target: "/" + channel, password: channel_password } : undefined, + nickname: name, + password: this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.password : undefined + }; + } + update_avatar() { + Modals.spawnAvatarUpload(data => { + if (typeof (data) === "undefined") + return; + if (data === null) { + log.info(LogCategory.CLIENT, _translations.kwfqGPH6 || (_translations.kwfqGPH6 = tr("Deleting existing avatar"))); + this.serverConnection.send_command('ftdeletefile', { + name: "/avatar_", + path: "", + cid: 0 + }).then(() => { + createInfoModal(_translations.N3fwN_XS || (_translations.N3fwN_XS = tr("Avatar deleted")), _translations.dWko40aF || (_translations.dWko40aF = tr("Avatar successfully deleted"))).open(); + }).catch(error => { + log.error(LogCategory.GENERAL, _translations._o7eTDux || (_translations._o7eTDux = tr("Failed to reset avatar flag: %o")), error); + let message; + if (error instanceof CommandResult) + message = MessageHelper.formatMessage(_translations.q6MV9BJ6 || (_translations.q6MV9BJ6 = tr("Failed to delete avatar.{:br:}Error: {0}")), error.extra_message || error.message); + if (!message) + message = MessageHelper.formatMessage(_translations.l2z4u5iq || (_translations.l2z4u5iq = tr("Failed to delete avatar.{:br:}Lookup the console for more details"))); + createErrorModal(_translations.SERPbLsV || (_translations.SERPbLsV = tr("Failed to delete avatar")), message).open(); + return; + }); + } + else { + log.info(LogCategory.CLIENT, _translations.Uwwz13se || (_translations.Uwwz13se = tr("Uploading new avatar"))); + (() => __awaiter(this, void 0, void 0, function* () { + let key; + try { + key = yield this.fileManager.upload_file({ + size: data.byteLength, + path: '', + name: '/avatar', + overwrite: true, + channel: undefined, + channel_password: undefined + }); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.p6ktJjDH || (_translations.p6ktJjDH = tr("Failed to initialize avatar upload: %o")), error); + let message; + if (error instanceof CommandResult) { + //TODO: Resolve permission name + //i_client_max_avatar_filesize + if (error.id == ErrorID.PERMISSION_ERROR) { + message = MessageHelper.formatMessage(_translations.DpzOU8kx || (_translations.DpzOU8kx = tr("Failed to initialize avatar upload.{:br:}Missing permission {0}")), error["failed_permid"]); + } + else { + message = MessageHelper.formatMessage(_translations.GSHnaSaC || (_translations.GSHnaSaC = tr("Failed to initialize avatar upload.{:br:}Error: {0}")), error.extra_message || error.message); + } + } + if (!message) + message = MessageHelper.formatMessage(_translations.HuAordM2 || (_translations.HuAordM2 = tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"))); + createErrorModal(_translations.SqHGgSbh || (_translations.SqHGgSbh = tr("Failed to upload avatar")), message).open(); + return; + } + try { + yield transfer.spawn_upload_transfer(key).put_data(data); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.cCwgYPgQ || (_translations.cCwgYPgQ = tr("Failed to upload avatar: %o")), error); + let message; + if (typeof (error) === "string") + message = MessageHelper.formatMessage(_translations.Jkzl_W58 || (_translations.Jkzl_W58 = tr("Failed to upload avatar.{:br:}Error: {0}")), error); + if (!message) + message = MessageHelper.formatMessage(_translations.HfLNjuHd || (_translations.HfLNjuHd = tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details"))); + createErrorModal(_translations.b_WF3dmV || (_translations.b_WF3dmV = tr("Failed to upload avatar")), message).open(); + return; + } + try { + yield this.serverConnection.send_command('clientupdate', { + client_flag_avatar: guid() + }); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.PkrE3ZLe || (_translations.PkrE3ZLe = tr("Failed to update avatar flag: %o")), error); + let message; + if (error instanceof CommandResult) + message = MessageHelper.formatMessage(_translations.Yw4Xjm9C || (_translations.Yw4Xjm9C = tr("Failed to update avatar flag.{:br:}Error: {0}")), error.extra_message || error.message); + if (!message) + message = MessageHelper.formatMessage(_translations.ZNppOyOZ || (_translations.ZNppOyOZ = tr("Failed to update avatar flag.{:br:}Lookup the console for more details"))); + createErrorModal(_translations.n1Io657i || (_translations.n1Io657i = tr("Failed to set avatar")), message).open(); + return; + } + createInfoModal(_translations.dVyA2a02 || (_translations.dVyA2a02 = tr("Avatar successfully uploaded")), _translations.Wrvy2Obl || (_translations.Wrvy2Obl = tr("Your avatar has been uploaded successfully!"))).open(); + }))(); + } + }); + } + destroy() { + this.cancel_reconnect(true); + this.tag_connection_handler && this.tag_connection_handler.remove(); + this.tag_connection_handler = undefined; + this.hostbanner && this.hostbanner.destroy(); + this.hostbanner = undefined; + this._local_client && this._local_client.destroy(); + this._local_client = undefined; + this.channelTree && this.channelTree.destroy(); + this.channelTree = undefined; + this.side_bar && this.side_bar.destroy(); + this.side_bar = undefined; + this.log && this.log.destroy(); + this.log = undefined; + this.permissions && this.permissions.destroy(); + this.permissions = undefined; + this.groups && this.groups.destroy(); + this.groups = undefined; + this.fileManager && this.fileManager.destroy(); + this.fileManager = undefined; + this.settings && this.settings.destroy(); + this.settings = undefined; + if (this.serverConnection) { + this.serverConnection.onconnectionstatechanged = undefined; + connection.destroy_server_connection(this.serverConnection); + } + this.serverConnection = undefined; + this.sound = undefined; + this._local_client = undefined; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5b0b0e729d8a6355fc16c9599bb8ac0e339c71eefff49f95cfba19ce44c9b4ae"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5b0b0e729d8a6355fc16c9599bb8ac0e339c71eefff49f95cfba19ce44c9b4ae"] = "5b0b0e729d8a6355fc16c9599bb8ac0e339c71eefff49f95cfba19ce44c9b4ae"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "SlnUPyvk", path: "D:/TeaSpeak/web/shared/js/MessageFormatter.ts (112,31)" }, { name: "nQeTIPlF", path: "D:/TeaSpeak/web/shared/js/MessageFormatter.ts (119,31)" }, { name: "tOI3DTZ8", path: "D:/TeaSpeak/web/shared/js/MessageFormatter.ts (124,31)" }, { name: "H9Jdm9OF", path: "D:/TeaSpeak/web/shared/js/MessageFormatter.ts (148,27)" }, { name: "qq3oBpng", path: "D:/TeaSpeak/web/shared/js/MessageFormatter.ts (153,27)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var messages; +(function (messages) { + var formatter; + (function (formatter) { + let bbcode; + (function (bbcode) { + const sanitizer_escaped = (key) => "[-- sescaped: " + key + " --]"; + const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/; + const sanitizer_escaped_map = {}; + const yt_url_regex = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; + function format(message, fsettings) { + fsettings = fsettings || {}; + single_url_parse: if (fsettings.is_chat_message) { + /* try if its only one url */ + const raw_url = message.replace(/\[url(=\S+)?](\S+)\[\/url]/, "$2"); + let url; + try { + url = new URL(raw_url); + } + catch (error) { + break single_url_parse; + } + single_url_yt: { + const result = raw_url.match(yt_url_regex); + if (!result) + break single_url_yt; + return format("[yt]https://www.youtube.com/watch?v=" + result[5] + "[/yt]"); + } + single_url_image: { + const ext_index = url.pathname.lastIndexOf("."); + if (ext_index == -1) + break single_url_image; + const ext_name = url.pathname.substr(ext_index + 1).toLowerCase(); + if ([ + "jpeg", "jpg", + "png", "bmp", "gif", + "tiff", "pdf", "svg" + ].findIndex(e => e === ext_name) == -1) + break single_url_image; + return format("[img]" + message + "[/img]"); + } + } + const result = xbbcode.parse(message, { + tag_whitelist: [ + "b", "big", + "i", "italic", + "u", "underlined", + "s", "strikethrough", + "color", + "url", + "code", + "i-code", "icode", + "sub", "sup", + "size", + "hr", "br", + "ul", "ol", "list", + "li", + "table", + "tr", "td", "th", + "yt", "youtube", + "img" + ] + }); + let html = result.build_html(); + if (typeof (window.twemoji) !== "undefined" && settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)) + html = twemoji.parse(html); + const container = $.spawn("div"); + let sanitized = DOMPurify.sanitize(html, { + ADD_ATTR: [ + "x-highlight-type", + "x-code-type", + "x-image-url" + ] + }); + sanitized = sanitized.replace(sanitizer_escaped_regex, data => { + const uid = data.match(sanitizer_escaped_regex)[1]; + const value = sanitizer_escaped_map[uid]; + if (!value) + return data; + delete sanitizer_escaped_map[uid]; + return value; + }); + container[0].innerHTML = sanitized; + container.find("a") + .attr('target', "_blank") + .on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + const url = $(event.target).attr("href"); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + callback: () => { + const win = window.open(url, '_blank'); + win.focus(); + }, + name: _translations.SlnUPyvk || (_translations.SlnUPyvk = tr("Open URL")), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-browse-addon-online" + }, { + callback: () => { + //TODO + }, + name: _translations.nQeTIPlF || (_translations.nQeTIPlF = tr("Open URL in Browser")), + type: contextmenu.MenuEntryType.ENTRY, + visible: !app.is_web() && false // Currently not possible + }, contextmenu.Entry.HR(), { + callback: () => copy_to_clipboard(url), + name: _translations.tOI3DTZ8 || (_translations.tOI3DTZ8 = tr("Copy URL to clipboard")), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-copy" + }); + }); + return [container.contents()]; + //return result.root_tag.content.map(e => e.build_html()).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)); + } + bbcode.format = format; + function load_image(entry) { + const url = decodeURIComponent(entry.getAttribute("x-image-url") || ""); + const proxy_url = "https://images.weserv.nl/?url=" + encodeURIComponent(url); + entry.onload = undefined; + entry.src = proxy_url; + const parent = $(entry.parentElement); + parent.on('contextmenu', event => { + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + callback: () => { + const win = window.open(url, '_blank'); + win.focus(); + }, + name: _translations.H9Jdm9OF || (_translations.H9Jdm9OF = tr("Open image in browser")), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-browse-addon-online" + }, contextmenu.Entry.HR(), { + callback: () => copy_to_clipboard(url), + name: _translations.qq3oBpng || (_translations.qq3oBpng = tr("Copy image URL to clipboard")), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-copy" + }); + }); + parent.css("cursor", "pointer").on('click', event => image_preview.preview_image(proxy_url, url)); + } + bbcode.load_image = load_image; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "XBBCode code tag init", + function: () => __awaiter(this, void 0, void 0, function* () { + /* override default parser */ + xbbcode.register.register_parser({ + tag: ["code", "icode", "i-code"], + content_tags_whitelist: [], + build_html(layer) { + const klass = layer.tag_normalized != 'code' ? "tag-hljs-inline-code" : "tag-hljs-code"; + const language = (layer.options || "").replace("\"", "'").toLowerCase(); + /* remove heading empty lines */ + let text = layer.content.map(e => e.build_text()) + .reduce((a, b) => a.length == 0 && b.replace(/[ \n\r\t]+/g, "").length == 0 ? "" : a + b, "") + .replace(/^([ \n\r\t]*)(?=\n)+/g, ""); + if (text.startsWith("\r") || text.startsWith("\n")) + text = text.substr(1); + let result; + if (window.hljs.getLanguage(language)) + result = window.hljs.highlight(language, text, true); + else + result = window.hljs.highlightAuto(text); + let html = '
';
+                            html += '';
+                            html += result.value;
+                            return html + "
"; + } + }); + /* override the yt parser */ + const original_parser = xbbcode.register.find_parser("yt"); + if (original_parser) + xbbcode.register.register_parser({ + tag: ["yt", "youtube"], + build_html(layer) { + const result = original_parser.build_html(layer); + if (!result.startsWith(""; + return sanitizer_escaped(uid); + } + }); + /* the image parse & displayer */ + xbbcode.register.register_parser({ + tag: ["img", "image"], + build_html(layer) { + const uid = guid(); + const fallback_value = "[img]" + layer.build_text() + "[/img]"; + let target; + let content = layer.content.map(e => e.build_text()).join(""); + if (!layer.options) { + target = content; + } + else + target = layer.options; + let url; + try { + url = new URL(target); + if (!url.hostname) + throw ""; + } + catch (error) { + return fallback_value; + } + sanitizer_escaped_map[uid] = "
"; + return sanitizer_escaped(uid); + } + }); + }), + priority: 10 + }); + })(bbcode = formatter.bbcode || (formatter.bbcode = {})); + function sanitize_text(text) { + return $(DOMPurify.sanitize("" + text + "", { + ADD_ATTR: [ + "x-highlight-type", + "x-code-type", + "x-image-url" + ] + })).text(); + } + formatter.sanitize_text = sanitize_text; + })(formatter = messages.formatter || (messages.formatter = {})); +})(messages || (messages = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["29ef4d07eca5d64f42ebf5dc62f9f51bdf2f1fda69c0d90abc439a47f29609ac"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["29ef4d07eca5d64f42ebf5dc62f9f51bdf2f1fda69c0d90abc439a47f29609ac"] = "29ef4d07eca5d64f42ebf5dc62f9f51bdf2f1fda69c0d90abc439a47f29609ac"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var dns; +(function (dns) { + dns.default_options = { + timeout: 5000, + allow_cache: true, + max_depth: 5 + }; +})(dns || (dns = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["9dc0537f39bf16c58b80536db4cb74fe25af0f9cb2e1072557c5d1e08049a414"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["9dc0537f39bf16c58b80536db4cb74fe25af0f9cb2e1072557c5d1e08049a414"] = "9dc0537f39bf16c58b80536db4cb74fe25af0f9cb2e1072557c5d1e08049a414"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var events; +(function (events_1) { + class SingletonEvent { + constructor() { + this.type = "singletone-instance"; + } + } + SingletonEvent.instance = new SingletonEvent(); + events_1.SingletonEvent = SingletonEvent; + class Registry { + constructor() { + this.handler = {}; + this.connections = {}; + this.debug_prefix = undefined; + this.registry_uuid = "evreg_data_" + guid(); + } + enable_debug(prefix) { this.debug_prefix = prefix || "---"; } + disable_debug() { this.debug_prefix = undefined; } + on(events, handler) { + if (!Array.isArray(events)) + events = [events]; + handler[this.registry_uuid] = { + singleshot: false + }; + for (const event of events) { + const handlers = this.handler[event] || (this.handler[event] = []); + handlers.push(handler); + } + } + one(events, handler) { + if (!Array.isArray(events)) + events = [events]; + for (const event of events) { + const handlers = this.handler[event] || (this.handler[event] = []); + handler[this.registry_uuid] = { singleshot: true }; + handlers.push(handler); + } + } + off(handler_or_events, handler) { + if (typeof handler_or_events === "function") { + for (const key of Object.keys(this.handler)) + this.handler[key].remove(handler_or_events); + } + else { + if (!Array.isArray(handler_or_events)) + handler_or_events = [handler_or_events]; + for (const event of handler_or_events) { + const handlers = this.handler[event]; + if (handlers) + handlers.remove(handler); + } + } + } + connect(event, target) { + (this.connections[event] || (this.connections[event] = [])).push(target); + } + disconnect(event, target) { + (this.connections[event] || []).remove(target); + } + disconnect_all(target) { + for (const event of Object.keys(this.connections)) + this.connections[event].remove(target); + } + fire(event_type, data) { + if (this.debug_prefix) + console.log("[%s] Trigger event: %s", this.debug_prefix, event_type); + const event = Object.assign(typeof data === "undefined" ? SingletonEvent.instance : data, { + type: event_type, + as: function () { return this; } + }); + for (const handler of (this.handler[event_type] || [])) { + handler(event); + const reg_data = handler[this.registry_uuid]; + if (typeof reg_data === "object" && reg_data.singleshot) + this.handler[event_type].remove(handler); + } + for (const evhandler of (this.connections[event_type] || [])) + evhandler.fire(event_type, event); + } + fire_async(event_type, data) { + setTimeout(() => this.fire(event_type, data)); + } + destory() { + this.handler = {}; + } + } + events_1.Registry = Registry; +})(events || (events = {})); +const eclient = new events.Registry(); +const emusic = new events.Registry(); +eclient.connect("playlist_song_loaded", emusic); +eclient.connect("playlist_song_loaded", emusic); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["2d91d2e2b1835bcadc3f37922502a465b7f2d371df52f15a79138ae94be7235b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["2d91d2e2b1835bcadc3f37922502a465b7f2d371df52f15a79138ae94be7235b"] = "2d91d2e2b1835bcadc3f37922502a465b7f2d371df52f15a79138ae94be7235b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "MPJIRlqs", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (81,51)" }, { name: "NDTF2Yzi", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (199,58)" }, { name: "ZI_tZyBd", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (199,72)" }, { name: "t4VTrEXu", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (205,58)" }, { name: "ybJHagZN", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (205,71)" }, { name: "LDpkmc_N", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (211,58)" }, { name: "_LzkhuHI", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (211,72)" }, { name: "cute2byM", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (217,58)" }, { name: "Yse9w1Aj", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (217,74)" }, { name: "sjq8XWYY", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (223,58)" }, { name: "LFkoylsJ", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat.ts (223,74)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var ChatType; +(function (ChatType) { + ChatType[ChatType["GENERAL"] = 0] = "GENERAL"; + ChatType[ChatType["SERVER"] = 1] = "SERVER"; + ChatType[ChatType["CHANNEL"] = 2] = "CHANNEL"; + ChatType[ChatType["CLIENT"] = 3] = "CLIENT"; +})(ChatType || (ChatType = {})); +var MessageHelper; +(function (MessageHelper) { + function htmlEscape(message) { + const div = document.createElement('div'); + div.innerText = message; + message = div.innerHTML; + return message.replace(/ /g, ' ').split(/
/); + } + MessageHelper.htmlEscape = htmlEscape; + function formatElement(object, escape_html = true) { + if ($.isArray(object)) { + let result = []; + for (let element of object) + result.push(...formatElement(element, escape_html)); + return result; + } + else if (typeof (object) == "string") { + if (object.length == 0) + return []; + return escape_html ? + htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)) : + [$.spawn("div").css("display", "inline-block").html(object)]; + } + else if (typeof (object) === "object") { + if (object instanceof $) + return [object]; + return formatElement(""); + } + else if (typeof (object) === "function") + return formatElement(object(), escape_html); + else if (typeof (object) === "undefined") + return formatElement(""); + else if (typeof (object) === "number") + return [$.spawn("a").text(object)]; + return formatElement(""); + } + MessageHelper.formatElement = formatElement; + function formatMessage(pattern, ...objects) { + let begin = 0, found = 0; + let result = []; + do { + found = pattern.indexOf('{', found); + if (found == -1 || pattern.length <= found + 1) { + result.push(...formatElement(pattern.substr(begin))); + break; + } + if (found > 0 && pattern[found - 1] == '\\') { + //TODO remove the escape! + found++; + continue; + } + result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text + let offset = 0; + if (pattern[found + 1] == ':') { + offset++; /* the beginning : */ + while (pattern[found + 1 + offset] != ':' && found + 1 + offset < pattern.length) + offset++; + const tag = pattern.substr(found + 2, offset - 1); + offset++; /* the ending : */ + if (pattern[found + offset + 1] != '}' && found + 1 + offset < pattern.length) { + found++; + continue; + } + result.push($.spawn(tag)); + } + else { + let number; + while ("0123456789".includes(pattern[found + 1 + offset])) + offset++; + number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0"); + if (pattern[found + offset + 1] != '}') { + found++; + continue; + } + if (objects.length < number) + log.warn(LogCategory.GENERAL, _translations.MPJIRlqs || (_translations.MPJIRlqs = tr("Message to format contains invalid index (%o)")), number); + result.push(...formatElement(objects[number])); + } + found = found + 1 + offset; + begin = found + 1; + } while (found++); + return result; + } + MessageHelper.formatMessage = formatMessage; + //TODO: Remove this (only legacy) + function bbcode_chat(message) { + return messages.formatter.bbcode.format(message, { + is_chat_message: true + }); + } + MessageHelper.bbcode_chat = bbcode_chat; + let network; + (function (network) { + network.KB = 1024; + network.MB = 1024 * network.KB; + network.GB = 1024 * network.MB; + network.TB = 1024 * network.GB; + function format_bytes(value, options) { + options = options || {}; + if (typeof options.exact !== "boolean") + options.exact = true; + if (typeof options.unit !== "string") + options.unit = "Bytes"; + let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + let v, unit; + if (value > 2 * network.TB) { + unit = "TB"; + v = value / network.TB; + } + else if (value > network.GB) { + unit = "GB"; + v = value / network.GB; + } + else if (value > network.MB) { + unit = "MB"; + v = value / network.MB; + } + else if (value > network.KB) { + unit = "KB"; + v = value / network.KB; + } + else { + unit = ""; + v = value; + } + let result = ""; + if (options.exact || !unit) { + result += points; + if (options.unit) { + result += " " + options.unit; + if (options.time) + result += "/" + options.time; + } + } + if (unit) { + result += (result ? " / " : "") + v.toFixed(2) + " " + unit; + if (options.time) + result += "/" + options.time; + } + return result; + } + network.format_bytes = format_bytes; + })(network = MessageHelper.network || (MessageHelper.network = {})); + MessageHelper.K = 1000; + MessageHelper.M = 1000 * MessageHelper.K; + MessageHelper.G = 1000 * MessageHelper.M; + MessageHelper.T = 1000 * MessageHelper.G; + function format_number(value, options) { + options = Object.assign(options || {}, {}); + let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + let v, unit; + if (value > 2 * MessageHelper.T) { + unit = "T"; + v = value / MessageHelper.T; + } + else if (value > MessageHelper.G) { + unit = "G"; + v = value / MessageHelper.G; + } + else if (value > MessageHelper.M) { + unit = "M"; + v = value / MessageHelper.M; + } + else if (value > MessageHelper.K) { + unit = "K"; + v = value / MessageHelper.K; + } + else { + unit = ""; + v = value; + } + if (unit && options.time) + unit = unit + "/" + options.time; + return points + " " + (options.unit || "") + (unit ? (" / " + v.toFixed(2) + " " + unit) : ""); + } + MessageHelper.format_number = format_number; + MessageHelper.TIME_SECOND = 1000; + MessageHelper.TIME_MINUTE = 60 * MessageHelper.TIME_SECOND; + MessageHelper.TIME_HOUR = 60 * MessageHelper.TIME_MINUTE; + MessageHelper.TIME_DAY = 24 * MessageHelper.TIME_HOUR; + MessageHelper.TIME_WEEK = 7 * MessageHelper.TIME_DAY; + function format_time(time, default_value) { + let result = ""; + if (time > MessageHelper.TIME_WEEK) { + const amount = Math.floor(time / MessageHelper.TIME_WEEK); + result += " " + amount + " " + (amount > 1 ? _translations.NDTF2Yzi || (_translations.NDTF2Yzi = tr("Weeks")) : _translations.ZI_tZyBd || (_translations.ZI_tZyBd = tr("Week"))); + time -= amount * MessageHelper.TIME_WEEK; + } + if (time > MessageHelper.TIME_DAY) { + const amount = Math.floor(time / MessageHelper.TIME_DAY); + result += " " + amount + " " + (amount > 1 ? _translations.t4VTrEXu || (_translations.t4VTrEXu = tr("Days")) : _translations.ybJHagZN || (_translations.ybJHagZN = tr("Day"))); + time -= amount * MessageHelper.TIME_DAY; + } + if (time > MessageHelper.TIME_HOUR) { + const amount = Math.floor(time / MessageHelper.TIME_HOUR); + result += " " + amount + " " + (amount > 1 ? _translations.LDpkmc_N || (_translations.LDpkmc_N = tr("Hours")) : _translations._LzkhuHI || (_translations._LzkhuHI = tr("Hour"))); + time -= amount * MessageHelper.TIME_HOUR; + } + if (time > MessageHelper.TIME_MINUTE) { + const amount = Math.floor(time / MessageHelper.TIME_MINUTE); + result += " " + amount + " " + (amount > 1 ? _translations.cute2byM || (_translations.cute2byM = tr("Minutes")) : _translations.Yse9w1Aj || (_translations.Yse9w1Aj = tr("Minute"))); + time -= amount * MessageHelper.TIME_MINUTE; + } + if (time > MessageHelper.TIME_SECOND) { + const amount = Math.floor(time / MessageHelper.TIME_SECOND); + result += " " + amount + " " + (amount > 1 ? _translations.sjq8XWYY || (_translations.sjq8XWYY = tr("Seconds")) : _translations.LFkoylsJ || (_translations.LFkoylsJ = tr("Second"))); + time -= amount * MessageHelper.TIME_SECOND; + } + return result.length > 0 ? result.substring(1) : default_value; + } + MessageHelper.format_time = format_time; + let _icon_size_style; + function set_icon_size(size) { + if (!_icon_size_style) + _icon_size_style = $.spawn("style").appendTo($("#style")); + _icon_size_style.text("\n" + + ".message > .emoji {\n" + + " height: " + size + "!important;\n" + + " width: " + size + "!important;\n" + + "}\n"); + } + MessageHelper.set_icon_size = set_icon_size; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "icon size init", + function: () => __awaiter(this, void 0, void 0, function* () { + MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em"); + }), + priority: 10 + }); +})(MessageHelper || (MessageHelper = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["94bd1d31ef570dd65f3469e82da58180cb66c5866ab7cf829a5362cb40cfd386"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["94bd1d31ef570dd65f3469e82da58180cb66c5866ab7cf829a5362cb40cfd386"] = "94bd1d31ef570dd65f3469e82da58180cb66c5866ab7cf829a5362cb40cfd386"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "IQMHO28d", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalConnect.ts (88,46)" }, { name: "kagWcYCB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalConnect.ts (107,21)" }, { name: "JfPT4VD3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalConnect.ts (288,95)" }, { name: "NJt4Js9d", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalConnect.ts (288,107)" }, { name: "iZ3MJUg4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalConnect.ts (292,80)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +//FIXME: Move this shit out of this file! +var connection_log; +(function (connection_log) { + let _history = []; + function log_connect(address) { + let entry = _history.find(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port); + if (!entry) { + _history.push(entry = { + last_timestamp: Date.now(), + first_timestamp: Date.now(), + address: address, + clients_online: 0, + clients_total: 0, + country: 'unknown', + name: 'Unknown', + icon_id: 0, + total_connection: 0, + flag_password: false, + password_hash: undefined + }); + } + entry.last_timestamp = Date.now(); + entry.total_connection++; + _save(); + } + connection_log.log_connect = log_connect; + function update_address_info(address, data) { + _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { + for (const key of Object.keys(data)) { + if (typeof (data[key]) !== "undefined") { + e[key] = data[key]; + } + } + }); + _save(); + } + connection_log.update_address_info = update_address_info; + function update_address_password(address, password_hash) { + _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { + e.password_hash = password_hash; + }); + _save(); + } + connection_log.update_address_password = update_address_password; + function _save() { + settings.changeGlobal(Settings.KEY_CONNECT_HISTORY, JSON.stringify(_history)); + } + function history() { + return _history.sort((a, b) => b.last_timestamp - a.last_timestamp); + } + connection_log.history = history; + function delete_entry(address) { + _history = _history.filter(e => !(e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port)); + _save(); + } + connection_log.delete_entry = delete_entry; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: 'connection history load', + priority: 1, + function: () => __awaiter(this, void 0, void 0, function* () { + _history = []; + try { + _history = JSON.parse(settings.global(Settings.KEY_CONNECT_HISTORY, "[]")); + } + catch (error) { + log.warn(LogCategory.CLIENT, _translations.IQMHO28d || (_translations.IQMHO28d = tr("Failed to load connection history: {}")), error); + } + }) + }); +})(connection_log || (connection_log = {})); +var Modals; +(function (Modals) { + function spawnConnectModal(options, defaultHost = { url: "ts.TeaSpeak.de", enforce: false }, connect_profile) { + let selected_profile; + const random_id = (() => { + const array = new Uint32Array(10); + window.crypto.getRandomValues(array); + return array.join(""); + })(); + const modal = createModal({ + header: _translations.kagWcYCB || (_translations.kagWcYCB = tr("Connect to a server")), + body: $("#tmpl_connect").renderTag({ + client: native_client, + forum_path: settings.static("forum_path"), + password_id: random_id, + multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), + default_connect_new_tab: typeof (options.default_connect_new_tab) === "boolean" && options.default_connect_new_tab + }), + footer: () => undefined, + min_width: "28em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-connect"); + /* server list toggle */ + { + const container_last_servers = modal.htmlTag.find(".container-last-servers"); + const button = modal.htmlTag.find(".button-toggle-last-servers"); + const set_show = shown => { + container_last_servers.toggleClass('shown', shown); + button.find(".arrow").toggleClass('down', shown).toggleClass('up', !shown); + settings.changeGlobal("connect_show_last_servers", shown); + }; + button.on('click', event => { + set_show(!container_last_servers.hasClass("shown")); + }); + set_show(settings.static_global("connect_show_last_servers", false)); + } + const apply = (header, body, footer) => { + const container_last_server_body = modal.htmlTag.find(".container-last-servers .table .body"); + const container_empty = container_last_server_body.find(".body-empty"); + let current_connect_data; + const button_connect = footer.find(".button-connect"); + const button_connect_tab = footer.find(".button-connect-new-tab"); + const button_manage = body.find(".button-manage-profiles"); + const input_profile = body.find(".container-select-profile select"); + const input_address = body.find(".container-address input"); + const input_nickname = body.find(".container-nickname input"); + const input_password = body.find(".container-password input"); + let updateFields = (reset_current_data) => { + if (reset_current_data) { + current_connect_data = undefined; + container_last_server_body.find(".selected").removeClass("selected"); + } + let address = input_address.val().toString(); + settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address); + let flag_address = !!address.match(Modals.Regex.IP_V4) || !!address.match(Modals.Regex.IP_V6) || !!address.match(Modals.Regex.DOMAIN); + let nickname = input_nickname.val().toString(); + if (nickname) + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname); + else + nickname = input_nickname.attr("placeholder") || ""; + let flag_nickname = nickname.length >= 3 && nickname.length <= 32; + input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address); + input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname); + const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid(); + button_connect.prop("disabled", flag_disabled); + button_connect_tab.prop("disabled", flag_disabled); + }; + input_address.val(defaultHost.enforce ? defaultHost.url : settings.static_global(Settings.KEY_CONNECT_ADDRESS, defaultHost.url)); + input_address + .on("keyup", () => updateFields(true)) + .on('keydown', event => { + if (event.keyCode == KeyCode.KEY_ENTER && !event.shiftKey) + button_connect.trigger('click'); + }); + button_manage.on('click', event => { + const modal = Modals.spawnSettingsModal("identity-profiles"); + modal.close_listener.push(() => { + input_profile.trigger('change'); + }); + return true; + }); + /* Connect Profiles */ + { + for (const profile of profiles.profiles()) { + input_profile.append($.spawn("option").text(profile.profile_name).val(profile.id)); + } + input_profile.on('change', event => { + selected_profile = profiles.find_profile(input_profile.val()) || profiles.default_profile(); + { + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined); + input_nickname + .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user") + .val(""); + } + settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, selected_profile.id); + input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid()); + updateFields(true); + }); + input_profile.val(connect_profile && connect_profile.profile ? + connect_profile.profile.id : + settings.static_global(Settings.KEY_CONNECT_PROFILE, "default")).trigger('change'); + } + const last_nickname = settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined); + if (last_nickname) /* restore */ + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, last_nickname); + input_nickname.val(last_nickname); + input_nickname.on("keyup", () => updateFields(true)); + setTimeout(() => updateFields(false), 100); + const server_address = () => { + let address = input_address.val().toString(); + if (address.match(Modals.Regex.IP_V6) && !address.startsWith("[")) + return "[" + address + "]"; + return address; + }; + button_connect.on('click', event => { + modal.close(); + const connection = server_connections.active_connection_handler(); + if (connection) { + connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { + nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), + password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } + }); + } + else { + button_connect_tab.trigger('click'); + } + }); + button_connect_tab.on('click', event => { + modal.close(); + const connection = server_connections.spawn_server_connection_handler(); + server_connections.set_active_connection_handler(connection); + connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { + nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), + password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } + }); + }); + /* connect history show */ + { + for (const entry of connection_log.history().slice(0, 10)) { + $.spawn("div").addClass("row").append($.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { + event.preventDefault(); + const row = $(event.target).parents('.row'); + row.hide(250, () => { + row.detach(); + }); + connection_log.delete_entry(entry.address); + container_empty.toggle(container_last_server_body.children().length > 1); + })).append($.spawn("div").addClass("column name").append([ + IconManager.generate_tag(IconManager.load_cached_icon(entry.icon_id)), + $.spawn("a").text(entry.name) + ])).append($.spawn("div").addClass("column address").text(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : ""))).append($.spawn("div").addClass("column password").text(entry.flag_password ? _translations.JfPT4VD3 || (_translations.JfPT4VD3 = tr("Yes")) : _translations.NJt4Js9d || (_translations.NJt4Js9d = tr("No")))).append($.spawn("div").addClass("column country-name").append([ + $.spawn("div").addClass("country flag-" + entry.country.toLowerCase()), + $.spawn("a").text(i18n.country_name(entry.country, _translations.iZ3MJUg4 || (_translations.iZ3MJUg4 = tr("Global")))) + ])).append($.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total)).append($.spawn("div").addClass("column connections").text(entry.total_connection + "")).on('click', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + current_connect_data = entry; + container_last_server_body.find(".selected").removeClass("selected"); + $(event.target).parent('.row').addClass('selected'); + input_address.val(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")); + input_password.val(entry.flag_password && entry.password_hash ? "WolverinDEV Yeahr!" : "").trigger('change'); + }).on('dblclick', event => { + current_connect_data = entry; + button_connect.trigger('click'); + }).appendTo(container_last_server_body); + container_empty.toggle(false); + } + } + }; + apply(modal.htmlTag, modal.htmlTag, modal.htmlTag); + modal.open(); + return; + } + Modals.spawnConnectModal = spawnConnectModal; + Modals.Regex = { + //DOMAIN<:port> + DOMAIN: /^(localhost|((([a-zA-Z0-9_-]{0,63}\.){0,253})?[a-zA-Z0-9_-]{0,63}\.[a-zA-Z]{2,64}))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,46}))$/, + //IP<:port> + IP_V4: /(^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4}))$/, + IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, + IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, + }; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["49ae0724dd242660d7449bee6e4df1efdc7b9a7760c6c9229e4cf368f74fc565"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["49ae0724dd242660d7449bee6e4df1efdc7b9a7760c6c9229e4cf368f74fc565"] = "49ae0724dd242660d7449bee6e4df1efdc7b9a7760c6c9229e4cf368f74fc565"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "BL0CxyqI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (24,46)" }, { name: "_34ju4T0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (24,66)" }, { name: "eQQj5pTU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (77,59)" }, { name: "bm2Swfik", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (79,59)" }, { name: "YUZTrce_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (83,70)" }, { name: "FawXE9NI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBanClient.ts (84,55)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function spawnBanClient(client, entries, callback) { + const max_ban_time = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value; + const permission_criteria_hwid = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_HWID).granted(1); + const permission_criteria_ip = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_IP).granted(1); + const permission_criteria_name = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_NAME).granted(1); + const modal = createModal({ + header: Array.isArray(entries) ? _translations.BL0CxyqI || (_translations.BL0CxyqI = tr("Ban clients")) : _translations._34ju4T0 || (_translations._34ju4T0 = tr("Ban client")), + body: function () { + let template = $("#tmpl_client_ban").renderTag({ entries: entries }); + let update_duration; + let update_button_ok; + const button_ok = template.find(".button-apply"); + const button_cancel = template.find(".button-cancel"); + const input_duration_value = template.find(".container-duration input").on('change keyup', () => update_duration()); + const input_duration_type = template.find(".container-duration select").on('change keyup', () => update_duration()); + const container_reason = template.find(".container-reason"); + const criteria_nickname = template.find(".criteria.nickname input") + .prop('checked', permission_criteria_name).prop("disabled", !permission_criteria_name) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_name); + const criteria_ip_address = template.find(".criteria.ip-address input") + .prop('checked', permission_criteria_ip).prop("disabled", !permission_criteria_ip) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_ip); + const criteria_hardware_id = template.find(".criteria.hardware-id input") + .prop('checked', permission_criteria_hwid).prop("disabled", !permission_criteria_hwid) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_hwid); + /* duration input handler */ + { + const tooltip_duration_max = template.find(".tooltip-max-time a.max"); + update_duration = () => { + const type = input_duration_type.val(); + const value = parseInt(input_duration_value.val()); + const disabled = input_duration_type.prop("disabled"); + input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); + if (type !== "perm") { + if (input_duration_value.attr("x-saved-value")) { + input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); + input_duration_value.attr("x-saved-value", null); + } + const selected_option = input_duration_type.find("option[value='" + type + "']"); + const max = parseInt(selected_option.attr("duration-max")); + input_duration_value.attr("max", max); + if ((value > max && max != -1) || value < 1) { + input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); + } + else { + input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); + } + if (max != -1) + tooltip_duration_max.html((_translations.eQQj5pTU || (_translations.eQQj5pTU = tr("You're allowed to ban a maximum of "))) + "" + max + " " + Modals.duration_data[type][max == 1 ? "1-text" : "text"] + ""); + else + tooltip_duration_max.html(_translations.bm2Swfik || (_translations.bm2Swfik = tr("You're allowed to ban permanent."))); + } + else { + if (value && !Number.isNaN(value)) + input_duration_value.attr("x-saved-value", value); + input_duration_value.attr("placeholder", _translations.YUZTrce_ || (_translations.YUZTrce_ = tr("for ever"))).val(null); + tooltip_duration_max.html(_translations.FawXE9NI || (_translations.FawXE9NI = tr("You're allowed to ban permanent."))); + } + update_button_ok && update_button_ok(); + }; + /* initialize ban time */ + Promise.resolve(max_ban_time).catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { + let unlimited = max_time == 0 || max_time == -1; + if (unlimited || typeof (max_time) === "undefined") + max_time = 0; + for (const value of Object.keys(Modals.duration_data)) { + input_duration_type.find("option[value='" + value + "']") + .prop("disabled", !unlimited && max_time >= Modals.duration_data[value].scale) + .attr("duration-scale", Modals.duration_data[value].scale) + .attr("duration-max", unlimited ? -1 : Math.floor(max_time / Modals.duration_data[value].scale)); + } + input_duration_type.find("option[value='perm']") + .prop("disabled", !unlimited) + .attr("duration-scale", 0) + .attr("duration-max", -1); + update_duration(); + }); + update_duration(); + } + /* ban reason */ + { + const input = container_reason.find("textarea"); + const insert_tag = (open, close) => { + if (input.prop("disabled")) + return; + const node = input[0]; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; + } + else { + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; + } + input.focus().trigger('change'); + }; + container_reason.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container_reason.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container_reason.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container_reason.find(".button-color input").on('change', event => { + insert_tag('[color=' + event.target.value + ']', '[/color]'); + }); + } + /* buttons */ + { + button_cancel.on('click', event => modal.close()); + button_ok.on('click', event => { + const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val())); + modal.close(); + callback({ + length: Math.floor(duration / 1000), + reason: container_reason.find("textarea").val(), + no_hwid: !criteria_hardware_id.find("input").prop("checked"), + no_ip: !criteria_ip_address.find("input").prop("checked"), + no_name: !criteria_nickname.find("input").prop("checked") + }); + }); + const inputs = template.find(".input-boxed"); + update_button_ok = () => { + const invalid = [...inputs].find(e => $(e).hasClass("is-invalid")); + button_ok.prop('disabled', !!invalid); + }; + update_button_ok(); + } + tooltip(template); + return template.children(); + }, + footer: null, + min_width: "10em", + width: "30em" + }); + modal.open(); + modal.htmlTag.find(".modal-body").addClass("modal-ban-client"); + } + Modals.spawnBanClient = spawnBanClient; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3029b469e8bd7e1fb5c0bfcd428b9adc83c2b65e721d1f06c8aacae048f4d0ed"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3029b469e8bd7e1fb5c0bfcd428b9adc83c2b65e721d1f06c8aacae048f4d0ed"] = "3029b469e8bd7e1fb5c0bfcd428b9adc83c2b65e721d1f06c8aacae048f4d0ed"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Ag35JegW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalYesNo.ts (14,69)" }, { name: "JkQ7VkSV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalYesNo.ts (15,67)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var Modals; +(function (Modals) { + function spawnYesNo(header, body, callback, properties) { + properties = properties || {}; + const props = ModalFunctions.warpProperties({}); + props.template_properties || (props.template_properties = {}); + props.template_properties.text_yes = properties.text_yes || (_translations.Ag35JegW || (_translations.Ag35JegW = tr("Yes"))); + props.template_properties.text_no = properties.text_no || (_translations.JkQ7VkSV || (_translations.JkQ7VkSV = tr("No"))); + props.template = "#tmpl_modal_yesno"; + props.header = header; + props.template_properties.question = ModalFunctions.jqueriefy(body); + props.closeable = typeof (properties.closeable) !== "boolean" || properties.closeable; + const modal = createModal(props); + let submited = false; + const button_yes = modal.htmlTag.find(".button-yes"); + const button_no = modal.htmlTag.find(".button-no"); + button_yes.on('click', event => { + if (!submited) { + submited = true; + callback(true); + } + modal.close(); + }); + button_no.on('click', event => { + if (!submited) { + submited = true; + callback(false); + } + modal.close(); + }); + modal.close_listener.push(() => button_no.trigger('click')); + modal.open(); + return modal; + } + Modals.spawnYesNo = spawnYesNo; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["7b2ae32fd19997df4d419a6d71100ae43c15bc27b3ea1a1523423a36e275ccd3"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["7b2ae32fd19997df4d419a6d71100ae43c15bc27b3ea1a1523423a36e275ccd3"] = "7b2ae32fd19997df4d419a6d71100ae43c15bc27b3ea1a1523423a36e275ccd3"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "IB5yR4Oy", path: "D:/TeaSpeak/web/shared/js/main.ts (46,66)" }, { name: "RDTk8by7", path: "D:/TeaSpeak/web/shared/js/main.ts (49,38)" }, { name: "m6cq39M5", path: "D:/TeaSpeak/web/shared/js/main.ts (111,44)" }, { name: "b5JhebTq", path: "D:/TeaSpeak/web/shared/js/main.ts (113,43)" }, { name: "q5H_DJQ9", path: "D:/TeaSpeak/web/shared/js/main.ts (124,23)" }, { name: "onq78WD6", path: "D:/TeaSpeak/web/shared/js/main.ts (142,31)" }, { name: "eIK8rj1V", path: "D:/TeaSpeak/web/shared/js/main.ts (149,22)" }, { name: "Eei5HLyp", path: "D:/TeaSpeak/web/shared/js/main.ts (155,43)" }, { name: "RvXdlEMk", path: "D:/TeaSpeak/web/shared/js/main.ts (162,38)" }, { name: "Lf15NSRg", path: "D:/TeaSpeak/web/shared/js/main.ts (166,37)" }, { name: "Dg9visVD", path: "D:/TeaSpeak/web/shared/js/main.ts (175,40)" }, { name: "dSUa5oYo", path: "D:/TeaSpeak/web/shared/js/main.ts (176,31)" }, { name: "QROnPXJ7", path: "D:/TeaSpeak/web/shared/js/main.ts (391,42)" }, { name: "gt6W14IK", path: "D:/TeaSpeak/web/shared/js/main.ts (496,45)" }, { name: "mihjffCP", path: "D:/TeaSpeak/web/shared/js/main.ts (498,35)" }, { name: "TFo_eVdJ", path: "D:/TeaSpeak/web/shared/js/main.ts (533,36)" }, { name: "M0D0FZJG", path: "D:/TeaSpeak/web/shared/js/main.ts (539,50)" }, { name: "wkNLjIGX", path: "D:/TeaSpeak/web/shared/js/main.ts (545,25)" }, { name: "JSLG1Ib8", path: "D:/TeaSpeak/web/shared/js/main.ts (554,50)" }, { name: "p16a70Ob", path: "D:/TeaSpeak/web/shared/js/main.ts (561,23)" }, { name: "JpTIODqT", path: "D:/TeaSpeak/web/shared/js/main.ts (585,39)" }, { name: "YhwU3o10", path: "D:/TeaSpeak/web/shared/js/main.ts (590,43)" }, { name: "HIJpRvc1", path: "D:/TeaSpeak/web/shared/js/main.ts (602,55)" }, { name: "IwzrZVZ4", path: "D:/TeaSpeak/web/shared/js/main.ts (612,21)" }, { name: "eW_bQNL1", path: "D:/TeaSpeak/web/shared/js/main.ts (621,43)" }, { name: "MWcN8CqM", path: "D:/TeaSpeak/web/shared/js/main.ts (624,39)" }, { name: "SbqJTJd5", path: "D:/TeaSpeak/web/shared/js/main.ts (639,35)" }, { name: "dWfMHleg", path: "D:/TeaSpeak/web/shared/js/main.ts (640,27)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/// +/// +/// +/// +/// +/// +var spawnYesNo = Modals.spawnYesNo; +const js_render = window.jsrender || $; +const native_client = window.require !== undefined; +function getUserMediaFunctionPromise() { + if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) + return constraints => navigator.mediaDevices.getUserMedia(constraints); + const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + if (!_callbacked_function) + return undefined; + return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); +} +function setup_close() { + window.onbeforeunload = event => { + if (profiles.requires_save()) + profiles.save(); + if (!settings.static(Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) { + const active_connections = server_connections.server_connection_handlers().filter(e => e.connected); + if (active_connections.length == 0) + return; + if (!native_client) { + event.returnValue = "Are you really sure?
You're still connected!"; + } + else { + const do_exit = () => { + const dp = server_connections.server_connection_handlers().map(e => { + if (e.serverConnection.connected()) + return e.serverConnection.disconnect(_translations.IB5yR4Oy || (_translations.IB5yR4Oy = tr("client closed"))); + return Promise.resolve(); + }).map(e => e.catch(error => { + console.warn(_translations.RDTk8by7 || (_translations.RDTk8by7 = tr("Failed to disconnect from server on client close: %o")), e); + })); + const exit = () => { + const { remote } = require('electron'); + remote.getCurrentWindow().close(); + }; + Promise.all(dp).then(exit); + /* force exit after 2500ms */ + setTimeout(exit, 2500); + }; + if (window.open_connected_question) { + event.preventDefault(); + event.returnValue = "question"; + window.open_connected_question().then(result => { + if (result) { + /* prevent quitting because we try to disconnect */ + window.onbeforeunload = e => e.preventDefault(); + /* allow a force quit after 5 seconds */ + setTimeout(() => window.onbeforeunload, 5000); + do_exit(); + } + }); + } + else { + /* we're in debugging mode */ + do_exit(); + } + } + } + }; +} +function setup_jsrender() { + if (!js_render) { + loader.critical_error("Missing jsrender extension!"); + return false; + } + if (!js_render.views) { + loader.critical_error("Missing jsrender viewer extension!"); + return false; + } + js_render.views.settings.allowCode(true); + js_render.views.tags("rnd", (argument) => { + let min = parseInt(argument.substr(0, argument.indexOf('~'))); + let max = parseInt(argument.substr(argument.indexOf('~') + 1)); + return (Math.round(Math.random() * (min + max + 1) - min)).toString(); + }); + js_render.views.tags("fmt_date", (...args) => { + return moment(args[0]).format(args[1]); + }); + js_render.views.tags("tr", (...args) => { + return tr(args[0]); + }); + $(".jsrender-template").each((idx, _entry) => { + if (!js_render.templates(_entry.id, _entry.innerHTML)) { + log.error(LogCategory.GENERAL, _translations.m6cq39M5 || (_translations.m6cq39M5 = tr("Failed to setup cache for js renderer template %s!")), _entry.id); + } + else + log.info(LogCategory.GENERAL, _translations.b5JhebTq || (_translations.b5JhebTq = tr("Successfully loaded jsrender template %s")), _entry.id); + }); + return true; +} +function initialize() { + return __awaiter(this, void 0, void 0, function* () { + Settings.initialize(); + try { + yield i18n.initialize(); + } + catch (error) { + console.error(_translations.q5H_DJQ9 || (_translations.q5H_DJQ9 = tr("Failed to initialized the translation system!\nError: %o")), error); + loader.critical_error("Failed to setup the translation system"); + return; + } + bipc.setup(); + }); +} +function initialize_app() { + return __awaiter(this, void 0, void 0, function* () { + try { //Initialize main template + const main = $("#tmpl_main").renderTag({ + multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), + app_version: app.ui_version() + }).dividerfy(); + $("body").append(main); + } + catch (error) { + log.error(LogCategory.GENERAL, error); + loader.critical_error(_translations.onq78WD6 || (_translations.onq78WD6 = tr("Failed to setup main page!"))); + return; + } + control_bar = new ControlBar($("#control_bar")); /* setup the control bar */ + if (!audio.player.initialize()) + console.warn(_translations.eIK8rj1V || (_translations.eIK8rj1V = tr("Failed to initialize audio controller!"))); + audio.player.on_ready(() => { + if (audio.player.set_master_volume) + audio.player.on_ready(() => audio.player.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER) / 100)); + else + log.warn(LogCategory.GENERAL, _translations.Eei5HLyp || (_translations.Eei5HLyp = tr("Client does not support audio.player.set_master_volume()... May client is too old?"))); + if (audio.recorder.device_refresh_available()) + audio.recorder.refresh_devices(); + }); + default_recorder = new RecorderProfile("default"); + default_recorder.initialize().catch(error => { + log.error(LogCategory.AUDIO, _translations.RvXdlEMk || (_translations.RvXdlEMk = tr("Failed to initialize default recorder: %o")), error); + }); + sound.initialize().then(() => { + log.info(LogCategory.AUDIO, _translations.Lf15NSRg || (_translations.Lf15NSRg = tr("Sounds initialized"))); + }); + sound.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER_SOUNDS) / 100); + yield profiles.load(); + try { + yield ppt.initialize(); + } + catch (error) { + log.error(LogCategory.GENERAL, _translations.Dg9visVD || (_translations.Dg9visVD = tr("Failed to initialize ppt!\nError: %o")), error); + loader.critical_error(_translations.dSUa5oYo || (_translations.dSUa5oYo = tr("Failed to initialize ppt!"))); + return; + } + setup_close(); + }); +} +function str2ab8(str) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} +/* FIXME Dont use atob, because it sucks for non UTF-8 tings */ +function arrayBufferBase64(base64) { + base64 = atob(base64); + const buf = new ArrayBuffer(base64.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = base64.length; i < strLen; i++) { + bufView[i] = base64.charCodeAt(i); + } + return buf; +} +function base64_encode_ab(source) { + const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let base64 = ""; + const bytes = new Uint8Array(source); + const byte_length = bytes.byteLength; + const byte_reminder = byte_length % 3; + const main_length = byte_length - byte_reminder; + let a, b, c, d; + let chunk; + // Main loop deals with bytes in chunks of 3 + for (let i = 0; i < main_length; i = i + 3) { + // Combine the three bytes into a single integer + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 + d = (chunk & 63) >> 0; // 63 = (2^6 - 1) << 0 + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; + } + // Deal with the remaining bytes and padding + if (byte_reminder == 1) { + chunk = bytes[main_length]; + a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4; // 3 = 2^2 - 1 + base64 += encodings[a] + encodings[b] + '=='; + } + else if (byte_reminder == 2) { + chunk = (bytes[main_length] << 8) | bytes[main_length + 1]; + a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2; // 15 = 2^4 - 1 + base64 += encodings[a] + encodings[b] + encodings[c] + '='; + } + return base64; +} +/* +class TestProxy extends bipc.MethodProxy { + constructor(params: bipc.MethodProxyConnectParameters) { + super(bipc.get_handler(), params.channel_id && params.client_id ? params : undefined); + + if(!this.is_slave()) { + this.register_method(this.add_slave); + } + if(!this.is_master()) { + this.register_method(this.say_hello); + this.register_method(this.add_master); + } + } + + setup() { + super.setup(); + } + + protected on_connected() { + log.info(LogCategory.IPC, "Test proxy connected"); + } + + protected on_disconnected() { + log.info(LogCategory.IPC, "Test proxy disconnected"); + } + + private async say_hello() : Promise { + log.info(LogCategory.IPC, "Hello World"); + } + + private async add_slave(a: number, b: number) : Promise { + return a + b; + } + + private async add_master(a: number, b: number) : Promise { + return a * b; + } +} +interface Window { + proxy_instance: TestProxy & {url: () => string}; +} +*/ +function handle_connect_request(properties, connection) { + const profile_uuid = properties.profile || (profiles.default_profile() || { id: 'default' }).id; + const profile = profiles.find_profile(profile_uuid) || profiles.default_profile(); + const username = properties.username || profile.connect_username(); + const password = properties.password ? properties.password.value : ""; + const password_hashed = properties.password ? properties.password.hashed : false; + if (profile && profile.valid()) { + connection.startConnection(properties.address, profile, true, { + nickname: username, + password: password.length > 0 ? { + password: password, + hashed: password_hashed + } : undefined + }); + server_connections.set_active_connection_handler(connection); + } + else { + Modals.spawnConnectModal({}, { + url: properties.address, + enforce: true + }, { + profile: profile, + enforce: true + }); + } +} +function main() { + /* + window.proxy_instance = new TestProxy({ + client_id: settings.static_global("proxy_client_id", undefined), + channel_id: settings.static_global("proxy_channel_id", undefined) + }) as any; + if(window.proxy_instance.is_master()) { + window.proxy_instance.setup(); + window.proxy_instance.url = () => { + const data = window.proxy_instance.generate_connect_parameters(); + return "proxy_channel_id=" + data.channel_id + "&proxy_client_id=" + data.client_id; + }; + } + */ + //http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1 + /* initialize font */ + { + const font = settings.static_global(Settings.KEY_FONT_SIZE, 14); //parseInt(getComputedStyle(document.body).fontSize) + $(document.body).css("font-size", font + "px"); + } + /* context menu prevent */ + $(document).on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + if (!settings.static_global(Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU)) + event.preventDefault(); + }); + top_menu.initialize(); + server_connections = new ServerConnectionManager($("#connection-handlers")); + control_bar.initialise(); /* before connection handler to allow property apply */ + const initial_handler = server_connections.spawn_server_connection_handler(); + initial_handler.acquire_recorder(default_recorder, false); + control_bar.set_connection_handler(initial_handler); + /** Setup the XF forum identity **/ + profiles.identities.update_forum(); + let _resize_timeout; + $(window).on('resize', event => { + if (event.target !== window) + return; + if (_resize_timeout) + clearTimeout(_resize_timeout); + _resize_timeout = setTimeout(() => { + for (const connection of server_connections.server_connection_handlers()) + connection.invoke_resized_on_activate = true; + const active_connection = server_connections.active_connection_handler(); + if (active_connection) + active_connection.resize_elements(); + $(".window-resize-listener").trigger('resize'); + }, 1000); + }); + stats.initialize({ + verbose: true, + anonymize_ip_addresses: true, + volatile_collection_only: false + }); + stats.register_user_count_listener(status => { + log.info(LogCategory.STATISTICS, _translations.QROnPXJ7 || (_translations.QROnPXJ7 = tr("Received user count update: %o")), status); + }); + server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]); + window.test_upload = (message) => { + message = message || "Hello World"; + const connection = server_connections.active_connection_handler(); + connection.fileManager.upload_file({ + size: message.length, + overwrite: true, + channel: connection.getClient().currentChannel(), + name: '/HelloWorld.txt', + path: '' + }).then(key => { + const upload = new RequestFileUpload(key); + const buffer = new Uint8Array(message.length); + { + for (let index = 0; index < message.length; index++) + buffer[index] = message.charCodeAt(index); + } + upload.put_data(buffer).catch(error => { + console.error(error); + }); + }); + }; + /* schedule it a bit later then the main because the main function is still within the loader */ + setTimeout(() => { + const connection = server_connections.active_connection_handler(); + /* + Modals.createChannelModal(connection, undefined, undefined, connection.permissions, (cb, perms) => { + + }); + */ + // Modals.openServerInfo(connection.channelTree.server); + //Modals.createServerModal(connection.channelTree.server, properties => Promise.resolve()); + //Modals.openClientInfo(connection.getClient()); + //Modals.openServerInfoBandwidth(connection.channelTree.server); + //Modals.openBanList(connection); + /* + Modals.spawnBanClient(connection,[ + {name: "WolverinDEV", unique_id: "XXXX"}, + {name: "WolverinDEV", unique_id: "XXXX"}, + {name: "WolverinDEV", unique_id: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}, + {name: "WolverinDEV", unique_id: "YYY"} + ], () => {}); + */ + }, 4000); + //Modals.spawnSettingsModal("identity-profiles"); + //Modals.spawnKeySelect(console.log); + //Modals.spawnBookmarkModal(); + /* + { + const modal = createModal({ + header: tr("Test Net Graph"), + body: () => { + const canvas = $.spawn("canvas") + .css("position", "absolute") + .css({ + top: 0, + bottom: 0, + right: 0, + left: 0 + }); + + return $.spawn("div") + .css("height", "5em") + .css("width", "30em") + .css("position", "relative") + .append(canvas); + }, + footer: null + }); + + const graph = new net.graph.Graph(modal.htmlTag.find("canvas")[0] as any); + graph.initialize(); + + modal.close_listener.push(() => graph.terminate()); + modal.open(); + } + */ + /* for testing */ + if (settings.static_global(Settings.KEY_USER_IS_NEW)) { + const modal = Modals.openModalNewcomer(); + modal.close_listener.push(() => settings.changeGlobal(Settings.KEY_USER_IS_NEW, false)); + } +} +const task_teaweb_starter = { + name: "voice app starter", + function: () => __awaiter(this, void 0, void 0, function* () { + try { + yield initialize_app(); + main(); + if (!audio.player.initialized()) { + log.info(LogCategory.VOICE, _translations.gt6W14IK || (_translations.gt6W14IK = tr("Initialize audio controller later!"))); + if (!audio.player.initializeFromGesture) { + console.error(_translations.mihjffCP || (_translations.mihjffCP = tr("Missing audio.player.initializeFromGesture"))); + } + else + $(document).one('click', event => audio.player.initializeFromGesture()); + } + } + catch (ex) { + console.error(ex.stack); + if (ex instanceof ReferenceError || ex instanceof TypeError) + ex = ex.name + ": " + ex.message; + loader.critical_error("Failed to invoke main function:
" + ex); + } + }), + priority: 10 +}; +const task_connect_handler = { + name: "Connect handler", + function: () => __awaiter(this, void 0, void 0, function* () { + const address = settings.static(Settings.KEY_CONNECT_ADDRESS, ""); + const chandler = bipc.get_connect_handler(); + if (settings.static(Settings.KEY_FLAG_CONNECT_DEFAULT, false) && address) { + const connect_data = { + address: address, + profile: settings.static(Settings.KEY_CONNECT_PROFILE, ""), + username: settings.static(Settings.KEY_CONNECT_USERNAME, ""), + password: { + value: settings.static(Settings.KEY_CONNECT_PASSWORD, ""), + hashed: settings.static(Settings.KEY_FLAG_CONNECT_PASSWORD, false) + } + }; + if (chandler) { + try { + yield chandler.post_connect_request(connect_data, () => new Promise((resolve, reject) => { + spawnYesNo(_translations.TFo_eVdJ || (_translations.TFo_eVdJ = tr("Another TeaWeb instance is already running")), tra("Another TeaWeb instance is already running.{:br:}Would you like to connect there?"), response => { + resolve(response); + }, { + closeable: false + }).open(); + })); + log.info(LogCategory.CLIENT, _translations.M0D0FZJG || (_translations.M0D0FZJG = tr("Executed connect successfully in another browser window. Closing this window"))); + const message = "You're connecting to {0} within the other TeaWeb instance.{:br:}" + + "You could now close this page."; + createInfoModal(_translations.wkNLjIGX || (_translations.wkNLjIGX = tr("Connecting successfully within other instance")), MessageHelper.formatMessage(tr(message), connect_data.address), { + closeable: false, + footer: undefined + }).open(); + return; + } + catch (error) { + log.info(LogCategory.CLIENT, _translations.JSLG1Ib8 || (_translations.JSLG1Ib8 = tr("Failed to execute connect within other TeaWeb instance. Using this one. Error: %o")), error); + } + } + loader.register_task(loader.Stage.LOADED, { + priority: 0, + function: () => __awaiter(this, void 0, void 0, function* () { return handle_connect_request(connect_data, server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler()); }), + name: _translations.p16a70Ob || (_translations.p16a70Ob = tr("default url connect")) + }); + } + if (chandler) { + /* no instance avail, so lets make us avail */ + chandler.callback_available = data => { + return !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION); + }; + chandler.callback_execute = data => { + handle_connect_request(data, server_connections.spawn_server_connection_handler()); + return true; + }; + } + loader.register_task(loader.Stage.LOADED, task_teaweb_starter); + }), + priority: 10 +}; +const task_certificate_callback = { + name: "certificate accept tester", + function: () => __awaiter(this, void 0, void 0, function* () { + const certificate_accept = settings.static_global(Settings.KEY_CERTIFICATE_CALLBACK, undefined); + if (certificate_accept) { + log.info(LogCategory.IPC, _translations.JpTIODqT || (_translations.JpTIODqT = tr("Using this instance as certificate callback. ID: %s")), certificate_accept); + try { + try { + yield bipc.get_handler().post_certificate_accpected(certificate_accept); + } + catch (e) { } //FIXME remove! + log.info(LogCategory.IPC, _translations.YhwU3o10 || (_translations.YhwU3o10 = tr("Other instance has acknowledged out work. Closing this window."))); + const seconds_tag = $.spawn("a"); + let seconds = 5; + let interval_id; + interval_id = setInterval(() => { + seconds--; + seconds_tag.text(seconds.toString()); + if (seconds <= 0) { + clearTimeout(interval_id); + log.info(LogCategory.GENERAL, _translations.HIJpRvc1 || (_translations.HIJpRvc1 = tr("Closing window"))); + window.close(); + return; + } + }, 1000); + const message = "You've successfully accepted the certificate.{:br:}" + + "This page will close in {0} seconds."; + createInfoModal(_translations.IwzrZVZ4 || (_translations.IwzrZVZ4 = tr("Certificate acccepted successfully")), MessageHelper.formatMessage(tr(message), seconds_tag), { + closeable: false, + footer: undefined + }).open(); + return; + } + catch (error) { + log.warn(LogCategory.IPC, _translations.eW_bQNL1 || (_translations.eW_bQNL1 = tr("Failed to successfully post certificate accept status: %o")), error); + } + } + else { + log.info(LogCategory.IPC, _translations.MWcN8CqM || (_translations.MWcN8CqM = tr("We're not used to accept certificated. Booting app."))); + } + loader.register_task(loader.Stage.LOADED, task_connect_handler); + }), + priority: 10 +}; +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "jrendere initialize", + function: () => __awaiter(this, void 0, void 0, function* () { + try { + if (!setup_jsrender()) + throw "invalid load"; + } + catch (error) { + loader.critical_error(_translations.SbqJTJd5 || (_translations.SbqJTJd5 = tr("Failed to setup jsrender"))); + console.error(_translations.dWfMHleg || (_translations.dWfMHleg = tr("Failed to load jsrender! %o")), error); + return; + } + }), + priority: 100 +}); +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "app starter", + function: () => __awaiter(this, void 0, void 0, function* () { + try { + yield initialize(); + if (app.is_web()) { + loader.register_task(loader.Stage.LOADED, task_certificate_callback); + } + else { + loader.register_task(loader.Stage.LOADED, task_teaweb_starter); + } + } + catch (ex) { + if (ex instanceof Error || typeof (ex.stack) !== "undefined") + console.error((tr || (msg => msg))("Critical error stack trace: %o"), ex.stack); + if (ex instanceof ReferenceError || ex instanceof TypeError) + ex = ex.name + ": " + ex.message; + loader.critical_error("Failed to boot app function:
" + ex); + } + }), + priority: 1000 +}); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["62df1507449bfd4cfeff0a258cbad9d01801f9ae54612fe2ef961ae958faf1c4"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["62df1507449bfd4cfeff0a258cbad9d01801f9ae54612fe2ef961ae958faf1c4"] = "62df1507449bfd4cfeff0a258cbad9d01801f9ae54612fe2ef961ae958faf1c4"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "aU106SIl", path: "D:/TeaSpeak/web/shared/js/stats.ts (76,46)" }, { name: "lDUhjdXK", path: "D:/TeaSpeak/web/shared/js/stats.ts (113,58)" }, { name: "VVoSHTqX", path: "D:/TeaSpeak/web/shared/js/stats.ts (123,58)" }, { name: "GfDLt7dd", path: "D:/TeaSpeak/web/shared/js/stats.ts (133,58)" }, { name: "xlw6v7TI", path: "D:/TeaSpeak/web/shared/js/stats.ts (144,62)" }, { name: "gFTimMhO", path: "D:/TeaSpeak/web/shared/js/stats.ts (173,50)" }, { name: "mHPFWzf_", path: "D:/TeaSpeak/web/shared/js/stats.ts (177,54)" }, { name: "ymcAaKXH", path: "D:/TeaSpeak/web/shared/js/stats.ts (215,55)" }, { name: "EIiJBDqh", path: "D:/TeaSpeak/web/shared/js/stats.ts (218,50)" }, { name: "NLURX_l7", path: "D:/TeaSpeak/web/shared/js/stats.ts (234,54)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var stats; +(function (stats) { + const LOG_PREFIX = "[Statistics] "; + let CloseCodes; + (function (CloseCodes) { + CloseCodes[CloseCodes["UNSET"] = 3000] = "UNSET"; + CloseCodes[CloseCodes["RECONNECT"] = 3001] = "RECONNECT"; + CloseCodes[CloseCodes["INTERNAL_ERROR"] = 3002] = "INTERNAL_ERROR"; + CloseCodes[CloseCodes["BANNED"] = 3100] = "BANNED"; + })(CloseCodes = stats.CloseCodes || (stats.CloseCodes = {})); + let ConnectionState; + (function (ConnectionState) { + ConnectionState[ConnectionState["CONNECTING"] = 0] = "CONNECTING"; + ConnectionState[ConnectionState["INITIALIZING"] = 1] = "INITIALIZING"; + ConnectionState[ConnectionState["CONNECTED"] = 2] = "CONNECTED"; + ConnectionState[ConnectionState["UNSET"] = 3] = "UNSET"; + })(ConnectionState || (ConnectionState = {})); + class SessionConfig { + } + stats.SessionConfig = SessionConfig; + class Config extends SessionConfig { + } + stats.Config = Config; + let reconnect_timer; + let current_config; + let last_user_count_update; + let user_count_listener = []; + const DEFAULT_CONFIG = { + verbose: true, + reconnect_interval: 5000, + anonymize_ip_addresses: true, + volatile_collection_only: false + }; + function initialize_config_object(target_object, source_object) { + for (const key of Object.keys(source_object)) { + if (typeof (source_object[key]) === 'object') + initialize_config_object(target_object[key] || (target_object[key] = {}), source_object[key]); + if (typeof (target_object[key]) !== 'undefined') + continue; + target_object[key] = source_object[key]; + } + return target_object; + } + function initialize(config) { + current_config = initialize_config_object(config || {}, DEFAULT_CONFIG); + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.aU106SIl || (_translations.aU106SIl = tr("Initializing statistics with this config: %o")), current_config); + connection.start_connection(); + } + stats.initialize = initialize; + function register_user_count_listener(listener) { + user_count_listener.push(listener); + } + stats.register_user_count_listener = register_user_count_listener; + function all_user_count_listener() { + return user_count_listener; + } + stats.all_user_count_listener = all_user_count_listener; + function deregister_user_count_listener(listener) { + user_count_listener.remove(listener); + } + stats.deregister_user_count_listener = deregister_user_count_listener; + let connection; + (function (connection_3) { + let connection; + connection_3.connection_state = ConnectionState.UNSET; + function start_connection() { + cancel_reconnect(); + close_connection(); + connection_3.connection_state = ConnectionState.CONNECTING; + connection = new WebSocket('wss://web-stats.teaspeak.de:27790'); + if (!connection) + connection = new WebSocket('wss://localhost:27788'); + { + const connection_copy = connection; + connection.onclose = (event) => { + if (connection_copy !== connection) + return; + if (current_config.verbose) + log.warn(LogCategory.STATISTICS, _translations.lDUhjdXK || (_translations.lDUhjdXK = tr("Lost connection to statistics server (Connection closed). Reason: %o. Event object: %o")), CloseCodes[event.code] || event.code, event); + if (event.code != CloseCodes.BANNED) + invoke_reconnect(); + }; + connection.onopen = () => { + if (connection_copy !== connection) + return; + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.VVoSHTqX || (_translations.VVoSHTqX = tr("Successfully connected to server. Initializing session."))); + connection_3.connection_state = ConnectionState.INITIALIZING; + initialize_session(); + }; + connection.onerror = (event) => { + if (connection_copy !== connection) + return; + if (current_config.verbose) + log.warn(LogCategory.STATISTICS, _translations.GfDLt7dd || (_translations.GfDLt7dd = tr("Received an error. Closing connection. Object: %o")), event); + connection.close(CloseCodes.INTERNAL_ERROR); + invoke_reconnect(); + }; + connection.onmessage = (event) => { + if (connection_copy !== connection) + return; + if (typeof (event.data) !== 'string') { + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.xlw6v7TI || (_translations.xlw6v7TI = tr("Received an message which isn't a string. Event object: %o")), event); + return; + } + handle_message(event.data); + }; + } + } + connection_3.start_connection = start_connection; + function close_connection() { + if (connection) { + const connection_copy = connection; + connection = undefined; + try { + connection_copy.close(3001); + } + catch (_) { } + } + } + connection_3.close_connection = close_connection; + function invoke_reconnect() { + close_connection(); + if (reconnect_timer) { + clearTimeout(reconnect_timer); + reconnect_timer = undefined; + } + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.gFTimMhO || (_translations.gFTimMhO = tr("Scheduled reconnect in %dms")), current_config.reconnect_interval); + reconnect_timer = setTimeout(() => { + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.mHPFWzf_ || (_translations.mHPFWzf_ = tr("Reconnecting"))); + start_connection(); + }, current_config.reconnect_interval); + } + function cancel_reconnect() { + if (reconnect_timer) { + clearTimeout(reconnect_timer); + reconnect_timer = undefined; + } + } + connection_3.cancel_reconnect = cancel_reconnect; + function send_message(type, data) { + connection.send(JSON.stringify({ + type: type, + data: data + })); + } + function initialize_session() { + const config_object = {}; + for (const key in SessionConfig) { + if (SessionConfig.hasOwnProperty(key)) + config_object[key] = current_config[key]; + } + send_message('initialize', { + config: config_object + }); + } + function handle_message(message) { + const data_object = JSON.parse(message); + const type = data_object.type; + const data = data_object.data; + if (typeof (handler[type]) === 'function') { + if (current_config.verbose) + log.debug(LogCategory.STATISTICS, _translations.ymcAaKXH || (_translations.ymcAaKXH = tr("Handling message of type %s")), type); + handler[type](data); + } + else if (current_config.verbose) { + log.warn(LogCategory.STATISTICS, _translations.EIiJBDqh || (_translations.EIiJBDqh = tr("Received message with an unknown type (%s). Dropping message. Full message: %o")), type, data_object); + } + } + let handler; + (function (handler) { + function handle_notify_user_count(data) { + last_user_count_update = Date.now(); + for (const listener of [...user_count_listener]) + listener(data); + } + function handle_notify_initialized(json) { + if (current_config.verbose) + log.info(LogCategory.STATISTICS, _translations.NLURX_l7 || (_translations.NLURX_l7 = tr("Session successfully initialized."))); + connection_3.connection_state = ConnectionState.CONNECTED; + } + handler["notifyinitialized"] = handle_notify_initialized; + handler["notifyusercount"] = handle_notify_user_count; + })(handler || (handler = {})); + })(connection || (connection = {})); +})(stats || (stats = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["7b858729af48df671cca14f199f2fa867310e13498573a5e8a3960b4e45e2c01"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["7b858729af48df671cca14f199f2fa867310e13498573a5e8a3960b4e45e2c01"] = "7b858729af48df671cca14f199f2fa867310e13498573a5e8a3960b4e45e2c01"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3ece9db6d19ea2802862b96dde584af87cf367f473167d28b5019735dc5327af"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3ece9db6d19ea2802862b96dde584af87cf367f473167d28b5019735dc5327af"] = "3ece9db6d19ea2802862b96dde584af87cf367f473167d28b5019735dc5327af"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "H2OOfK3a", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (227,67)" }, { name: "qklBLtvc", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (259,63)" }, { name: "YjALhbl6", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (278,67)" }, { name: "_LVElZ8N", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (309,63)" }, { name: "iPkDE0bO", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (343,63)" }, { name: "GIgB621U", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (357,63)" }, { name: "Afd7iFYb", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (380,63)" }, { name: "kF1u3PFy", path: "D:/TeaSpeak/web/shared/js/connection/CommandHelper.ts (403,63)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var connection; +(function (connection_4) { + class CommandHelper extends connection_4.AbstractCommandHandler { + constructor(connection) { + super(connection); + this._awaiters_unique_ids = {}; + this._awaiters_unique_dbid = {}; + this.volatile_handler_boss = false; + this.ignore_consumed = true; + } + initialize() { + this.connection.command_handler_boss().register_handler(this); + } + destroy() { + if (this.connection) { + const hboss = this.connection.command_handler_boss(); + hboss && hboss.unregister_handler(this); + } + this._awaiters_unique_ids = undefined; + } + handle_command(command) { + if (command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + if (command.command == "notifyclientgetnamefromdbid") + this.handle_notifyclientgetnamefromdbid(command.arguments); + else + return false; + return true; + } + joinChannel(channel, password) { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); + } + sendMessage(message, type, target) { + if (type == ChatType.SERVER) + return this.connection.send_command("sendtextmessage", { "targetmode": 3, "target": 0, "msg": message }); + else if (type == ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", { "targetmode": 2, "target": target.getChannelId(), "msg": message }); + else if (type == ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", { "targetmode": 1, "target": target.clientId(), "msg": message }); + } + updateClient(key, value) { + let data = {}; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } + info_from_uid(..._unique_ids) { + return __awaiter(this, void 0, void 0, function* () { + const response = []; + const request = []; + const unique_ids = new Set(_unique_ids); + if (!unique_ids.size) + return []; + const unique_id_resolvers = {}; + for (const unique_id of unique_ids) { + request.push({ 'cluid': unique_id }); + (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) + .push(unique_id_resolvers[unique_id] = info => response.push(info)); + } + try { + yield this.connection.send_command("clientgetnamefromuid", request); + } + catch (error) { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } + else { + throw error; + } + } + finally { + /* cleanup */ + for (const unique_id of Object.keys(unique_id_resolvers)) + (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); + } + return response; + }); + } + handle_notifyclientgetnamefromdbid(json) { + for (const entry of json) { + const info = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + const functions = this._awaiters_unique_dbid[info.client_database_id] || []; + delete this._awaiters_unique_dbid[info.client_database_id]; + for (const fn of functions) + fn(info); + } + } + info_from_cldbid(..._cldbid) { + return __awaiter(this, void 0, void 0, function* () { + const response = []; + const request = []; + const unique_cldbid = new Set(_cldbid); + if (!unique_cldbid.size) + return []; + const unique_cldbid_resolvers = {}; + for (const cldbid of unique_cldbid) { + request.push({ 'cldbid': cldbid }); + (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) + .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); + } + try { + yield this.connection.send_command("clientgetnamefromdbid", request); + } + catch (error) { + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } + else { + throw error; + } + } + finally { + /* cleanup */ + for (const cldbid of Object.keys(unique_cldbid_resolvers)) + (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); + } + return response; + }); + } + handle_notifyclientnamefromuid(json) { + for (const entry of json) { + const info = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + const functions = this._awaiters_unique_ids[entry["cluid"]] || []; + delete this._awaiters_unique_ids[entry["cluid"]]; + for (const fn of functions) + fn(info); + } + } + request_query_list(server_id = undefined) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + const result = {}; + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + for (const entry of json) { + const rentry = {}; + rentry.bounded_server = parseInt(entry["client_bound_server"]); + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + result.queries.push(rentry); + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + let data = {}; + if (server_id !== undefined) + data["server_id"] = server_id; + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof CommandResult) { + if (error.id == ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } + } + reject(error); + }); + }); + } + request_playlist_list() { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result = []; + for (const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } + catch (error) { + log.error(LogCategory.NETWORKING, _translations.H2OOfK3a || (_translations.H2OOfK3a = tr("Failed to parse playlist entry: %o")), error); + } + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof CommandResult) { + if (error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }); + }); + } + request_playlist_songs(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; + if (json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, _translations.qklBLtvc || (_translations.qklBLtvc = tr("Received invalid notification for playlist songs"))); + return false; + } + const result = []; + for (const entry of json) { + try { + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] + }); + } + catch (error) { + log.error(LogCategory.NETWORKING, _translations.YjALhbl6 || (_translations.YjALhbl6 = tr("Failed to parse playlist song entry: %o")), error); + } + } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistsonglist", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof CommandResult) { + if (error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }); + }); + } + request_playlist_client_list(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistclientlist", + function: command => { + const json = command.arguments; + if (json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, _translations._LVElZ8N || (_translations._LVElZ8N = tr("Received invalid notification for playlist clients"))); + return false; + } + const result = []; + for (const entry of json) + result.push(parseInt(entry["cldbid"])); + resolve(result.filter(e => !isNaN(e))); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistclientlist", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if (error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + reject(error); + }); + }); + } + request_clients_by_server_group(group_id) { + return __awaiter(this, void 0, void 0, function* () { + //servergroupclientlist sgid=2 + //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyservergroupclientlist", + function: command => { + if (command.arguments[0]["sgid"] != group_id) { + log.error(LogCategory.NETWORKING, _translations.iPkDE0bO || (_translations.iPkDE0bO = tr("Received invalid notification for server group client list"))); + return false; + } + try { + const result = []; + for (const entry of command.arguments) + result.push({ + client_database_id: parseInt(entry["cldbid"]), + client_nickname: entry["client_nickname"], + client_unique_identifier: entry["client_unique_identifier"] + }); + resolve(result); + } + catch (error) { + log.error(LogCategory.NETWORKING, _translations.GIgB621U || (_translations.GIgB621U = tr("Failed to parse server group client list: %o")), error); + reject("failed to parse info"); + } + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("servergroupclientlist", { sgid: group_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + }); + } + request_playlist_info(playlist_id) { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, _translations.Afd7iFYb || (_translations.Afd7iFYb = tr("Received invalid notification for playlist info"))); + return; + } + try { + //resolve + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + playlist_max_songs: parseInt(json["playlist_max_songs"]) + }); + } + catch (error) { + log.error(LogCategory.NETWORKING, _translations.kF1u3PFy || (_translations.kF1u3PFy = tr("Failed to parse playlist info: %o")), error); + reject("failed to parse info"); + } + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("playlistinfo", { playlist_id: playlist_id }).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() { + if (this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + return new Promise((resolve, reject) => { + const single_handler = { + function: command => { + if (command.command != "" && command.command.indexOf("=") == -1) + return false; + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } + } + connection_4.CommandHelper = CommandHelper; +})(connection || (connection = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5231f662a327146d48f3085c7c57a8416d97bfc052e39b639f51171e8915807a"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5231f662a327146d48f3085c7c57a8416d97bfc052e39b639f51171e8915807a"] = "5231f662a327146d48f3085c7c57a8416d97bfc052e39b639f51171e8915807a"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "dXuuqi93", path: "D:/TeaSpeak/web/shared/js/connection/HandshakeHandler.ts (75,39)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var connection; +(function (connection) { + class HandshakeHandler { + constructor(profile, parameters) { + this.failed = false; + this.profile = profile; + this.parameters = parameters; + } + setConnection(con) { + this.connection = con; + } + initialize() { + this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection); + if (!this.handshake_handler) { + this.handshake_failed("failed to create identity handler"); + return; + } + this.handshake_handler.register_callback((flag, message) => { + if (flag) + this.handshake_finished(); + else + this.handshake_failed(message); + }); + } + get_identity_handler() { + return this.handshake_handler; + } + startHandshake() { + this.handshake_handler.start_handshake(); + } + on_teamspeak() { + const type = this.profile.selected_type(); + if (type == profiles.identities.IdentitifyType.TEAMSPEAK) + this.handshake_finished(); + else { + if (this.failed) + return; + this.failed = true; + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED); + } + } + handshake_failed(message) { + if (this.failed) + return; + this.failed = true; + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_FAILED, message); + } + handshake_finished(version) { + const _native = window["native"]; + if (native_client && _native && _native.client_version && !version) { + _native.client_version() + .then(this.handshake_finished.bind(this)) + .catch(error => { + console.error(_translations.dXuuqi93 || (_translations.dXuuqi93 = tr("Failed to get version:"))); + console.error(error); + this.handshake_finished("?.?.?"); + }); + return; + } + const git_version = settings.static_global("version", "unknown"); + const browser_name = (navigator.browserSpecs || {})["name"] || " "; + let data = { + client_nickname: this.parameters.nickname || "Another TeaSpeak user", + client_platform: (browser_name ? browser_name + " " : "") + navigator.platform, + client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")", + client_version_sign: undefined, + client_default_channel: (this.parameters.channel || {}).target, + client_default_channel_password: (this.parameters.channel || {}).password, + client_default_token: this.parameters.token, + client_server_password: this.parameters.password ? this.parameters.password.password : undefined, + client_browser_engine: navigator.product, + client_input_hardware: this.connection.client.client_status.input_hardware, + client_output_hardware: false, + client_input_muted: this.connection.client.client_status.input_muted, + client_output_muted: this.connection.client.client_status.output_muted, + }; + //0.0.1 [Build: 1549713549] Linux 7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ== + if (version) { + data.client_version = "TeaClient "; + data.client_version += " " + version; + const os = require("os"); + const arch_mapping = { + "x32": "32bit", + "x64": "64bit" + }; + data.client_version += " " + (arch_mapping[os.arch()] || os.arch()); + const os_mapping = { + "win32": "Windows", + "linux": "Linux" + }; + data.client_platform = (os_mapping[os.platform()] || os.platform()); + } + /* required to keep compatibility */ + if (this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) { + data["client_key_offset"] = this.profile.selected_identity().hash_number; + } + this.connection.send_command("clientinit", data).catch(error => { + if (error instanceof CommandResult) { + if (error.id == 1028) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); + } + else if (error.id == 783 || error.id == 519) { + error.extra_message = isNaN(parseInt(error.extra_message)) ? "8" : error.extra_message; + this.connection.client.handleDisconnect(DisconnectReason.IDENTITY_TOO_LOW, error); + } + else if (error.id == 3329) { + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_BANNED, error); + } + else { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); + } + } + else + this.connection.disconnect(); + }); + } + } + connection.HandshakeHandler = HandshakeHandler; +})(connection || (connection = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["060051cbf8eeed8239b90b8a8a80d575412f10f065dbed5bd8be5dd5e4c9fd8b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["060051cbf8eeed8239b90b8a8a80d575412f10f065dbed5bd8be5dd5e4c9fd8b"] = "060051cbf8eeed8239b90b8a8a80d575412f10f065dbed5bd8be5dd5e4c9fd8b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var ErrorID; +(function (ErrorID) { + ErrorID[ErrorID["NOT_IMPLEMENTED"] = 2] = "NOT_IMPLEMENTED"; + ErrorID[ErrorID["COMMAND_NOT_FOUND"] = 256] = "COMMAND_NOT_FOUND"; + ErrorID[ErrorID["PERMISSION_ERROR"] = 2568] = "PERMISSION_ERROR"; + ErrorID[ErrorID["EMPTY_RESULT"] = 1281] = "EMPTY_RESULT"; + ErrorID[ErrorID["PLAYLIST_IS_IN_USE"] = 8451] = "PLAYLIST_IS_IN_USE"; + ErrorID[ErrorID["FILE_ALREADY_EXISTS"] = 2050] = "FILE_ALREADY_EXISTS"; + ErrorID[ErrorID["CLIENT_INVALID_ID"] = 512] = "CLIENT_INVALID_ID"; + ErrorID[ErrorID["CONVERSATION_INVALID_ID"] = 8704] = "CONVERSATION_INVALID_ID"; + ErrorID[ErrorID["CONVERSATION_MORE_DATA"] = 8705] = "CONVERSATION_MORE_DATA"; + ErrorID[ErrorID["CONVERSATION_IS_PRIVATE"] = 8706] = "CONVERSATION_IS_PRIVATE"; +})(ErrorID || (ErrorID = {})); +class CommandResult { + constructor(json) { + this.json = json; + this.id = parseInt(json["id"]); + this.message = json["msg"]; + this.extra_message = ""; + if (json["extra_msg"]) + this.extra_message = json["extra_msg"]; + this.success = this.id == 0; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["44b39e0f81c423563b3da77145e11d4583bdaf437fa62b0b33c325db0e94385b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["44b39e0f81c423563b3da77145e11d4583bdaf437fa62b0b33c325db0e94385b"] = "44b39e0f81c423563b3da77145e11d4583bdaf437fa62b0b33c325db0e94385b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +// ASN.1 JavaScript decoder +// Copyright (c) 2008-2018 Lapo Luchini +// Copyright (c) 2019-2019 Markus Hadenfeldt +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +var asn1; +(function (asn1) { + const ellipsis = "\u2026"; + function string_cut(str, len) { + if (str.length > len) + str = str.substring(0, len) + ellipsis; + return str; + } + class Stream { + constructor(data, position) { + if (data instanceof Stream) + this.data = data.data; + else + this.data = data; + this.position = position; + } + length() { + if (this.data instanceof ArrayBuffer) + return this.data.byteLength; + return this.data.length; + } + get(position) { + if (position === undefined) + position = this.position++; + if (position >= this.length()) + throw 'Requesting byte offset ' + this.position + ' on a stream of length ' + this.length(); + return (typeof (this.data) === "string") ? this.data.charCodeAt(position) : this.data[position]; + } + hexByte(byte) { + return Stream.HEX_DIGITS.charAt((byte >> 4) & 0xF) + Stream.HEX_DIGITS.charAt(byte & 0xF); + } + parseStringISO(start, end) { + let s = ""; + for (let i = start; i < end; ++i) + s += String.fromCharCode(this.get(i)); + return s; + } + parseStringUTF(start, end) { + let s = ""; + for (let i = start; i < end;) { + let c = this.get(i++); + if (c < 128) + s += String.fromCharCode(c); + else if ((c > 191) && (c < 224)) + s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); + else + s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); + } + return s; + } + parseStringBMP(start, end) { + let str = "", hi, lo; + for (let i = start; i < end;) { + hi = this.get(i++); + lo = this.get(i++); + str += String.fromCharCode((hi << 8) | lo); + } + return str; + } + parseTime(start, end, shortYear) { + let s = this.parseStringISO(start, end), m = (shortYear ? Stream.reTimeS : Stream.reTimeL).exec(s); + if (!m) + return "Unrecognized time: " + s; + if (shortYear) { + // to avoid querying the timer, use the fixed range [1970, 2069] + // it will conform with ITU X.400 [-10, +40] sliding window until 2030 + //m[1] = +m[1]; + //m[1] += (parseInt(m[1]) < 70) ? 2000 : 1900; + throw "fixme!"; + } + s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; + if (m[5]) { + s += ":" + m[5]; + if (m[6]) { + s += ":" + m[6]; + if (m[7]) + s += "." + m[7]; + } + } + if (m[8]) { + s += " UTC"; + if (m[8] != 'Z') { + s += m[8]; + if (m[9]) + s += ":" + m[9]; + } + } + return s; + } + ; + parseInteger(start, end) { + let current = this.get(start); + let negative = (current > 127); + let padding = negative ? 255 : 0; + let length; + let descriptor; + // skip unuseful bits (not allowed in DER) + while (current == padding && ++start < end) + current = this.get(start); + length = end - start; + if (length === 0) + return negative ? '-1' : '0'; + // show bit length of huge integers + if (length > 4) { + descriptor = current; + length <<= 3; /* calculate bit length */ + while (((descriptor ^ padding) & 0x80) == 0) { + descriptor <<= 1; + --length; + } + descriptor = "(" + length + " bit)\n"; + } + // decode the integer + if (negative) + current = current - 256; + let number = ""; + if (typeof (Int10) !== "undefined") { + let n = new Int10(current); + for (let i = start + 1; i < end; ++i) + n.mulAdd(256, this.get(i)); + number = n.toString(); + } + else { + let n = 0; + for (let i = start + 1; i < end; ++i) { + n <<= 8; + n += this.get(i); + } + number = n.toString(); + } + return descriptor + number; + } + ; + isASCII(start, end) { + for (let i = start; i < end; ++i) { + const c = this.get(i); + if (c < 32 || c > 176) + return false; + } + return true; + } + ; + parseBitString(start, end, maxLength) { + let unusedBit = this.get(start), lenBit = ((end - start - 1) << 3) - unusedBit, intro = "(" + lenBit + " bit)\n", s = ""; + for (let i = start + 1; i < end; ++i) { + let b = this.get(i), skip = (i == end - 1) ? unusedBit : 0; + for (let j = 7; j >= skip; --j) + s += (b >> j) & 1 ? "1" : "0"; + if (s.length > maxLength) + return intro + string_cut(s, maxLength); + } + return intro + s; + } + ; + parseOctetString(start, end, maxLength) { + if (this.isASCII(start, end)) + return string_cut(this.parseStringISO(start, end), maxLength); + let len = end - start, s = "(" + len + " byte)\n"; + maxLength /= 2; // we work in bytes + if (len > maxLength) + end = start + maxLength; + for (let i = start; i < end; ++i) + s += this.hexByte(this.get(i)); + if (len > maxLength) + s += ellipsis; + return s; + } + ; + parseOID(start, end, maxLength) { + let s = '', n = new Int10(), bits = 0; + for (let i = start; i < end; ++i) { + let v = this.get(i); + n.mulAdd(128, v & 0x7F); + bits += 7; + if (!(v & 0x80)) { // finished + if (s === '') { + n = n.simplify(); + if (n instanceof Int10) { + n.sub(80); + s = "2." + n.toString(); + } + else { + let m = n < 80 ? n < 40 ? 0 : 1 : 2; + s = m + "." + (n - m * 40); + } + } + else + s += "." + n.toString(); + if (s.length > maxLength) + return string_cut(s, maxLength); + n = new Int10(); + bits = 0; + } + } + if (bits > 0) + s += ".incomplete"; + /* FIXME + if (typeof oids === 'object') { + let oid = oids[s]; + if (oid) { + if (oid.d) s += "\n" + oid.d; + if (oid.c) s += "\n" + oid.c; + if (oid.w) s += "\n(warning!)"; + } + } + */ + return s; + } + ; + } + Stream.HEX_DIGITS = "0123456789ABCDEF"; + Stream.reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + Stream.reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + asn1.Stream = Stream; + let TagClass; + (function (TagClass) { + TagClass[TagClass["UNIVERSAL"] = 0] = "UNIVERSAL"; + TagClass[TagClass["APPLICATION"] = 1] = "APPLICATION"; + TagClass[TagClass["CONTEXT"] = 2] = "CONTEXT"; + TagClass[TagClass["PRIVATE"] = 3] = "PRIVATE"; + })(TagClass = asn1.TagClass || (asn1.TagClass = {})); + let TagType; + (function (TagType) { + TagType[TagType["EOC"] = 0] = "EOC"; + TagType[TagType["BOOLEAN"] = 1] = "BOOLEAN"; + TagType[TagType["INTEGER"] = 2] = "INTEGER"; + TagType[TagType["BIT_STRING"] = 3] = "BIT_STRING"; + TagType[TagType["OCTET_STRING"] = 4] = "OCTET_STRING"; + TagType[TagType["NULL"] = 5] = "NULL"; + TagType[TagType["OBJECT_IDENTIFIER"] = 6] = "OBJECT_IDENTIFIER"; + TagType[TagType["ObjectDescriptor"] = 7] = "ObjectDescriptor"; + TagType[TagType["EXTERNAL"] = 8] = "EXTERNAL"; + TagType[TagType["REAL"] = 9] = "REAL"; + TagType[TagType["ENUMERATED"] = 10] = "ENUMERATED"; + TagType[TagType["EMBEDDED_PDV"] = 11] = "EMBEDDED_PDV"; + TagType[TagType["UTF8String"] = 12] = "UTF8String"; + TagType[TagType["SEQUENCE"] = 16] = "SEQUENCE"; + TagType[TagType["SET"] = 17] = "SET"; + TagType[TagType["NumericString"] = 18] = "NumericString"; + TagType[TagType["PrintableString"] = 19] = "PrintableString"; + TagType[TagType["TeletextString"] = 20] = "TeletextString"; + TagType[TagType["VideotexString"] = 21] = "VideotexString"; + TagType[TagType["IA5String"] = 22] = "IA5String"; + TagType[TagType["UTCTime"] = 23] = "UTCTime"; + TagType[TagType["GeneralizedTime"] = 24] = "GeneralizedTime"; + TagType[TagType["GraphicString"] = 25] = "GraphicString"; + TagType[TagType["VisibleString"] = 26] = "VisibleString"; + TagType[TagType["GeneralString"] = 27] = "GeneralString"; + TagType[TagType["UniversalString"] = 28] = "UniversalString"; + TagType[TagType["BMPString"] = 30] = "BMPString"; + })(TagType = asn1.TagType || (asn1.TagType = {})); + class ASN1Tag { + constructor(stream) { + let buf = stream.get(); + this.tagClass = buf >> 6; + this.tagConstructed = ((buf & 0x20) !== 0); + this.tagNumber = buf & 0x1F; + if (this.tagNumber == 0x1F) { // long tag + let n = new Int10(); + do { + buf = stream.get(); + n.mulAdd(128, buf & 0x7F); + } while (buf & 0x80); + this.tagNumber = n.simplify(); + } + } + isUniversal() { + return this.tagClass === 0x00; + } + ; + isEOC() { + return this.tagClass === 0x00 && this.tagNumber === 0x00; + } + ; + } + class ASN1 { + constructor(stream, header, length, tag, children) { + this.stream = stream; + this.header = header; + this.length = length; + this.tag = tag; + this.children = children; + } + content(max_length, type) { + if (this.tag === undefined) + return null; + if (max_length === undefined) + max_length = Infinity; + let content = this.posContent(), len = Math.abs(this.length); + if (!this.tag.isUniversal()) { + if (this.children !== null) + return "(" + this.children.length + " elem)"; + return this.stream.parseOctetString(content, content + len, max_length); + } + switch (type || this.tag.tagNumber) { + case 0x01: // BOOLEAN + return (this.stream.get(content) === 0) ? "false" : "true"; + case 0x02: // INTEGER + return this.stream.parseInteger(content, content + len); + case 0x03: // BIT_STRING + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseBitString(content, content + len, max_length); + case 0x04: // OCTET_STRING + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseOctetString(content, content + len, max_length); + //case 0x05: // NULL + case 0x06: // OBJECT_IDENTIFIER + return this.stream.parseOID(content, content + len, max_length); + //case 0x07: // ObjectDescriptor + //case 0x08: // EXTERNAL + //case 0x09: // REAL + //case 0x0A: // ENUMERATED + //case 0x0B: // EMBEDDED_PDV + case 0x10: // SEQUENCE + case 0x11: // SET + if (this.children !== null) + return "(" + this.children.length + " elem)"; + else + return "(no elem)"; + case 0x0C: // UTF8String + return string_cut(this.stream.parseStringUTF(content, content + len), max_length); + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + //case 0x19: // GraphicString + case 0x1A: // VisibleString + //case 0x1B: // GeneralString + //case 0x1C: // UniversalString + return string_cut(this.stream.parseStringISO(content, content + len), max_length); + case 0x1E: // BMPString + return string_cut(this.stream.parseStringBMP(content, content + len), max_length); + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); + } + return null; + } + ; + typeName() { + switch (this.tag.tagClass) { + case 0: // universal + return TagType[this.tag.tagNumber] || ("Universal_" + this.tag.tagNumber.toString()); + case 1: + return "Application_" + this.tag.tagNumber.toString(); + case 2: + return "[" + this.tag.tagNumber.toString() + "]"; // Context + case 3: + return "Private_" + this.tag.tagNumber.toString(); + } + } + ; + toString() { + return this.typeName() + "@" + this.stream.position + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.children === null) ? 'null' : this.children.length) + "]"; + } + toPrettyString(indent) { + if (indent === undefined) + indent = ''; + let s = indent + this.typeName() + " @" + this.stream.position; + if (this.length >= 0) + s += "+"; + s += this.length; + if (this.tag.tagConstructed) + s += " (constructed)"; + else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.children !== null)) + s += " (encapsulates)"; + let content = this.content(); + if (content) + s += ": " + content.replace(/\n/g, '|'); + s += "\n"; + if (this.children !== null) { + indent += ' '; + for (let i = 0, max = this.children.length; i < max; ++i) + s += this.children[i].toPrettyString(indent); + } + return s; + } + ; + posStart() { + return this.stream.position; + } + ; + posContent() { + return this.stream.position + this.header; + } + ; + posEnd() { + return this.stream.position + this.header + Math.abs(this.length); + } + ; + static decodeLength(stream) { + let buf = stream.get(); + const len = buf & 0x7F; + if (len == buf) + return len; + if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways + throw "Length over 48 bits not supported at position " + (stream.position - 1); + if (len === 0) + return null; // undefined + buf = 0; + for (let i = 0; i < len; ++i) + buf = (buf << 8) + stream.get(); + return buf; + } + ; + static encodeLength(buffer, offset, length) { + if (length < 0x7F) { + buffer[offset] = length; + } + else { + buffer[offset] = 0x80; + let index = 1; + while (length > 0) { + buffer[offset + index++] = length & 0xFF; + length >>= 8; + buffer[offset] += 1; + } + } + } + } + asn1.ASN1 = ASN1; + function decode0(stream) { + const streamStart = new Stream(stream, 0); /* copy */ + const tag = new ASN1Tag(stream); + let len = ASN1.decodeLength(stream); + const start = stream.position; + const length_header = start - streamStart.position; + let children = null; + const query_children = () => { + children = []; + if (len !== null) { + const end = start + len; + if (end > stream.length()) + throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; + while (stream.position < end) + children[children.length] = decode0(stream); + if (stream.position != end) + throw 'Content size is not correct for container at offset ' + start; + } + else { + // undefined length + try { + while (true) { + const s = decode0(stream); + if (s.tag.isEOC()) + break; + children[children.length] = s; + } + len = start - stream.position; // undefined lengths are represented as negative values + } + catch (e) { + throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e; + } + } + }; + if (tag.tagConstructed) { + // must have valid content + query_children(); + } + else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { + // sometimes BitString and OctetString are used to encapsulate ASN.1 + try { + if (tag.tagNumber == 0x03) + if (stream.get() != 0) + throw "BIT STRINGs with unused bits cannot encapsulate."; + query_children(); + for (let i = 0; i < children.length; ++i) + if (children[i].tag.isEOC()) + throw 'EOC is not supposed to be actual content.'; + } + catch (e) { + // but silently ignore when they don't + children = null; + //DEBUG console.log('Could not decode structure at ' + start + ':', e); + } + } + if (children === null) { + if (len === null) + throw "We can't skip over an invalid tag with undefined length at offset " + start; + stream.position = start + Math.abs(len); + } + return new ASN1(streamStart, length_header, len, tag, children); + } + function decode(stream) { + return decode0(new Stream(stream, 0)); + } + asn1.decode = decode; +})(asn1 || (asn1 = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["40e158fd8e3f636d8ce20ba2e787957a9f5aee6c7612e4e69590e058bdcabe54"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["40e158fd8e3f636d8ce20ba2e787957a9f5aee6c7612e4e69590e058bdcabe54"] = "40e158fd8e3f636d8ce20ba2e787957a9f5aee6c7612e4e69590e058bdcabe54"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +class Crc32 { + constructor() { + this.crc = -1 >>> 0; + } + update(data) { + const dataView = new Uint8Array(data, 0); + const len = dataView.length; + for (let i = 0; i < len; i++) { + this.crc = (this.crc >>> 8) ^ Crc32.lookup[(this.crc ^ dataView[i]) & 0xFF]; + } + } + ; + digest(radix) { + const buffer = new ArrayBuffer(4); + const dv = new DataView(buffer); + dv.setUint32(0, ~this.crc >>> 0, false); + return dv.getUint32(0).toString(radix || 16); + } + ; +} +Crc32.lookup = [ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +]; +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["8ba95a24447d1d808ee3655ba65ce013c1e572413ea5c442d930a3012aa144f1"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["8ba95a24447d1d808ee3655ba65ce013c1e572413ea5c442d930a3012aa144f1"] = "8ba95a24447d1d808ee3655ba65ce013c1e572413ea5c442d930a3012aa144f1"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var hex; +(function (hex) { + function encode(buffer) { + let hexCodes = []; + let view = new DataView(buffer); + for (let i = 0; i < view.byteLength % 4; i++) { + let value = view.getUint32(i * 4); + let stringValue = value.toString(16); + let padding = '00000000'; + let paddedValue = (padding + stringValue).slice(-padding.length); + hexCodes.push(paddedValue); + } + for (let i = (view.byteLength % 4) * 4; i < view.byteLength; i++) { + let value = view.getUint8(i).toString(16); + let padding = '00'; + hexCodes.push((padding + value).slice(-padding.length)); + } + return hexCodes.join(""); + } + hex.encode = encode; +})(hex || (hex = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e1d54bf625c362c99387a9e5848a1d964a10e12d46a4b54b6e6afe928d0daca3"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e1d54bf625c362c99387a9e5848a1d964a10e12d46a4b54b6e6afe928d0daca3"] = "e1d54bf625c362c99387a9e5848a1d964a10e12d46a4b54b6e6afe928d0daca3"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "flaw3JIB", path: "D:/TeaSpeak/web/shared/js/i18n/country.ts (1498,77)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var i18n; +(function (i18n) { + const country_infos = []; + const alpha_2_map = {}; + const fill_country_infos = (array) => { + array.push({ + name: "Afghanistan", + alpha_2: "AF", + alpha_3: "AFG", + un_code: 4 + }); + array.push({ + name: "Aland Islands", + alpha_2: "AX", + alpha_3: "ALA", + un_code: 248 + }); + array.push({ + name: "Albania", + alpha_2: "AL", + alpha_3: "ALB", + un_code: 8 + }); + array.push({ + name: "Algeria", + alpha_2: "DZ", + alpha_3: "DZA", + un_code: 12 + }); + array.push({ + name: "American Samoa", + alpha_2: "AS", + alpha_3: "ASM", + un_code: 16 + }); + array.push({ + name: "Andorra", + alpha_2: "AD", + alpha_3: "AND", + un_code: 20 + }); + array.push({ + name: "Angola", + alpha_2: "AO", + alpha_3: "AGO", + un_code: 24 + }); + array.push({ + name: "Anguilla", + alpha_2: "AI", + alpha_3: "AIA", + un_code: 660 + }); + array.push({ + name: "Antarctica", + alpha_2: "AQ", + alpha_3: "ATA", + un_code: 10 + }); + array.push({ + name: "Antigua and Barbuda", + alpha_2: "AG", + alpha_3: "ATG", + un_code: 28 + }); + array.push({ + name: "Argentina", + alpha_2: "AR", + alpha_3: "ARG", + un_code: 32 + }); + array.push({ + name: "Armenia", + alpha_2: "AM", + alpha_3: "ARM", + un_code: 51 + }); + array.push({ + name: "Aruba", + alpha_2: "AW", + alpha_3: "ABW", + un_code: 533 + }); + array.push({ + name: "Australia", + alpha_2: "AU", + alpha_3: "AUS", + un_code: 36 + }); + array.push({ + name: "Austria", + alpha_2: "AT", + alpha_3: "AUT", + un_code: 40 + }); + array.push({ + name: "Azerbaijan", + alpha_2: "AZ", + alpha_3: "AZE", + un_code: 31 + }); + array.push({ + name: "Bahamas", + alpha_2: "BS", + alpha_3: "BHS", + un_code: 44 + }); + array.push({ + name: "Bahrain", + alpha_2: "BH", + alpha_3: "BHR", + un_code: 48 + }); + array.push({ + name: "Bangladesh", + alpha_2: "BD", + alpha_3: "BGD", + un_code: 50 + }); + array.push({ + name: "Barbados", + alpha_2: "BB", + alpha_3: "BRB", + un_code: 52 + }); + array.push({ + name: "Belarus", + alpha_2: "BY", + alpha_3: "BLR", + un_code: 112 + }); + array.push({ + name: "Belgium", + alpha_2: "BE", + alpha_3: "BEL", + un_code: 56 + }); + array.push({ + name: "Belize", + alpha_2: "BZ", + alpha_3: "BLZ", + un_code: 84 + }); + array.push({ + name: "Benin", + alpha_2: "BJ", + alpha_3: "BEN", + un_code: 204 + }); + array.push({ + name: "Bermuda", + alpha_2: "BM", + alpha_3: "BMU", + un_code: 60 + }); + array.push({ + name: "Bhutan", + alpha_2: "BT", + alpha_3: "BTN", + un_code: 64 + }); + array.push({ + name: "Bolivia", + alpha_2: "BO", + alpha_3: "BOL", + un_code: 68 + }); + array.push({ + name: "Bosnia and Herzegovina", + alpha_2: "BA", + alpha_3: "BIH", + un_code: 70 + }); + array.push({ + name: "Botswana", + alpha_2: "BW", + alpha_3: "BWA", + un_code: 72 + }); + array.push({ + name: "Bouvet Island", + alpha_2: "BV", + alpha_3: "BVT", + un_code: 74 + }); + array.push({ + name: "Brazil", + alpha_2: "BR", + alpha_3: "BRA", + un_code: 76 + }); + array.push({ + name: "British Virgin Islands", + alpha_2: "VG", + alpha_3: "VGB", + un_code: 92 + }); + array.push({ + name: "British Indian Ocean Territory", + alpha_2: "IO", + alpha_3: "IOT", + un_code: 86 + }); + array.push({ + name: "Brunei Darussalam", + alpha_2: "BN", + alpha_3: "BRN", + un_code: 96 + }); + array.push({ + name: "Bulgaria", + alpha_2: "BG", + alpha_3: "BGR", + un_code: 100 + }); + array.push({ + name: "Burkina Faso", + alpha_2: "BF", + alpha_3: "BFA", + un_code: 854 + }); + array.push({ + name: "Burundi", + alpha_2: "BI", + alpha_3: "BDI", + un_code: 108 + }); + array.push({ + name: "Cambodia", + alpha_2: "KH", + alpha_3: "KHM", + un_code: 116 + }); + array.push({ + name: "Cameroon", + alpha_2: "CM", + alpha_3: "CMR", + un_code: 120 + }); + array.push({ + name: "Canada", + alpha_2: "CA", + alpha_3: "CAN", + un_code: 124 + }); + array.push({ + name: "Cape Verde", + alpha_2: "CV", + alpha_3: "CPV", + un_code: 132 + }); + array.push({ + name: "Cayman Islands", + alpha_2: "KY", + alpha_3: "CYM", + un_code: 136 + }); + array.push({ + name: "Central African Republic", + alpha_2: "CF", + alpha_3: "CAF", + un_code: 140 + }); + array.push({ + name: "Chad", + alpha_2: "TD", + alpha_3: "TCD", + un_code: 148 + }); + array.push({ + name: "Chile", + alpha_2: "CL", + alpha_3: "CHL", + un_code: 152 + }); + array.push({ + name: "China", + alpha_2: "CN", + alpha_3: "CHN", + un_code: 156 + }); + array.push({ + name: "Hong Kong, SAR China", + alpha_2: "HK", + alpha_3: "HKG", + un_code: 344 + }); + array.push({ + name: "Macao, SAR China", + alpha_2: "MO", + alpha_3: "MAC", + un_code: 446 + }); + array.push({ + name: "Christmas Island", + alpha_2: "CX", + alpha_3: "CXR", + un_code: 162 + }); + array.push({ + name: "Cocos (Keeling) Islands", + alpha_2: "CC", + alpha_3: "CCK", + un_code: 166 + }); + array.push({ + name: "Colombia", + alpha_2: "CO", + alpha_3: "COL", + un_code: 170 + }); + array.push({ + name: "Comoros", + alpha_2: "KM", + alpha_3: "COM", + un_code: 174 + }); + array.push({ + name: "Congo (Brazzaville)", + alpha_2: "CG", + alpha_3: "COG", + un_code: 178 + }); + array.push({ + name: "Congo, (Kinshasa)", + alpha_2: "CD", + alpha_3: "COD", + un_code: 180 + }); + array.push({ + name: "Cook Islands", + alpha_2: "CK", + alpha_3: "COK", + un_code: 184 + }); + array.push({ + name: "Costa Rica", + alpha_2: "CR", + alpha_3: "CRI", + un_code: 188 + }); + array.push({ + name: "Côte d'Ivoire", + alpha_2: "CI", + alpha_3: "CIV", + un_code: 384 + }); + array.push({ + name: "Croatia", + alpha_2: "HR", + alpha_3: "HRV", + un_code: 191 + }); + array.push({ + name: "Cuba", + alpha_2: "CU", + alpha_3: "CUB", + un_code: 192 + }); + array.push({ + name: "Cyprus", + alpha_2: "CY", + alpha_3: "CYP", + un_code: 196 + }); + array.push({ + name: "Czech Republic", + alpha_2: "CZ", + alpha_3: "CZE", + un_code: 203 + }); + array.push({ + name: "Denmark", + alpha_2: "DK", + alpha_3: "DNK", + un_code: 208 + }); + array.push({ + name: "Djibouti", + alpha_2: "DJ", + alpha_3: "DJI", + un_code: 262 + }); + array.push({ + name: "Dominica", + alpha_2: "DM", + alpha_3: "DMA", + un_code: 212 + }); + array.push({ + name: "Dominican Republic", + alpha_2: "DO", + alpha_3: "DOM", + un_code: 214 + }); + array.push({ + name: "Ecuador", + alpha_2: "EC", + alpha_3: "ECU", + un_code: 218 + }); + array.push({ + name: "Egypt", + alpha_2: "EG", + alpha_3: "EGY", + un_code: 818 + }); + array.push({ + name: "El Salvador", + alpha_2: "SV", + alpha_3: "SLV", + un_code: 222 + }); + array.push({ + name: "Equatorial Guinea", + alpha_2: "GQ", + alpha_3: "GNQ", + un_code: 226 + }); + array.push({ + name: "Eritrea", + alpha_2: "ER", + alpha_3: "ERI", + un_code: 232 + }); + array.push({ + name: "Estonia", + alpha_2: "EE", + alpha_3: "EST", + un_code: 233 + }); + array.push({ + name: "Ethiopia", + alpha_2: "ET", + alpha_3: "ETH", + un_code: 231 + }); + array.push({ + name: "Falkland Islands (Malvinas)", + alpha_2: "FK", + alpha_3: "FLK", + un_code: 238 + }); + array.push({ + name: "Faroe Islands", + alpha_2: "FO", + alpha_3: "FRO", + un_code: 234 + }); + array.push({ + name: "Fiji", + alpha_2: "FJ", + alpha_3: "FJI", + un_code: 242 + }); + array.push({ + name: "Finland", + alpha_2: "FI", + alpha_3: "FIN", + un_code: 246 + }); + array.push({ + name: "France", + alpha_2: "FR", + alpha_3: "FRA", + un_code: 250 + }); + array.push({ + name: "French Guiana", + alpha_2: "GF", + alpha_3: "GUF", + un_code: 254 + }); + array.push({ + name: "French Polynesia", + alpha_2: "PF", + alpha_3: "PYF", + un_code: 258 + }); + array.push({ + name: "French Southern Territories", + alpha_2: "TF", + alpha_3: "ATF", + un_code: 260 + }); + array.push({ + name: "Gabon", + alpha_2: "GA", + alpha_3: "GAB", + un_code: 266 + }); + array.push({ + name: "Gambia", + alpha_2: "GM", + alpha_3: "GMB", + un_code: 270 + }); + array.push({ + name: "Georgia", + alpha_2: "GE", + alpha_3: "GEO", + un_code: 268 + }); + array.push({ + name: "Germany", + alpha_2: "DE", + alpha_3: "DEU", + un_code: 276 + }); + array.push({ + name: "Ghana", + alpha_2: "GH", + alpha_3: "GHA", + un_code: 288 + }); + array.push({ + name: "Gibraltar", + alpha_2: "GI", + alpha_3: "GIB", + un_code: 292 + }); + array.push({ + name: "Greece", + alpha_2: "GR", + alpha_3: "GRC", + un_code: 300 + }); + array.push({ + name: "Greenland", + alpha_2: "GL", + alpha_3: "GRL", + un_code: 304 + }); + array.push({ + name: "Grenada", + alpha_2: "GD", + alpha_3: "GRD", + un_code: 308 + }); + array.push({ + name: "Guadeloupe", + alpha_2: "GP", + alpha_3: "GLP", + un_code: 312 + }); + array.push({ + name: "Guam", + alpha_2: "GU", + alpha_3: "GUM", + un_code: 316 + }); + array.push({ + name: "Guatemala", + alpha_2: "GT", + alpha_3: "GTM", + un_code: 320 + }); + array.push({ + name: "Guernsey", + alpha_2: "GG", + alpha_3: "GGY", + un_code: 831 + }); + array.push({ + name: "Guinea", + alpha_2: "GN", + alpha_3: "GIN", + un_code: 324 + }); + array.push({ + name: "Guinea-Bissau", + alpha_2: "GW", + alpha_3: "GNB", + un_code: 624 + }); + array.push({ + name: "Guyana", + alpha_2: "GY", + alpha_3: "GUY", + un_code: 328 + }); + array.push({ + name: "Haiti", + alpha_2: "HT", + alpha_3: "HTI", + un_code: 332 + }); + array.push({ + name: "Heard and Mcdonald Islands", + alpha_2: "HM", + alpha_3: "HMD", + un_code: 334 + }); + array.push({ + name: "Holy See (Vatican City State)", + alpha_2: "VA", + alpha_3: "VAT", + un_code: 336 + }); + array.push({ + name: "Honduras", + alpha_2: "HN", + alpha_3: "HND", + un_code: 340 + }); + array.push({ + name: "Hungary", + alpha_2: "HU", + alpha_3: "HUN", + un_code: 348 + }); + array.push({ + name: "Iceland", + alpha_2: "IS", + alpha_3: "ISL", + un_code: 352 + }); + array.push({ + name: "India", + alpha_2: "IN", + alpha_3: "IND", + un_code: 356 + }); + array.push({ + name: "Indonesia", + alpha_2: "ID", + alpha_3: "IDN", + un_code: 360 + }); + array.push({ + name: "Iran, Islamic Republic of", + alpha_2: "IR", + alpha_3: "IRN", + un_code: 364 + }); + array.push({ + name: "Iraq", + alpha_2: "IQ", + alpha_3: "IRQ", + un_code: 368 + }); + array.push({ + name: "Ireland", + alpha_2: "IE", + alpha_3: "IRL", + un_code: 372 + }); + array.push({ + name: "Isle of Man", + alpha_2: "IM", + alpha_3: "IMN", + un_code: 833 + }); + array.push({ + name: "Israel", + alpha_2: "IL", + alpha_3: "ISR", + un_code: 376 + }); + array.push({ + name: "Italy", + alpha_2: "IT", + alpha_3: "ITA", + un_code: 380 + }); + array.push({ + name: "Jamaica", + alpha_2: "JM", + alpha_3: "JAM", + un_code: 388 + }); + array.push({ + name: "Japan", + alpha_2: "JP", + alpha_3: "JPN", + un_code: 392 + }); + array.push({ + name: "Jersey", + alpha_2: "JE", + alpha_3: "JEY", + un_code: 832 + }); + array.push({ + name: "Jordan", + alpha_2: "JO", + alpha_3: "JOR", + un_code: 400 + }); + array.push({ + name: "Kazakhstan", + alpha_2: "KZ", + alpha_3: "KAZ", + un_code: 398 + }); + array.push({ + name: "Kenya", + alpha_2: "KE", + alpha_3: "KEN", + un_code: 404 + }); + array.push({ + name: "Kiribati", + alpha_2: "KI", + alpha_3: "KIR", + un_code: 296 + }); + array.push({ + name: "Korea (North)", + alpha_2: "KP", + alpha_3: "PRK", + un_code: 408 + }); + array.push({ + name: "Korea (South)", + alpha_2: "KR", + alpha_3: "KOR", + un_code: 410 + }); + array.push({ + name: "Kuwait", + alpha_2: "KW", + alpha_3: "KWT", + un_code: 414 + }); + array.push({ + name: "Kyrgyzstan", + alpha_2: "KG", + alpha_3: "KGZ", + un_code: 417 + }); + array.push({ + name: "Lao PDR", + alpha_2: "LA", + alpha_3: "LAO", + un_code: 418 + }); + array.push({ + name: "Latvia", + alpha_2: "LV", + alpha_3: "LVA", + un_code: 428 + }); + array.push({ + name: "Lebanon", + alpha_2: "LB", + alpha_3: "LBN", + un_code: 422 + }); + array.push({ + name: "Lesotho", + alpha_2: "LS", + alpha_3: "LSO", + un_code: 426 + }); + array.push({ + name: "Liberia", + alpha_2: "LR", + alpha_3: "LBR", + un_code: 430 + }); + array.push({ + name: "Libya", + alpha_2: "LY", + alpha_3: "LBY", + un_code: 434 + }); + array.push({ + name: "Liechtenstein", + alpha_2: "LI", + alpha_3: "LIE", + un_code: 438 + }); + array.push({ + name: "Lithuania", + alpha_2: "LT", + alpha_3: "LTU", + un_code: 440 + }); + array.push({ + name: "Luxembourg", + alpha_2: "LU", + alpha_3: "LUX", + un_code: 442 + }); + array.push({ + name: "Macedonia, Republic of", + alpha_2: "MK", + alpha_3: "MKD", + un_code: 807 + }); + array.push({ + name: "Madagascar", + alpha_2: "MG", + alpha_3: "MDG", + un_code: 450 + }); + array.push({ + name: "Malawi", + alpha_2: "MW", + alpha_3: "MWI", + un_code: 454 + }); + array.push({ + name: "Malaysia", + alpha_2: "MY", + alpha_3: "MYS", + un_code: 458 + }); + array.push({ + name: "Maldives", + alpha_2: "MV", + alpha_3: "MDV", + un_code: 462 + }); + array.push({ + name: "Mali", + alpha_2: "ML", + alpha_3: "MLI", + un_code: 466 + }); + array.push({ + name: "Malta", + alpha_2: "MT", + alpha_3: "MLT", + un_code: 470 + }); + array.push({ + name: "Marshall Islands", + alpha_2: "MH", + alpha_3: "MHL", + un_code: 584 + }); + array.push({ + name: "Martinique", + alpha_2: "MQ", + alpha_3: "MTQ", + un_code: 474 + }); + array.push({ + name: "Mauritania", + alpha_2: "MR", + alpha_3: "MRT", + un_code: 478 + }); + array.push({ + name: "Mauritius", + alpha_2: "MU", + alpha_3: "MUS", + un_code: 480 + }); + array.push({ + name: "Mayotte", + alpha_2: "YT", + alpha_3: "MYT", + un_code: 175 + }); + array.push({ + name: "Mexico", + alpha_2: "MX", + alpha_3: "MEX", + un_code: 484 + }); + array.push({ + name: "Micronesia, Federated States of", + alpha_2: "FM", + alpha_3: "FSM", + un_code: 583 + }); + array.push({ + name: "Moldova", + alpha_2: "MD", + alpha_3: "MDA", + un_code: 498 + }); + array.push({ + name: "Monaco", + alpha_2: "MC", + alpha_3: "MCO", + un_code: 492 + }); + array.push({ + name: "Mongolia", + alpha_2: "MN", + alpha_3: "MNG", + un_code: 496 + }); + array.push({ + name: "Montenegro", + alpha_2: "ME", + alpha_3: "MNE", + un_code: 499 + }); + array.push({ + name: "Montserrat", + alpha_2: "MS", + alpha_3: "MSR", + un_code: 500 + }); + array.push({ + name: "Morocco", + alpha_2: "MA", + alpha_3: "MAR", + un_code: 504 + }); + array.push({ + name: "Mozambique", + alpha_2: "MZ", + alpha_3: "MOZ", + un_code: 508 + }); + array.push({ + name: "Myanmar", + alpha_2: "MM", + alpha_3: "MMR", + un_code: 104 + }); + array.push({ + name: "Namibia", + alpha_2: "NA", + alpha_3: "NAM", + un_code: 516 + }); + array.push({ + name: "Nauru", + alpha_2: "NR", + alpha_3: "NRU", + un_code: 520 + }); + array.push({ + name: "Nepal", + alpha_2: "NP", + alpha_3: "NPL", + un_code: 524 + }); + array.push({ + name: "Netherlands", + alpha_2: "NL", + alpha_3: "NLD", + un_code: 528 + }); + array.push({ + name: "Netherlands Antilles", + alpha_2: "AN", + alpha_3: "ANT", + un_code: 530 + }); + array.push({ + name: "New Caledonia", + alpha_2: "NC", + alpha_3: "NCL", + un_code: 540 + }); + array.push({ + name: "New Zealand", + alpha_2: "NZ", + alpha_3: "NZL", + un_code: 554 + }); + array.push({ + name: "Nicaragua", + alpha_2: "NI", + alpha_3: "NIC", + un_code: 558 + }); + array.push({ + name: "Niger", + alpha_2: "NE", + alpha_3: "NER", + un_code: 562 + }); + array.push({ + name: "Nigeria", + alpha_2: "NG", + alpha_3: "NGA", + un_code: 566 + }); + array.push({ + name: "Niue", + alpha_2: "NU", + alpha_3: "NIU", + un_code: 570 + }); + array.push({ + name: "Norfolk Island", + alpha_2: "NF", + alpha_3: "NFK", + un_code: 574 + }); + array.push({ + name: "Northern Mariana Islands", + alpha_2: "MP", + alpha_3: "MNP", + un_code: 580 + }); + array.push({ + name: "Norway", + alpha_2: "NO", + alpha_3: "NOR", + un_code: 578 + }); + array.push({ + name: "Oman", + alpha_2: "OM", + alpha_3: "OMN", + un_code: 512 + }); + array.push({ + name: "Pakistan", + alpha_2: "PK", + alpha_3: "PAK", + un_code: 586 + }); + array.push({ + name: "Palau", + alpha_2: "PW", + alpha_3: "PLW", + un_code: 585 + }); + array.push({ + name: "Palestinian Territory", + alpha_2: "PS", + alpha_3: "PSE", + un_code: 275 + }); + array.push({ + name: "Panama", + alpha_2: "PA", + alpha_3: "PAN", + un_code: 591 + }); + array.push({ + name: "Papua New Guinea", + alpha_2: "PG", + alpha_3: "PNG", + un_code: 598 + }); + array.push({ + name: "Paraguay", + alpha_2: "PY", + alpha_3: "PRY", + un_code: 600 + }); + array.push({ + name: "Peru", + alpha_2: "PE", + alpha_3: "PER", + un_code: 604 + }); + array.push({ + name: "Philippines", + alpha_2: "PH", + alpha_3: "PHL", + un_code: 608 + }); + array.push({ + name: "Pitcairn", + alpha_2: "PN", + alpha_3: "PCN", + un_code: 612 + }); + array.push({ + name: "Poland", + alpha_2: "PL", + alpha_3: "POL", + un_code: 616 + }); + array.push({ + name: "Portugal", + alpha_2: "PT", + alpha_3: "PRT", + un_code: 620 + }); + array.push({ + name: "Puerto Rico", + alpha_2: "PR", + alpha_3: "PRI", + un_code: 630 + }); + array.push({ + name: "Qatar", + alpha_2: "QA", + alpha_3: "QAT", + un_code: 634 + }); + array.push({ + name: "Réunion", + alpha_2: "RE", + alpha_3: "REU", + un_code: 638 + }); + array.push({ + name: "Romania", + alpha_2: "RO", + alpha_3: "ROU", + un_code: 642 + }); + array.push({ + name: "Russian Federation", + alpha_2: "RU", + alpha_3: "RUS", + un_code: 643 + }); + array.push({ + name: "Rwanda", + alpha_2: "RW", + alpha_3: "RWA", + un_code: 646 + }); + array.push({ + name: "Saint-Barthélemy", + alpha_2: "BL", + alpha_3: "BLM", + un_code: 652 + }); + array.push({ + name: "Saint Helena", + alpha_2: "SH", + alpha_3: "SHN", + un_code: 654 + }); + array.push({ + name: "Saint Kitts and Nevis", + alpha_2: "KN", + alpha_3: "KNA", + un_code: 659 + }); + array.push({ + name: "Saint Lucia", + alpha_2: "LC", + alpha_3: "LCA", + un_code: 662 + }); + array.push({ + name: "Saint-Martin (French part)", + alpha_2: "MF", + alpha_3: "MAF", + un_code: 663 + }); + array.push({ + name: "Saint Pierre and Miquelon", + alpha_2: "PM", + alpha_3: "SPM", + un_code: 666 + }); + array.push({ + name: "Saint Vincent and Grenadines", + alpha_2: "VC", + alpha_3: "VCT", + un_code: 670 + }); + array.push({ + name: "Samoa", + alpha_2: "WS", + alpha_3: "WSM", + un_code: 882 + }); + array.push({ + name: "San Marino", + alpha_2: "SM", + alpha_3: "SMR", + un_code: 674 + }); + array.push({ + name: "Sao Tome and Principe", + alpha_2: "ST", + alpha_3: "STP", + un_code: 678 + }); + array.push({ + name: "Saudi Arabia", + alpha_2: "SA", + alpha_3: "SAU", + un_code: 682 + }); + array.push({ + name: "Senegal", + alpha_2: "SN", + alpha_3: "SEN", + un_code: 686 + }); + array.push({ + name: "Serbia", + alpha_2: "RS", + alpha_3: "SRB", + un_code: 688 + }); + array.push({ + name: "Seychelles", + alpha_2: "SC", + alpha_3: "SYC", + un_code: 690 + }); + array.push({ + name: "Sierra Leone", + alpha_2: "SL", + alpha_3: "SLE", + un_code: 694 + }); + array.push({ + name: "Singapore", + alpha_2: "SG", + alpha_3: "SGP", + un_code: 702 + }); + array.push({ + name: "Slovakia", + alpha_2: "SK", + alpha_3: "SVK", + un_code: 703 + }); + array.push({ + name: "Slovenia", + alpha_2: "SI", + alpha_3: "SVN", + un_code: 705 + }); + array.push({ + name: "Solomon Islands", + alpha_2: "SB", + alpha_3: "SLB", + un_code: 90 + }); + array.push({ + name: "Somalia", + alpha_2: "SO", + alpha_3: "SOM", + un_code: 706 + }); + array.push({ + name: "South Africa", + alpha_2: "ZA", + alpha_3: "ZAF", + un_code: 710 + }); + array.push({ + name: "South Georgia and the South Sandwich Islands", + alpha_2: "GS", + alpha_3: "SGS", + un_code: 239 + }); + array.push({ + name: "South Sudan", + alpha_2: "SS", + alpha_3: "SSD", + un_code: 728 + }); + array.push({ + name: "Spain", + alpha_2: "ES", + alpha_3: "ESP", + un_code: 724 + }); + array.push({ + name: "Sri Lanka", + alpha_2: "LK", + alpha_3: "LKA", + un_code: 144 + }); + array.push({ + name: "Sudan", + alpha_2: "SD", + alpha_3: "SDN", + un_code: 736 + }); + array.push({ + name: "Suriname", + alpha_2: "SR", + alpha_3: "SUR", + un_code: 740 + }); + array.push({ + name: "Svalbard and Jan Mayen Islands", + alpha_2: "SJ", + alpha_3: "SJM", + un_code: 744 + }); + array.push({ + name: "Swaziland", + alpha_2: "SZ", + alpha_3: "SWZ", + un_code: 748 + }); + array.push({ + name: "Sweden", + alpha_2: "SE", + alpha_3: "SWE", + un_code: 752 + }); + array.push({ + name: "Switzerland", + alpha_2: "CH", + alpha_3: "CHE", + un_code: 756 + }); + array.push({ + name: "Syrian Arab Republic (Syria)", + alpha_2: "SY", + alpha_3: "SYR", + un_code: 760 + }); + array.push({ + name: "Taiwan, Republic of China", + alpha_2: "TW", + alpha_3: "TWN", + un_code: 158 + }); + array.push({ + name: "Tajikistan", + alpha_2: "TJ", + alpha_3: "TJK", + un_code: 762 + }); + array.push({ + name: "Tanzania, United Republic of", + alpha_2: "TZ", + alpha_3: "TZA", + un_code: 834 + }); + array.push({ + name: "Thailand", + alpha_2: "TH", + alpha_3: "THA", + un_code: 764 + }); + array.push({ + name: "Timor-Leste", + alpha_2: "TL", + alpha_3: "TLS", + un_code: 626 + }); + array.push({ + name: "Togo", + alpha_2: "TG", + alpha_3: "TGO", + un_code: 768 + }); + array.push({ + name: "Tokelau", + alpha_2: "TK", + alpha_3: "TKL", + un_code: 772 + }); + array.push({ + name: "Tonga", + alpha_2: "TO", + alpha_3: "TON", + un_code: 776 + }); + array.push({ + name: "Trinidad and Tobago", + alpha_2: "TT", + alpha_3: "TTO", + un_code: 780 + }); + array.push({ + name: "Tunisia", + alpha_2: "TN", + alpha_3: "TUN", + un_code: 788 + }); + array.push({ + name: "Turkey", + alpha_2: "TR", + alpha_3: "TUR", + un_code: 792 + }); + array.push({ + name: "Turkmenistan", + alpha_2: "TM", + alpha_3: "TKM", + un_code: 795 + }); + array.push({ + name: "Turks and Caicos Islands", + alpha_2: "TC", + alpha_3: "TCA", + un_code: 796 + }); + array.push({ + name: "Tuvalu", + alpha_2: "TV", + alpha_3: "TUV", + un_code: 798 + }); + array.push({ + name: "Uganda", + alpha_2: "UG", + alpha_3: "UGA", + un_code: 800 + }); + array.push({ + name: "Ukraine", + alpha_2: "UA", + alpha_3: "UKR", + un_code: 804 + }); + array.push({ + name: "United Arab Emirates", + alpha_2: "AE", + alpha_3: "ARE", + un_code: 784 + }); + array.push({ + name: "United Kingdom", + alpha_2: "GB", + alpha_3: "GBR", + un_code: 826 + }); + array.push({ + name: "United States of America", + alpha_2: "US", + alpha_3: "USA", + un_code: 840 + }); + array.push({ + name: "US Minor Outlying Islands", + alpha_2: "UM", + alpha_3: "UMI", + un_code: 581 + }); + array.push({ + name: "Uruguay", + alpha_2: "UY", + alpha_3: "URY", + un_code: 858 + }); + array.push({ + name: "Uzbekistan", + alpha_2: "UZ", + alpha_3: "UZB", + un_code: 860 + }); + array.push({ + name: "Vanuatu", + alpha_2: "VU", + alpha_3: "VUT", + un_code: 548 + }); + array.push({ + name: "Venezuela (Bolivarian Republic)", + alpha_2: "VE", + alpha_3: "VEN", + un_code: 862 + }); + array.push({ + name: "Viet Nam", + alpha_2: "VN", + alpha_3: "VNM", + un_code: 704 + }); + array.push({ + name: "Virgin Islands, US", + alpha_2: "VI", + alpha_3: "VIR", + un_code: 850 + }); + array.push({ + name: "Wallis and Futuna Islands", + alpha_2: "WF", + alpha_3: "WLF", + un_code: 876 + }); + array.push({ + name: "Western Sahara", + alpha_2: "EH", + alpha_3: "ESH", + un_code: 732 + }); + array.push({ + name: "Yemen", + alpha_2: "YE", + alpha_3: "YEM", + un_code: 887 + }); + array.push({ + name: "Zambia", + alpha_2: "ZM", + alpha_3: "ZMB", + un_code: 894 + }); + array.push({ + name: "Zimbabwe", + alpha_2: "ZW", + alpha_3: "ZWE", + un_code: 716 + }); + }; + function country_name(alpha_code, fallback) { + return (alpha_2_map[alpha_code.toUpperCase()] || { name: fallback || (_translations.flaw3JIB || (_translations.flaw3JIB = i18n.tr("unknown country"))) }).name; + } + i18n.country_name = country_name; + fill_country_infos(country_infos); + for (const country of country_infos) + alpha_2_map[country.alpha_2] = country; +})(i18n || (i18n = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["36f3b62ac78dcf997caadc844250a877529ef7cc8f67b7c34ea38cae195bdf1d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["36f3b62ac78dcf997caadc844250a877529ef7cc8f67b7c34ea38cae195bdf1d"] = "36f3b62ac78dcf997caadc844250a877529ef7cc8f67b7c34ea38cae195bdf1d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "kET_CIMv", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (124,31)" }, { name: "y0AzVS4M", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (125,34)" }, { name: "zfwYoTqI", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (125,90)" }, { name: "V5G4WXp1", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (140,35)" }, { name: "RWlYypJZ", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (165,38)" }, { name: "Y9SQ5M3h", path: "D:/TeaSpeak/web/shared/js/profiles/ConnectionProfile.ts (165,81)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var profiles; +(function (profiles_1) { + class ConnectionProfile { + constructor(id) { + this.selected_identity_type = "unset"; + this.identities = {}; + this.id = id; + } + connect_username() { + if (this.default_username && this.default_username !== "Another TeaSpeak user") + return this.default_username; + let selected = this.selected_identity(); + let name = selected ? selected.fallback_name() : undefined; + return name || "Another TeaSpeak user"; + } + selected_identity(current_type) { + if (!current_type) + current_type = this.selected_type(); + if (current_type === undefined) + return undefined; + if (current_type == profiles_1.identities.IdentitifyType.TEAFORO) { + return profiles_1.identities.static_forum_identity(); + } + else if (current_type == profiles_1.identities.IdentitifyType.TEAMSPEAK || current_type == profiles_1.identities.IdentitifyType.NICKNAME) { + return this.identities[profiles_1.identities.IdentitifyType[current_type].toLowerCase()]; + } + return undefined; + } + selected_type() { + return this.selected_identity_type ? profiles_1.identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; + } + set_identity(type, identity) { + this.identities[profiles_1.identities.IdentitifyType[type].toLowerCase()] = identity; + } + spawn_identity_handshake_handler(connection) { + const identity = this.selected_identity(); + if (!identity) + return undefined; + return identity.spawn_identity_handshake_handler(connection); + } + encode() { + const identity_data = {}; + for (const key in this.identities) + if (this.identities[key]) + identity_data[key] = this.identities[key].encode(); + return JSON.stringify({ + version: 1, + username: this.default_username, + password: this.default_password, + profile_name: this.profile_name, + identity_type: this.selected_identity_type, + identity_data: identity_data, + id: this.id + }); + } + valid() { + const identity = this.selected_identity(); + if (!identity || !identity.valid()) + return false; + return true; + } + } + profiles_1.ConnectionProfile = ConnectionProfile; + function decode_profile(data) { + return __awaiter(this, void 0, void 0, function* () { + data = JSON.parse(data); + if (data.version !== 1) + return "invalid version"; + const result = new ConnectionProfile(data.id); + result.default_username = data.username; + result.default_password = data.password; + result.profile_name = data.profile_name; + result.selected_identity_type = (data.identity_type || "").toLowerCase(); + if (data.identity_data) { + for (const key in data.identity_data) { + const type = profiles_1.identities.IdentitifyType[key.toUpperCase()]; + const _data = data.identity_data[key]; + if (type == undefined) + continue; + const identity = yield profiles_1.identities.decode_identity(type, _data); + if (identity == undefined) + continue; + result.identities[key.toLowerCase()] = identity; + } + } + return result; + }); + } + let available_profiles = []; + function load() { + return __awaiter(this, void 0, void 0, function* () { + available_profiles = []; + const profiles_json = localStorage.getItem("profiles"); + let profiles_data = (() => { + try { + return profiles_json ? JSON.parse(profiles_json) : { version: 0 }; + } + catch (error) { + debugger; + console.error(_translations.kET_CIMv || (_translations.kET_CIMv = tr("Invalid profile json! Resetting profiles :( (%o)")), profiles_json); + createErrorModal(_translations.y0AzVS4M || (_translations.y0AzVS4M = tr("Profile data invalid")), MessageHelper.formatMessage(_translations.zfwYoTqI || (_translations.zfwYoTqI = tr("The profile data is invalid.{:br:}This might cause data loss.")))).open(); + return { version: 0 }; + } + })(); + if (profiles_data.version === 0) { + profiles_data = { + version: 1, + profiles: [] + }; + } + if (profiles_data.version == 1) { + for (const profile_data of profiles_data.profiles) { + const profile = yield decode_profile(profile_data); + if (typeof (profile) === 'string') { + console.error(_translations.V5G4WXp1 || (_translations.V5G4WXp1 = tr("Failed to load profile. Reason: %s, Profile data: %s")), profile, profiles_data); + continue; + } + available_profiles.push(profile); + } + } + if (!find_profile("default")) { //Create a default profile and teaforo profile + { + const profile = create_new_profile("default", "default"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "Default Profile"; + /* generate default identity */ + try { + const identity = yield profiles_1.identities.TeaSpeakIdentity.generate_new(); + let active = true; + setTimeout(() => { + active = false; + }, 1000); + yield identity.improve_level(8, 1, () => active); + profile.set_identity(profiles_1.identities.IdentitifyType.TEAMSPEAK, identity); + profile.selected_identity_type = profiles_1.identities.IdentitifyType[profiles_1.identities.IdentitifyType.TEAMSPEAK]; + } + catch (error) { + createErrorModal(_translations.RWlYypJZ || (_translations.RWlYypJZ = tr("Failed to generate default identity")), _translations.Y9SQ5M3h || (_translations.Y9SQ5M3h = tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles"))).open(); + } + } + { /* forum identity (works only when connected to the forum) */ + const profile = create_new_profile("TeaSpeak Forum", "teaforo"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "TeaSpeak Forum profile"; + profile.set_identity(profiles_1.identities.IdentitifyType.TEAFORO, profiles_1.identities.static_forum_identity()); + profile.selected_identity_type = profiles_1.identities.IdentitifyType[profiles_1.identities.IdentitifyType.TEAFORO]; + } + save(); + } + }); + } + profiles_1.load = load; + function create_new_profile(name, id) { + const profile = new ConnectionProfile(id || guid()); + profile.profile_name = name; + profile.default_username = ""; + available_profiles.push(profile); + return profile; + } + profiles_1.create_new_profile = create_new_profile; + let _requires_save = false; + function save() { + const profiles = []; + for (const profile of available_profiles) + profiles.push(profile.encode()); + const data = JSON.stringify({ + version: 1, + profiles: profiles + }); + localStorage.setItem("profiles", data); + } + profiles_1.save = save; + function mark_need_save() { + _requires_save = true; + } + profiles_1.mark_need_save = mark_need_save; + function requires_save() { + return _requires_save; + } + profiles_1.requires_save = requires_save; + function profiles() { + return available_profiles; + } + profiles_1.profiles = profiles; + function find_profile(id) { + for (const profile of profiles()) + if (profile.id == id) + return profile; + return undefined; + } + profiles_1.find_profile = find_profile; + function find_profile_by_name(name) { + name = name.toLowerCase(); + for (const profile of profiles()) + if ((profile.profile_name || "").toLowerCase() == name) + return profile; + return undefined; + } + profiles_1.find_profile_by_name = find_profile_by_name; + function default_profile() { + return find_profile("default"); + } + profiles_1.default_profile = default_profile; + function set_default_profile(profile) { + const old_default = default_profile(); + if (old_default && old_default != profile) { + old_default.id = guid(); + } + profile.id = "default"; + return old_default; + } + profiles_1.set_default_profile = set_default_profile; + function delete_profile(profile) { + available_profiles.remove(profile); + } + profiles_1.delete_profile = delete_profile; +})(profiles || (profiles = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c98187f830dc6b0f36a4ad6002bfa58f867cf63df95d93b2615674cf6e7b68e2"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c98187f830dc6b0f36a4ad6002bfa58f867cf63df95d93b2615674cf6e7b68e2"] = "c98187f830dc6b0f36a4ad6002bfa58f867cf63df95d93b2615674cf6e7b68e2"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "MP4Po9Pb", path: "D:/TeaSpeak/web/shared/js/profiles/Identity.ts (79,30)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var profiles; +(function (profiles) { + var identities; + (function (identities) { + let IdentitifyType; + (function (IdentitifyType) { + IdentitifyType[IdentitifyType["TEAFORO"] = 0] = "TEAFORO"; + IdentitifyType[IdentitifyType["TEAMSPEAK"] = 1] = "TEAMSPEAK"; + IdentitifyType[IdentitifyType["NICKNAME"] = 2] = "NICKNAME"; + })(IdentitifyType = identities.IdentitifyType || (identities.IdentitifyType = {})); + function decode_identity(type, data) { + return __awaiter(this, void 0, void 0, function* () { + let identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new identities.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new identities.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new identities.TeaSpeakIdentity(undefined, undefined); + break; + } + if (!identity) + return undefined; + try { + yield identity.decode(data); + } + catch (error) { + /* todo better error handling! */ + console.error(error); + return undefined; + } + return identity; + }); + } + identities.decode_identity = decode_identity; + function create_identity(type) { + let identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new identities.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new identities.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new identities.TeaSpeakIdentity(undefined, undefined); + break; + } + return identity; + } + identities.create_identity = create_identity; + class HandshakeCommandHandler extends connection.AbstractCommandHandler { + constructor(connection, handle) { + super(connection); + this.handle = handle; + } + handle_command(command) { + if ($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if (command.command == "error") { + return false; + } + else { + console.warn(_translations.MP4Po9Pb || (_translations.MP4Po9Pb = tr("Received unknown command while handshaking (%o)")), command); + } + return true; + } + } + identities.HandshakeCommandHandler = HandshakeCommandHandler; + class AbstractHandshakeIdentityHandler { + constructor(connection) { + this.callbacks = []; + this.connection = connection; + } + register_callback(callback) { + this.callbacks.push(callback); + } + trigger_success() { + for (const callback of this.callbacks) + callback(true); + } + trigger_fail(message) { + for (const callback of this.callbacks) + callback(false, message); + } + } + identities.AbstractHandshakeIdentityHandler = AbstractHandshakeIdentityHandler; + })(identities = profiles.identities || (profiles.identities = {})); +})(profiles || (profiles = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["4f9e18c83b3da294810607c6f8fcf9feb14743dd1dce10c97c911a3eabfa9d4b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["4f9e18c83b3da294810607c6f8fcf9feb14743dd1dce10c97c911a3eabfa9d4b"] = "4f9e18c83b3da294810607c6f8fcf9feb14743dd1dce10c97c911a3eabfa9d4b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "u5Zv5_eG", path: "D:/TeaSpeak/web/shared/js/profiles/identities/NameIdentity.ts (23,51)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var profiles; +(function (profiles) { + var identities; + (function (identities) { + class NameHandshakeHandler extends identities.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new identities.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + client_nickname: this.identity.name() + }).catch(error => { + log.error(LogCategory.IDENTITIES, _translations.u5Zv5_eG || (_translations.u5Zv5_eG = tr("Failed to initialize name based handshake. Error: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }).then(() => this.trigger_success()); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } + } + class NameIdentity { + constructor(name) { + this._name = name; + } + set_name(name) { this._name = name; } + name() { return this._name; } + fallback_name() { + return this._name; + } + uid() { + return btoa(this._name); //FIXME hash! + } + type() { + return identities.IdentitifyType.NICKNAME; + } + valid() { + return this._name != undefined && this._name.length >= 5; + } + decode(data) { + data = JSON.parse(data); + if (data.version !== 1) + throw "invalid version"; + this._name = data["name"]; + return; + } + encode() { + return JSON.stringify({ + version: 1, + name: this._name + }); + } + spawn_identity_handshake_handler(connection) { + return new NameHandshakeHandler(connection, this); + } + } + identities.NameIdentity = NameIdentity; + })(identities = profiles.identities || (profiles.identities = {})); +})(profiles || (profiles = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5d120c56c8577b2f19ee369d9b574360ab78471e5f9b2b2d0fb08e8ad3807ea8"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5d120c56c8577b2f19ee369d9b574360ab78471e5f9b2b2d0fb08e8ad3807ea8"] = "5d120c56c8577b2f19ee369d9b574360ab78471e5f9b2b2d0fb08e8ad3807ea8"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "W2pDcDNQ", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeaForumIdentity.ts (22,51)" }, { name: "SWcrhX8D", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeaForumIdentity.ts (35,51)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var profiles; +(function (profiles) { + var identities; + (function (identities) { + class TeaForumHandshakeHandler extends identities.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new identities.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + data: this.identity.data().data_json() + }).catch(error => { + log.error(LogCategory.IDENTITIES, _translations.W2pDcDNQ || (_translations.W2pDcDNQ = tr("Failed to initialize TeaForum based handshake. Error: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + handle_proof(json) { + this.connection.send_command("handshakeindentityproof", { + proof: this.identity.data().data_sign() + }).catch(error => { + log.error(LogCategory.IDENTITIES, _translations.SWcrhX8D || (_translations.SWcrhX8D = tr("Failed to proof the identity. Error: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } + } + class TeaForumIdentity { + constructor(data) { + this.identity_data = data; + } + valid() { + return !!this.identity_data && !this.identity_data.is_expired(); + } + data() { + return this.identity_data; + } + decode(data) { + data = JSON.parse(data); + if (data.version !== 1) + throw "invalid version"; + return; + } + encode() { + return JSON.stringify({ + version: 1 + }); + } + spawn_identity_handshake_handler(connection) { + return new TeaForumHandshakeHandler(connection, this); + } + fallback_name() { + return this.identity_data ? this.identity_data.name() : undefined; + } + type() { + return identities.IdentitifyType.TEAFORO; + } + uid() { + //FIXME: Real UID! + return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); + } + } + identities.TeaForumIdentity = TeaForumIdentity; + let static_identity; + function set_static_identity(identity) { + static_identity = identity; + } + identities.set_static_identity = set_static_identity; + function update_forum() { + if (forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { + static_identity = new TeaForumIdentity(forum.data()); + } + else { + static_identity = undefined; + } + } + identities.update_forum = update_forum; + function valid_static_forum_identity() { + return static_identity && static_identity.valid(); + } + identities.valid_static_forum_identity = valid_static_forum_identity; + function static_forum_identity() { + return static_identity; + } + identities.static_forum_identity = static_forum_identity; + })(identities = profiles.identities || (profiles.identities = {})); +})(profiles || (profiles = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["bfe72893496c223378f70914d679ecbba6151df3cd16fa95aae3f5832ef4d162"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["bfe72893496c223378f70914d679ecbba6151df3cd16fa95aae3f5832ef4d162"] = "bfe72893496c223378f70914d679ecbba6151df3cd16fa95aae3f5832ef4d162"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "l6DvuihS", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (231,51)" }, { name: "DMNxyJuG", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (247,55)" }, { name: "ekkOaQqE", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (298,55)" }, { name: "pGZABZ0J", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (411,51)" }, { name: "tOjKyP5T", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (419,46)" }, { name: "orYglH6g", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (429,51)" }, { name: "mCXt2f6G", path: "D:/TeaSpeak/web/shared/js/profiles/identities/TeamSpeakIdentity.ts (478,51)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var profiles; +(function (profiles) { + var identities; + (function (identities) { + let CryptoHelper; + (function (CryptoHelper) { + function base64_url_encode(str) { + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); + } + CryptoHelper.base64_url_encode = base64_url_encode; + function base64_url_decode(str, pad) { + if (typeof (pad) === 'undefined' || pad) + str = (str + '===').slice(0, str.length + (str.length % 4)); + return str.replace(/-/g, '+').replace(/_/g, '/'); + } + CryptoHelper.base64_url_decode = base64_url_decode; + function arraybuffer_to_string(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } + CryptoHelper.arraybuffer_to_string = arraybuffer_to_string; + function export_ecc_key(crypto_key, public_key) { + return __awaiter(this, void 0, void 0, function* () { + /* + Tomcrypt public key export: + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + */ + const key_data = yield crypto.subtle.exportKey("jwk", crypto_key); + let index = 0; + const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ + const buffer = new Uint8Array(length); /* fixed ASN1 length */ + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* the flags bit string */ + buffer[index++] = 0x03; /* type */ + buffer[index++] = 0x02; /* length */ + buffer[index++] = 0x07; /* data */ + buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ + } + { /* key size (const 32 for P-256) */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x01; /* length */ + buffer[index++] = 0x20; + } + try { /* Public kex X */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + const raw = atob(base64_url_decode(key_data.x, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse x coordinate (invalid base64)"; + throw error; + } + try { /* Public kex Y */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + const raw = atob(base64_url_decode(key_data.y, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; + } + if (!public_key) { + try { /* Public kex K */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + const raw = atob(base64_url_decode(key_data.d, false)); + if (raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; + } + } + buffer[1] = index - 2; /* set the final sequence length */ + return base64_encode_ab(buffer.buffer.slice(0, index)); + }); + } + CryptoHelper.export_ecc_key = export_ecc_key; + const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; + function c_strlen(buffer, offset) { + let index = 0; + while (index + offset < buffer.length && buffer[index + offset] != 0) + index++; + return index; + } + function decrypt_ts_identity(buffer) { + return __awaiter(this, void 0, void 0, function* () { + /* buffer could contains a zero! */ + const hash = new Uint8Array(yield sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for (let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + const length = Math.min(buffer.length, 100); + for (let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + return arraybuffer_to_string(buffer); + }); + } + CryptoHelper.decrypt_ts_identity = decrypt_ts_identity; + function encrypt_ts_identity(buffer) { + return __awaiter(this, void 0, void 0, function* () { + const length = Math.min(buffer.length, 100); + for (let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + const hash = new Uint8Array(yield sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for (let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + return base64_encode_ab(buffer); + }); + } + CryptoHelper.encrypt_ts_identity = encrypt_ts_identity; + /** + * @param buffer base64 encoded ASN.1 string + */ + function decode_tomcrypt_key(buffer) { + let decoded; + try { + decoded = asn1.decode(atob(buffer)); + } + catch (error) { + if (error instanceof DOMException) + throw "failed to parse key buffer (invalid base64)"; + throw error; + } + let { x, y, k } = { + x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), + y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), + k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) + }; + if (x.length > 32) { + if (x.charCodeAt(0) != 0) + throw "Invalid X coordinate! (Too long)"; + x = x.substr(1); + } + if (y.length > 32) { + if (y.charCodeAt(0) != 0) + throw "Invalid Y coordinate! (Too long)"; + y = y.substr(1); + } + if (k.length > 32) { + if (k.charCodeAt(0) != 0) + throw "Invalid private coordinate! (Too long)"; + k = k.substr(1); + } + /* + console.log("Key x: %s (%d)", btoa(x), x.length); + console.log("Key y: %s (%d)", btoa(y), y.length); + console.log("Key k: %s (%d)", btoa(k), k.length); + */ + return { + crv: "P-256", + d: base64_url_encode(btoa(k)), + x: base64_url_encode(btoa(x)), + y: base64_url_encode(btoa(y)), + ext: true, + key_ops: ["deriveKey", "sign"], + kty: "EC", + }; + } + CryptoHelper.decode_tomcrypt_key = decode_tomcrypt_key; + })(CryptoHelper = identities.CryptoHelper || (identities.CryptoHelper = {})); + class TeaSpeakHandshakeHandler extends identities.AbstractHandshakeIdentityHandler { + constructor(connection, identity) { + super(connection); + this.identity = identity; + this.handler = new identities.HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + publicKey: this.identity.public_key + }).catch(error => { + log.error(LogCategory.IDENTITIES, _translations.l6DvuihS || (_translations.l6DvuihS = tr("Failed to initialize TeamSpeak based handshake. Error: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + handle_proof(json) { + if (!json[0]["digest"]) { + this.trigger_fail("server too old"); + return; + } + this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { + this.connection.send_command("handshakeindentityproof", { proof: proof }).catch(error => { + log.error(LogCategory.IDENTITIES, _translations.DMNxyJuG || (_translations.DMNxyJuG = tr("Failed to proof the identity. Error: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + }).catch(error => { + this.trigger_fail("failed to sign message"); + }); + } + trigger_fail(message) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } + } + class IdentityPOWWorker { + initialize(key) { + return __awaiter(this, void 0, void 0, function* () { + this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); + /* initialize */ + yield new Promise((resolve, reject) => { + const timeout_id = setTimeout(() => reject("timeout"), 1000); + this._worker.onmessage = event => { + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + this._worker.onerror = event => { + log.error(LogCategory.IDENTITIES, _translations.ekkOaQqE || (_translations.ekkOaQqE = tr("POW Worker error %o")), event); + clearTimeout(timeout_id); + reject("Failed to load worker (" + event.message + ")"); + }; + }); + /* set data */ + yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "set_data", + private_key: key, + code: "set_data" + }); + const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); + this._worker.onmessage = event => { + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + }); + }); + } + mine(hash, iterations, target, timeout) { + return __awaiter(this, void 0, void 0, function* () { + this._current_hash = hash; + if (target < this._best_level) + return true; + return yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "mine", + hash: this._current_hash, + iterations: iterations, + target: target, + code: "mine" + }); + const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + if (event.data.result) { + this._best_level = event.data.level; + this._current_hash = event.data.hash; + resolve(true); + } + else { + resolve(false); /* no result */ + } + }; + }); + }); + } + current_hash() { + return this._current_hash; + } + current_level() { + return this._best_level; + } + finalize(timeout) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + resolve(); + }; + }); + } + catch (error) { + log.error(LogCategory.IDENTITIES, _translations.pGZABZ0J || (_translations.pGZABZ0J = tr("Failed to finalize POW worker! (%o)")), error); + } + this._worker.terminate(); + this._worker = undefined; + }); + } + handle_message(message) { + log.info(LogCategory.IDENTITIES, _translations.tOjKyP5T || (_translations.tOjKyP5T = tr("Received message: %o")), message); + } + } + class TeaSpeakIdentity { + constructor(private_key, hash, name, initialize) { + this.private_key = private_key; + this.hash_number = hash || "0"; + this._name = name; + if (this.private_key && (typeof (initialize) === "undefined" || initialize)) { + this.initialize().catch(error => { + log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); + this._initialized = false; + }); + } + } + static generate_new() { + return __awaiter(this, void 0, void 0, function* () { + let key; + try { + key = yield crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); + } + catch (e) { + log.error(LogCategory.IDENTITIES, _translations.orYglH6g || (_translations.orYglH6g = tr("Could not generate a new key: %o")), e); + throw "Failed to generate keypair"; + } + const private_key = yield CryptoHelper.export_ecc_key(key.privateKey, false); + const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); + yield identity.initialize(); + return identity; + }); + } + static import_ts(ts_string, ini) { + return __awaiter(this, void 0, void 0, function* () { + const parse_string = string => { + /* parsing without INI structure */ + const V_index = string.indexOf('V'); + if (V_index == -1) + throw "invalid input (missing V)"; + return { + hash: string.substr(0, V_index), + data: string.substr(V_index + 1), + name: "TeaSpeak user" + }; + }; + const { hash, data, name } = (!ini ? () => parse_string(ts_string) : () => { + /* parsing with INI structure */ + let identity, name; + for (const line of ts_string.split("\n")) { + if (line.startsWith("identity=")) + identity = line.substr(9); + else if (line.startsWith("nickname=")) + name = line.substr(9); + } + if (!identity) + throw "missing identity keyword"; + identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; + if (!identity) + throw "invalid identity key value"; + const result = parse_string(identity); + result.name = name || result.name; + return result; + })(); + if (!ts_string.match(/[0-9]+/g)) + throw "invalid hash!"; + let buffer; + try { + buffer = new Uint8Array(arrayBufferBase64(data)); + } + catch (error) { + log.error(LogCategory.IDENTITIES, _translations.mCXt2f6G || (_translations.mCXt2f6G = tr("Failed to decode given base64 data (%s)")), data); + throw "failed to base data (base64 decode failed)"; + } + const key64 = yield CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); + const identity = new TeaSpeakIdentity(key64, hash, name, false); + yield identity.initialize(); + return identity; + }); + } + fallback_name() { + return this._name; + } + uid() { + return this._unique_id; + } + type() { + return identities.IdentitifyType.TEAMSPEAK; + } + valid() { + return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; + } + decode(data) { + return __awaiter(this, void 0, void 0, function* () { + const json = JSON.parse(data); + if (!json) + throw "invalid json"; + if (json.version == 2) { + this.private_key = json.key; + this.hash_number = json.hash; + this._name = json.name; + } + else if (json.version == 1) { + const key = json.key; + this._name = json.name; + const clone = yield TeaSpeakIdentity.import_ts(key, false); + this.private_key = clone.private_key; + this.hash_number = clone.hash_number; + } + else + throw "invalid version"; + yield this.initialize(); + }); + } + encode() { + return JSON.stringify({ + key: this.private_key, + hash: this.hash_number, + name: this._name, + version: 2 + }); + } + level() { + return __awaiter(this, void 0, void 0, function* () { + if (!this._initialized || !this.public_key) + throw "not initialized"; + const hash = new Uint8Array(yield sha.sha1(this.public_key + this.hash_number)); + let level = 0; + while (level < hash.byteLength && hash[level] == 0) + level++; + if (level >= hash.byteLength) { + level = 256; + } + else { + let byte = hash[level]; + level <<= 3; + while ((byte & 0x1) == 0) { + level++; + byte >>= 1; + } + } + return level; + }); + } + /** + * @param {string} a + * @param {string} b + * @description b must be smaller (in bytes) then a + */ + string_add(a, b) { + const char_result = []; + const char_a = [...a].reverse().map(e => e.charCodeAt(0)); + const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + let carry = false; + while (char_b.length > 0) { + let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; + if ((carry = result > 57)) + result -= 10; + char_result.push(result); + } + while (char_a.length > 0) { + let result = char_a.pop_front() + (carry ? 1 : 0); + if ((carry = result > 57)) + result -= 10; + char_result.push(result); + } + if (carry) + char_result.push(49); + return String.fromCharCode.apply(null, char_result.slice().reverse()); + } + improve_level_for(time, threads) { + return __awaiter(this, void 0, void 0, function* () { + let active = true; + setTimeout(() => active = false, time); + return yield this.improve_level(-1, threads, () => active); + }); + } + improve_level(target, threads, active_callback, callback_level, callback_status) { + return __awaiter(this, void 0, void 0, function* () { + if (!this._initialized || !this.public_key) + throw "not initialized"; + if (target == -1) /* get the highest level possible */ + target = 0; + else if (target <= (yield this.level())) + return true; + const workers = []; + const iterations = 100000; + let current_hash; + const next_hash = () => { + if (!current_hash) + return (current_hash = this.hash_number); + if (current_hash.length < iterations.toString().length) { + current_hash = this.string_add(iterations.toString(), current_hash); + } + else { + current_hash = this.string_add(current_hash, iterations.toString()); + } + return current_hash; + }; + { /* init */ + const initialize_promise = []; + for (let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } + try { + yield Promise.all(initialize_promise); + } + catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } + } + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : (yield this.level()) + 1; + const worker_promise = []; + const hash_timestamps = []; + let last_hashrate_update = 0; + const update_hashrate = () => { + if (!callback_status) + return; + const now = Date.now(); + hash_timestamps.push(now); + if (last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + const timeout = now - 10 * 1000; /* 10s */ + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))); + } + }; + try { + result = yield new Promise((resolve, reject) => { + let active = true; + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; + }; + for (const worker of workers) { + const worker_mine = () => { + if (!active) + return; + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + worker_promise.remove(p); + if (result.valueOf()) { + if (worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if (callback_level) + callback_level(best_level); + } + if (active) { + if (target > 0) + exit(); + else + target_level = best_level + 1; + } + } + if (active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + return Promise.resolve(); + }); + worker_promise.push(p); + }; + worker_mine(); + } + }); + } + catch (error) { + //error already printed before reject had been called + } + { /* shutdown */ + const finalize_promise = []; + for (const worker of workers) + finalize_promise.push(worker.finalize(250)); + try { + yield Promise.all(finalize_promise); + } + catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to finalize"; + } + } + return result; + }); + } + initialize() { + return __awaiter(this, void 0, void 0, function* () { + if (!this.private_key) + throw "Invalid private key"; + let jwk; + try { + jwk = yield CryptoHelper.decode_tomcrypt_key(this.private_key); + if (!jwk) + throw "result undefined"; + } + catch (error) { + throw "failed to parse key (" + error + ")"; + } + try { + this._crypto_key_sign = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ["sign"]); + } + catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto sign key"; + } + try { + this._crypto_key = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); + } + catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto key"; + } + try { + this.public_key = yield CryptoHelper.export_ecc_key(this._crypto_key, true); + this._unique_id = base64_encode_ab(yield sha.sha1(this.public_key)); + } + catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to calculate unique id"; + } + this._initialized = true; + //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); + }); + } + export_ts(ini) { + return __awaiter(this, void 0, void 0, function* () { + if (!this.private_key) + throw "Invalid private key"; + const identity = this.hash_number + "V" + (yield CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key)))); + if (!ini) + return identity; + return "[Identity]\n" + + "id=TeaWeb-Exported\n" + + "identity=\"" + identity + "\"\n" + + "nickname=\"" + this.fallback_name() + "\"\n" + + "phonetic_nickname="; + }); + } + sign_message(message, hash = "SHA-256") { + return __awaiter(this, void 0, void 0, function* () { + /* bring this to libtomcrypt format */ + const sign_buffer = yield crypto.subtle.sign({ + name: "ECDSA", + hash: hash + }, this._crypto_key_sign, str2ab8(message)); + const sign = new Uint8Array(sign_buffer); + /* first 32 r bits | last 32 s bits */ + const buffer = new Uint8Array(72); + let index = 0; + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* integer r */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + if (sign[0] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = sign[i]; + } + { /* integer s */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + if (sign[32] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + for (let i = 0; i < 32; i++) + buffer[index++] = sign[32 + i]; + } + buffer[1] = index - 2; + return base64_encode_ab(buffer.subarray(0, index)); + }); + } + spawn_identity_handshake_handler(connection) { + return new TeaSpeakHandshakeHandler(connection, this); + } + } + identities.TeaSpeakIdentity = TeaSpeakIdentity; + })(identities = profiles.identities || (profiles.identities = {})); +})(profiles || (profiles = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["252393eff9ee9bd3314e7489a9d25e6f643f4cde49f2b32fe1c7029eb0b5daee"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["252393eff9ee9bd3314e7489a9d25e6f643f4cde49f2b32fe1c7029eb0b5daee"] = "252393eff9ee9bd3314e7489a9d25e6f643f4cde49f2b32fe1c7029eb0b5daee"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "S4Ndcg1N", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (43,35)" }, { name: "EB7cq9IQ", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (44,27)" }, { name: "ObF9UjLw", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (54,23)" }, { name: "QqP4ziMI", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (61,31)" }, { name: "CB82sp6h", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (62,23)" }, { name: "XtV0zss8", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (158,37)" }, { name: "Vy5TBBLR", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (159,32)" }, { name: "OBRgr3qI", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (166,32)" }, { name: "aQTPJDUN", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (171,27)" }, { name: "qo0mcyqP", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (174,65)" }, { name: "dySnoVdL", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (179,27)" }, { name: "saqRg2Z9", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (181,27)" }, { name: "pWWqDzP0", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (185,27)" }, { name: "q7ZPaK5Y", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (192,31)" }, { name: "B_aTnd86", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (211,27)" }, { name: "Xp60Q526", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (214,32)" }, { name: "LyfKK5vy", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (240,37)" }, { name: "LGRGmAO1", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (241,32)" }, { name: "qQhEY7WN", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (246,19)" }, { name: "LWrXMvtN", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (250,27)" }, { name: "OyRiiDGL", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (251,52)" }, { name: "mOnXkXgi", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (261,19)" }, { name: "b7eQlRLY", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (263,23)" }, { name: "NKi7YaO9", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (271,27)" }, { name: "UFFPaqTS", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (272,19)" }, { name: "bxKhuXcc", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (298,37)" }, { name: "NxOnZbz3", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (299,32)" }, { name: "zRhhTEw9", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (304,19)" }, { name: "MqQjxf4A", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (308,27)" }, { name: "hJTwy8kO", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (309,52)" }, { name: "TPtST7cV", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (334,29)" }, { name: "oACRN0w_", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (341,31)" }, { name: "W98X1kxg", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (345,30)" }, { name: "v_PzN8TK", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (348,38)" }, { name: "TudD2giw", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (350,38)" }, { name: "SIztajHh", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (356,34)" }, { name: "iv0zFIyj", path: "D:/TeaSpeak/web/shared/js/profiles/identities/teaspeak-forum.ts (362,31)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var forum; +(function (forum) { + let gcaptcha; + (function (gcaptcha) { + function initialize() { + return __awaiter(this, void 0, void 0, function* () { + if (typeof (window.grecaptcha) === "undefined") { + let script = document.createElement("script"); + script.async = true; + let timeout; + const callback_name = "captcha_callback_" + Math.random().toString().replace(".", ""); + try { + yield new Promise((resolve, reject) => { + script.onerror = reject; + window[callback_name] = resolve; + script.src = "https://www.google.com/recaptcha/api.js?onload=" + encodeURIComponent(callback_name) + "&render=explicit"; + document.body.append(script); + timeout = setTimeout(() => reject("timeout"), 15000); + }); + } + catch (error) { + script.remove(); + script = undefined; + console.error(_translations.S4Ndcg1N || (_translations.S4Ndcg1N = tr("Failed to fetch recaptcha javascript source: %o")), error); + throw _translations.EB7cq9IQ || (_translations.EB7cq9IQ = tr("failed to download source")); + } + finally { + if (script) + script.onerror = undefined; + delete window[callback_name]; + clearTimeout(timeout); + } + } + if (typeof (window.grecaptcha) === "undefined") + throw _translations.ObF9UjLw || (_translations.ObF9UjLw = tr("failed to load recaptcha")); + }); + } + gcaptcha.initialize = initialize; + function spawn(container, key, callback_data) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield initialize(); + } + catch (error) { + console.error(_translations.QqP4ziMI || (_translations.QqP4ziMI = tr("Failed to initialize G-Recaptcha. Error: %o")), error); + throw _translations.CB82sp6h || (_translations.CB82sp6h = tr("initialisation failed")); + } + if (container.attr("captcha-uuid")) + window.grecaptcha.reset(container.attr("captcha-uuid")); + else { + container.attr("captcha-uuid", window.grecaptcha.render(container[0], { + "sitekey": key, + callback: callback_data + })); + } + }); + } + gcaptcha.spawn = spawn; + })(gcaptcha = forum.gcaptcha || (forum.gcaptcha = {})); + function api_url() { + return settings.static_global(Settings.KEY_TEAFORO_URL); + } + class Data { + constructor(auth, raw, sign) { + this.auth_key = auth; + this.raw = raw; + this.sign = sign; + this.parsed = JSON.parse(raw); + } + data_json() { return this.raw; } + data_sign() { return this.sign; } + name() { return this.parsed.user_name; } + user_id() { return this.parsed.user_id; } + user_group() { return this.parsed.user_group_id; } + is_stuff() { return this.parsed.is_staff; } + is_premium() { return this.parsed.user_groups.indexOf(5) != -1; } + data_age() { return new Date(this.parsed.data_age); } + is_expired() { return this.parsed.data_age + 48 * 60 * 60 * 1000 < Date.now(); } + should_renew() { return this.parsed.data_age + 24 * 60 * 60 * 1000 < Date.now(); } /* renew data all 24hrs */ + } + forum.Data = Data; + let _data; + function logged_in() { + return !!_data && !_data.is_expired(); + } + forum.logged_in = logged_in; + function data() { return _data; } + forum.data = data; + function login(username, password, captcha) { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/login", + type: "POST", + cache: false, + data: { + username: username, + password: password, + remember: true, + "g-recaptcha-response": captcha + }, + crossDomain: true, + success: resolve, + error: (xhr, status, error) => { + console.log(_translations.XtV0zss8 || (_translations.XtV0zss8 = tr("Login request failed %o: %o")), status, error); + reject(_translations.Vy5TBBLR || (_translations.Vy5TBBLR = tr("request failed"))); + } + }); + }); + } + catch (error) { + return { + status: "error", + error_message: _translations.OBRgr3qI || (_translations.OBRgr3qI = tr("failed to send login request")) + }; + } + if (response["status"] !== "ok") { + console.error(_translations.aQTPJDUN || (_translations.aQTPJDUN = tr("Response status not okey. Error happend: %o")), response); + return { + status: "error", + error_message: (response["errors"] || [])[0] || (_translations.qo0mcyqP || (_translations.qo0mcyqP = tr("Unknown error"))) + }; + } + if (!response["success"]) { + console.error(_translations.dySnoVdL || (_translations.dySnoVdL = tr("Login failed. Response %o")), response); + let message = _translations.saqRg2Z9 || (_translations.saqRg2Z9 = tr("failed to login")); + let captcha; + /* user/password wrong | and maybe captcha required */ + if (response["code"] == 1 || response["code"] == 3) + message = _translations.pWWqDzP0 || (_translations.pWWqDzP0 = tr("Invalid username or password")); + if (response["code"] == 2 || response["code"] == 3) { + captcha = { + type: response["captcha"]["type"], + data: response["captcha"]["siteKey"] //TODO: Why so static here? + }; + if (response["code"] == 2) + message = _translations.q7ZPaK5Y || (_translations.q7ZPaK5Y = tr("captcha required")); + } + return { + status: typeof (captcha) !== "undefined" ? "captcha" : "error", + error_message: message, + captcha: captcha + }; + } + //document.cookie = "user_data=" + response["data"] + ";path=/"; + //document.cookie = "user_sign=" + response["sign"] + ";path=/"; + try { + _data = new Data(response["auth-key"], response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); + profiles.identities.update_forum(); + } + catch (error) { + console.error(_translations.B_aTnd86 || (_translations.B_aTnd86 = tr("Failed to parse forum given data: %o")), error); + return { + status: "error", + error_message: _translations.Xp60Q526 || (_translations.Xp60Q526 = tr("Failed to parse response data")) + }; + } + return { + status: "success" + }; + }); + } + forum.login = login; + function renew_data() { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/renew-data", + type: "GET", + cache: false, + crossDomain: true, + data: { + "auth-key": _data.auth_key + }, + success: resolve, + error: (xhr, status, error) => { + console.log(_translations.LyfKK5vy || (_translations.LyfKK5vy = tr("Renew request failed %o: %o")), status, error); + reject(_translations.LGRGmAO1 || (_translations.LGRGmAO1 = tr("request failed"))); + } + }); + }); + } + catch (error) { + throw _translations.qQhEY7WN || (_translations.qQhEY7WN = tr("failed to send renew request")); + } + if (response["status"] !== "ok") { + console.error(_translations.LWrXMvtN || (_translations.LWrXMvtN = tr("Response status not okey. Error happend: %o")), response); + throw (response["errors"] || [])[0] || (_translations.OyRiiDGL || (_translations.OyRiiDGL = tr("Unknown error"))); + } + if (!response["success"]) { + if (response["code"] == 1) { + return "login-required"; + } + throw "invalid error code (" + response["code"] + ")"; + } + if (!response["data"] || !response["sign"]) + throw _translations.mOnXkXgi || (_translations.mOnXkXgi = tr("response missing data")); + console.debug(_translations.b7eQlRLY || (_translations.b7eQlRLY = tr("Renew succeeded. Parsing data."))); + try { + _data = new Data(_data.auth_key, response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + profiles.identities.update_forum(); + } + catch (error) { + console.error(_translations.NKi7YaO9 || (_translations.NKi7YaO9 = tr("Failed to parse forum given data: %o")), error); + throw _translations.UFFPaqTS || (_translations.UFFPaqTS = tr("failed to parse data")); + } + return "success"; + }); + } + forum.renew_data = renew_data; + function logout() { + return __awaiter(this, void 0, void 0, function* () { + if (!logged_in()) + return; + let response; + try { + response = yield new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/logout", + type: "GET", + cache: false, + crossDomain: true, + data: { + "auth-key": _data.auth_key + }, + success: resolve, + error: (xhr, status, error) => { + console.log(_translations.bxKhuXcc || (_translations.bxKhuXcc = tr("Logout request failed %o: %o")), status, error); + reject(_translations.NxOnZbz3 || (_translations.NxOnZbz3 = tr("request failed"))); + } + }); + }); + } + catch (error) { + throw _translations.zRhhTEw9 || (_translations.zRhhTEw9 = tr("failed to send logout request")); + } + if (response["status"] !== "ok") { + console.error(_translations.MqQjxf4A || (_translations.MqQjxf4A = tr("Response status not okey. Error happend: %o")), response); + throw (response["errors"] || [])[0] || (_translations.hJTwy8kO || (_translations.hJTwy8kO = tr("Unknown error"))); + } + if (!response["success"]) { + /* code 1 means not logged in, its an success */ + if (response["code"] != 1) { + throw "invalid error code (" + response["code"] + ")"; + } + } + _data = undefined; + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + profiles.identities.update_forum(); + }); + } + forum.logout = logout; + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "TeaForo initialize", + priority: 10, + function: () => __awaiter(this, void 0, void 0, function* () { + const raw_data = localStorage.getItem("teaspeak-forum-data"); + const raw_sign = localStorage.getItem("teaspeak-forum-sign"); + const forum_auth = localStorage.getItem("teaspeak-forum-auth"); + if (!raw_data || !raw_sign || !forum_auth) { + console.log(_translations.TPtST7cV || (_translations.TPtST7cV = tr("No TeaForo authentification found. TeaForo connection status: unconnected"))); + return; + } + try { + _data = new Data(forum_auth, raw_data, raw_sign); + } + catch (error) { + console.error(_translations.oACRN0w_ || (_translations.oACRN0w_ = tr("Failed to initialize TeaForo connection from local data. Error: %o")), error); + return; + } + if (_data.should_renew()) { + console.info(_translations.W98X1kxg || (_translations.W98X1kxg = tr("TeaForo data should be renewed. Executing renew."))); + renew_data().then(status => { + if (status === "success") { + console.info(_translations.v_PzN8TK || (_translations.v_PzN8TK = tr("TeaForo data has been successfully renewed."))); + } + else { + console.warn(_translations.TudD2giw || (_translations.TudD2giw = tr("Failed to renew TeaForo data. New login required."))); + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + } + }).catch(error => { + console.warn(_translations.SIztajHh || (_translations.SIztajHh = tr("Failed to renew TeaForo data. An error occurred: %o")), error); + }); + return; + } + if (_data && _data.is_expired()) { + console.error(_translations.iv0zFIyj || (_translations.iv0zFIyj = tr("TeaForo data is expired. TeaForo connection isn't available!"))); + } + }) + }); +})(forum || (forum = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3ac4bc90640ab00e41ec66d414aef709d86113a645f77b253e52e9efeb40b68d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3ac4bc90640ab00e41ec66d414aef709d86113a645f77b253e52e9efeb40b68d"] = "3ac4bc90640ab00e41ec66d414aef709d86113a645f77b253e52e9efeb40b68d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "aSWrAHYB", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (233,25)" }, { name: "UkmzxtCM", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (252,41)" }, { name: "kN3PHhau", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (262,45)" }, { name: "qr1gpdRg", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (270,49)" }, { name: "HoO3dFkw", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (282,49)" }, { name: "jBQ2VSvY", path: "D:/TeaSpeak/web/shared/js/sound/Sounds.ts (289,45)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Sound; +(function (Sound) { + Sound["SOUND_TEST"] = "sound.test"; + Sound["SOUND_EGG"] = "sound.egg"; + Sound["AWAY_ACTIVATED"] = "away_activated"; + Sound["AWAY_DEACTIVATED"] = "away_deactivated"; + Sound["MICROPHONE_MUTED"] = "microphone.muted"; + Sound["MICROPHONE_ACTIVATED"] = "microphone.activated"; + Sound["SOUND_MUTED"] = "sound.muted"; + Sound["SOUND_ACTIVATED"] = "sound.activated"; + Sound["CONNECTION_CONNECTED"] = "connection.connected"; + Sound["CONNECTION_DISCONNECTED"] = "connection.disconnected"; + Sound["CONNECTION_BANNED"] = "connection.banned"; + Sound["CONNECTION_DISCONNECTED_TIMEOUT"] = "connection.disconnected.timeout"; + Sound["CONNECTION_REFUSED"] = "connection.refused"; + Sound["SERVER_EDITED"] = "server.edited"; + Sound["SERVER_EDITED_SELF"] = "server.edited.self"; + Sound["SERVER_KICKED"] = "server.kicked"; + Sound["CHANNEL_CREATED"] = "channel.created"; + Sound["CHANNEL_MOVED"] = "channel.moved"; + Sound["CHANNEL_EDITED"] = "channel.edited"; + Sound["CHANNEL_EDITED_SELF"] = "channel.edited.self"; + Sound["CHANNEL_DELETED"] = "channel.deleted"; + Sound["CHANNEL_JOINED"] = "channel.joined"; + Sound["CHANNEL_KICKED"] = "channel.kicked"; + Sound["USER_MOVED"] = "user.moved"; + Sound["USER_MOVED_SELF"] = "user.moved.self"; + Sound["USER_POKED_SELF"] = "user.poked.self"; + Sound["USER_BANNED"] = "user.banned"; + Sound["USER_ENTERED"] = "user.joined"; + Sound["USER_ENTERED_MOVED"] = "user.joined.moved"; + Sound["USER_ENTERED_KICKED"] = "user.joined.kicked"; + Sound["USER_ENTERED_CONNECT"] = "user.joined.connect"; + Sound["USER_LEFT"] = "user.left"; + Sound["USER_LEFT_MOVED"] = "user.left.moved"; + Sound["USER_LEFT_KICKED_CHANNEL"] = "user.left.kicked.server"; + Sound["USER_LEFT_KICKED_SERVER"] = "user.left.kicked.channel"; + Sound["USER_LEFT_DISCONNECT"] = "user.left.disconnect"; + Sound["USER_LEFT_BANNED"] = "user.left.banned"; + Sound["USER_LEFT_TIMEOUT"] = "user.left.timeout"; + Sound["ERROR_INSUFFICIENT_PERMISSIONS"] = "error.insufficient_permissions"; + Sound["MESSAGE_SEND"] = "message.send"; + Sound["MESSAGE_RECEIVED"] = "message.received"; + Sound["GROUP_SERVER_ASSIGNED"] = "group.server.assigned"; + Sound["GROUP_SERVER_REVOKED"] = "group.server.revoked"; + Sound["GROUP_CHANNEL_CHANGED"] = "group.channel.changed"; + Sound["GROUP_SERVER_ASSIGNED_SELF"] = "group.server.assigned.self"; + Sound["GROUP_SERVER_REVOKED_SELF"] = "group.server.revoked.self"; + Sound["GROUP_CHANNEL_CHANGED_SELF"] = "group.channel.changed.self"; +})(Sound || (Sound = {})); +var sound; +(function (sound_1) { + let speech_mapping = {}; + let volume_require_save = false; + let speech_volume = {}; + let master_volume; + let overlap_sounds; + let ignore_muted; + let master_mixed; + function register_sound(key, file) { + speech_mapping[key] = { key: key, filename: file }; + } + function get_sound_volume(sound, default_volume) { + let result = speech_volume[sound]; + if (typeof (result) === "undefined") { + if (typeof (default_volume) !== "undefined") + result = default_volume; + else + result = 1; + } + return result; + } + sound_1.get_sound_volume = get_sound_volume; + function set_sound_volume(sound, volume) { + volume_require_save = volume_require_save || speech_volume[sound] != volume; + speech_volume[sound] = volume == 1 ? undefined : volume; + } + sound_1.set_sound_volume = set_sound_volume; + function get_master_volume() { + return master_volume; + } + sound_1.get_master_volume = get_master_volume; + function set_master_volume(volume) { + volume_require_save = volume_require_save || master_volume != volume; + master_volume = volume; + if (master_mixed) { + if (master_mixed.gain.setValueAtTime) + master_mixed.gain.setValueAtTime(volume, 0); + else + master_mixed.gain.value = volume; + } + } + sound_1.set_master_volume = set_master_volume; + function overlap_activated() { + return overlap_sounds; + } + sound_1.overlap_activated = overlap_activated; + function set_overlap_activated(flag) { + volume_require_save = volume_require_save || overlap_sounds != flag; + overlap_sounds = flag; + } + sound_1.set_overlap_activated = set_overlap_activated; + function ignore_output_muted() { + return ignore_muted; + } + sound_1.ignore_output_muted = ignore_output_muted; + function set_ignore_output_muted(flag) { + volume_require_save = volume_require_save || ignore_muted != flag; + ignore_muted = flag; + } + sound_1.set_ignore_output_muted = set_ignore_output_muted; + function reinitialisize_audio() { + const context = audio.player.context(); + const destination = audio.player.destination(); + if (master_mixed) + master_mixed.disconnect(); + master_mixed = context.createGain(); + if (master_mixed.gain.setValueAtTime) + master_mixed.gain.setValueAtTime(master_volume, 0); + else + master_mixed.gain.value = master_volume; + master_mixed.connect(destination); + } + sound_1.reinitialisize_audio = reinitialisize_audio; + function save() { + if (volume_require_save) { + volume_require_save = false; + const data = {}; + data.version = 1; + for (const key in Sound) { + if (typeof (speech_volume[Sound[key]]) !== "undefined") + data[Sound[key]] = speech_volume[Sound[key]]; + } + data.master = master_volume; + data.overlap = overlap_sounds; + data.ignore_muted = ignore_muted; + settings.changeGlobal("sound_volume", JSON.stringify(data)); + } + } + sound_1.save = save; + function initialize() { + $.ajaxSetup({ + beforeSend: function (jqXHR, settings) { + if (settings.dataType === 'binary') { + settings.xhr().responseType = 'arraybuffer'; + settings.processData = false; + } + } + }); + /* volumes */ + { + const data = JSON.parse(settings.static_global("sound_volume", "{}")); + for (const sound_key in Sound) { + if (typeof (data[Sound[sound_key]]) !== "undefined") + speech_volume[Sound[sound_key]] = data[Sound[sound_key]]; + } + master_volume = typeof (data.master) === "number" ? data.master : 1; + overlap_sounds = typeof (data.overlap) === "boolean" ? data.overlap : true; + ignore_muted = typeof (data.ignore_muted) === "boolean" ? data.ignore_muted : false; + } + register_sound("message.received", "effects/message_received.wav"); + register_sound("message.send", "effects/message_send.wav"); + sound_1.manager = new SoundManager(undefined); + audio.player.on_ready(reinitialisize_audio); + return new Promise((resolve, reject) => { + $.ajax({ + url: "audio/speech/mapping.json", + success: response => { + if (typeof (response) === "string") + response = JSON.parse(response); + for (const entry of response) + register_sound(entry.key, "speech/" + entry.file); + resolve(); + }, + error: error => { + log.error(LogCategory.AUDIO, "error: %o", error); + reject(); + }, + timeout: 5000, + async: true, + type: 'GET' + }); + }); + } + sound_1.initialize = initialize; + function resolve_sound(sound) { + return __awaiter(this, void 0, void 0, function* () { + const file = speech_mapping[sound]; + if (!file) + throw _translations.aSWrAHYB || (_translations.aSWrAHYB = tr("Missing sound handle")); + return file; + }); + } + sound_1.resolve_sound = resolve_sound; + class SoundManager { + constructor(handle) { + this._playing_sounds = {}; + this._handle = handle; + } + play(_sound, options) { + options = options || {}; + const volume = get_sound_volume(_sound, options.default_volume); + log.info(LogCategory.AUDIO, _translations.UkmzxtCM || (_translations.UkmzxtCM = tr("Replaying sound %s (Sound volume: %o | Master volume %o)")), _sound, volume, master_volume); + if (volume == 0 || master_volume == 0) + return; + if (this._handle && !options.ignore_muted && !sound.ignore_output_muted() && this._handle.client_status.output_muted) + return; + const context = audio.player.context(); + if (!context) { + log.warn(LogCategory.AUDIO, _translations.kN3PHhau || (_translations.kN3PHhau = tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback")), _sound); + return; + } + sound.resolve_sound(_sound).then(handle => { + if (!handle) + return; + if (!options.ignore_overlap && (this._playing_sounds[handle.filename] > 0) && !sound.overlap_activated()) { + log.info(LogCategory.AUDIO, _translations.qr1gpdRg || (_translations.qr1gpdRg = tr("Dropping requested playback for sound %s because it would overlap.")), _sound); + return; + } + this._playing_sounds[handle.filename] = (this._playing_sounds[handle.filename] || 0) + 1; + audio.sounds.play_sound({ + path: "audio/" + handle.filename, + volume: volume * master_volume + }).then(() => { + if (options.callback) + options.callback(true); + }).catch(error => { + log.warn(LogCategory.AUDIO, _translations.HoO3dFkw || (_translations.HoO3dFkw = tr("Failed to replay sound %s: %o")), handle.filename, error); + if (options.callback) + options.callback(false); + }).then(() => { + this._playing_sounds[handle.filename]--; + }); + }).catch(error => { + log.warn(LogCategory.AUDIO, _translations.jBQ2VSvY || (_translations.jBQ2VSvY = tr("Failed to replay sound %o because it could not be resolved: %o")), sound, error); + if (options.callback) + options.callback(false); + }); + } + } + sound_1.SoundManager = SoundManager; +})(sound || (sound = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["94fd90a83218a1cef495ffe767f26df95b1521f409477f9ee4d9e716cb205b4d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["94fd90a83218a1cef495ffe767f26df95b1521f409477f9ee4d9e716cb205b4d"] = "94fd90a83218a1cef495ffe767f26df95b1521f409477f9ee4d9e716cb205b4d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "L1w47X12", path: "D:/TeaSpeak/web/shared/js/ui/htmltags.ts (41,30)" }, { name: "tylczNSM", path: "D:/TeaSpeak/web/shared/js/ui/htmltags.ts (49,30)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var htmltags; +(function (htmltags) { + let mouse_coordinates = { x: 0, y: 0 }; + function initialize() { + document.addEventListener('mousemove', event => { + mouse_coordinates.x = event.pageX; + mouse_coordinates.y = event.pageY; + }); + } + initialize(); + /* required for the bbcodes */ + function generate_client_open(properties) { + let result = ""; + /* build the opening tag:
*/ + result = result + "
"; + return result; + } + function generate_client(properties) { + let result = generate_client_open(properties); + /* content */ + { + if (properties.add_braces) + result = result + "\""; + result = result + MessageHelper.htmlEscape(properties.client_name || "undefined").join(" "); + if (properties.add_braces) + result = result + "\""; + } + /* close tag */ + { + result += "
"; + } + return result; + } + htmltags.generate_client = generate_client; + function generate_client_object(properties) { + return $(this.generate_client(properties)); + } + htmltags.generate_client_object = generate_client_object; + /* required for the bbcodes */ + function generate_channel_open(properties) { + let result = ""; + /* build the opening tag:
*/ + result = result + "
"; + return result; + } + function generate_channel(properties) { + let result = generate_channel_open(properties); + /* content */ + { + if (properties.add_braces) + result = result + "\""; + result = result + MessageHelper.htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" "); + if (properties.add_braces) + result = result + "\""; + } + /* close tag */ + { + result += "
"; + } + return result; + } + htmltags.generate_channel = generate_channel; + function generate_channel_object(properties) { + return $(this.generate_channel(properties)); + } + htmltags.generate_channel_object = generate_channel_object; + let callbacks; + (function (callbacks) { + function callback_context_client(element) { + const client_id = parseInt(element.attr("client-id") || "0"); + const client_unique_id = decodeURIComponent(element.attr("client-unique-id") || ""); + /* we ignore the name, we cant find clients by name because the name is too volatile*/ + let client; + const current_connection = server_connections.active_connection_handler(); + if (current_connection && current_connection.channelTree) { + if (!client && client_id) { + client = current_connection.channelTree.findClient(client_id); + if (client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) { + client = undefined; /* client id dosn't match anymore, lets search for the unique id */ + } + } + if (!client && client_unique_id) + client = current_connection.channelTree.find_client_by_unique_id(client_unique_id); + if (!client) { + if (current_connection.channelTree.server.properties.virtualserver_unique_identifier === client_unique_id) { + current_connection.channelTree.server.spawnContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return; + } + } + } + if (!client) { + /* we may should open a "offline" menu? */ + log.debug(LogCategory.GENERAL, "Failed to resolve client from html tag. Client id: %o, Client unique id: %o, Client name: %o", client_id, client_unique_id, decodeURIComponent(element.attr("client-name"))); + return false; + } + client.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return false; + } + callbacks.callback_context_client = callback_context_client; + function callback_context_channel(element) { + const channel_id = parseInt(element.attr("channel-id") || "0"); + const current_connection = server_connections.active_connection_handler(); + let channel; + if (current_connection && current_connection.channelTree) { + channel = current_connection.channelTree.findChannel(channel_id); + } + if (!channel) + return false; + channel.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return false; + } + callbacks.callback_context_channel = callback_context_channel; + })(callbacks = htmltags.callbacks || (htmltags.callbacks = {})); + let bbcodes; + (function (bbcodes) { + /* the = because we sometimes get that */ + //const url_client_regex = /?client:\/\/(?[0-9]+)\/(?[a-zA-Z0-9+=#]+)~(?(?:[^%]|%[0-9A-Fa-f]{2})+)$/g; + const url_client_regex = /client:\/\/([0-9]+)\/([a-zA-Z0-9+=/#]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; /* IDK which browsers already support group naming */ + const url_channel_regex = /channel:\/\/([0-9]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; + function initialize() { + const origin_url = xbbcode.register.find_parser('url'); + xbbcode.register.register_parser({ + tag: 'url', + build_html_tag_open(layer) { + if (layer.options) { + if (layer.options.match(url_channel_regex)) { + const groups = url_channel_regex.exec(layer.options); + return generate_channel_open({ + add_braces: false, + channel_id: parseInt(groups[1]), + channel_name: decodeURIComponent(groups[2]) + }); + } + else if (layer.options.match(url_client_regex)) { + const groups = url_client_regex.exec(layer.options); + return generate_client_open({ + add_braces: false, + client_id: parseInt(groups[1]), + client_unique_id: groups[2], + client_name: decodeURIComponent(groups[3]) + }); + } + } + return origin_url.build_html_tag_open(layer); + }, + build_html_tag_close(layer) { + if (layer.options) { + if (layer.options.match(url_client_regex)) + return "
"; + if (layer.options.match(url_channel_regex)) + return "
"; + } + return origin_url.build_html_tag_close(layer); + } + }); + /* + "img": { + openTag: function(params,content) { + let myUrl; + + if (!params) { + myUrl = content.replace(/<.*?>/g,""); + } else { + myUrl = params.substr(1); + } + + urlPattern.lastIndex = 0; + if ( !urlPattern.test( myUrl ) ) { + myUrl = "#"; + } + + return ''; + }, + closeTag: function(params,content) { + return ''; + } + }, + */ + } + initialize(); + })(bbcodes || (bbcodes = {})); +})(htmltags || (htmltags = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e015f9e59a056d21357482ef065db99f87293c0c9049bdfeb1cb3c1515932bec"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e015f9e59a056d21357482ef065db99f87293c0c9049bdfeb1cb3c1515932bec"] = "e015f9e59a056d21357482ef065db99f87293c0c9049bdfeb1cb3c1515932bec"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "sXkKn_Vj", path: "D:/TeaSpeak/web/shared/js/ui/elements/context_divider.ts (128,56)" }, { name: "yvApqoiK", path: "D:/TeaSpeak/web/shared/js/ui/elements/context_divider.ts (133,56)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +if (!$.fn.dividerfy) { + $.fn.dividerfy = function () { + this.find(".container-seperator").each(function () { + if (!this.previousElementSibling) + return; + if (!this.nextElementSibling) + return; + const element = $(this); + const parent_element = $(this.parentElement); + const previous_element = $(this.previousElementSibling); + const next_element = $(this.nextElementSibling); + const seperator_id = element.attr("seperator-id"); + const vertical = element.hasClass("vertical"); + const apply_view = (property, previous, next) => { + if (previous + next != 100) { + //Fix values if they dont addup to 100 + const diff = 100 - (previous + next); + previous += diff * previous / (previous + next); + next += diff * next / (previous + next); + //Some minor adjustments due to roundings + next += 100 - (previous + next); + } + const center = (vertical ? element.width() : element.height()) / 2; + const value_a = "calc(" + previous + "% - " + center + "px)"; + const value_b = "calc(" + next + "% - " + center + "px)"; + /* dont cause a reflow here */ + if (property === "height") { + previous_element[0].style.height = value_a; + next_element[0].style.height = value_b; + } + else { + previous_element[0].style.width = value_a; + next_element[0].style.width = value_b; + } + }; + const listener_move = (event) => { + const parent_offset = parent_element.offset(); + const min = vertical ? parent_offset.left : parent_offset.top; + const max = vertical ? parent_offset.left + parent_element.width() : parent_offset.top + parent_element.height(); + const current = event instanceof MouseEvent ? + (vertical ? event.pageX : event.pageY) : + (vertical ? event.touches[event.touches.length - 1].clientX : event.touches[event.touches.length - 1].clientY); + /* + const previous_offset = previous_element.offset(); + const next_offset = next_element.offset(); + + const min = vertical ? Math.min(previous_offset.left, next_offset.left) : Math.min(previous_offset.top, next_offset.top); + const max = vertical ? + Math.max(previous_offset.left + previous_element.width(), next_offset.left + next_element.width()) : + Math.max(previous_offset.top + previous_element.height(), next_offset.top + next_element.height()); + */ + let previous = 0; + let next = 0; + if (current < min) { + previous = 0; + next = 1; + } + else if (current < max) { + const x_offset = current - min; + const x_offset_max = max - min; + previous = x_offset / x_offset_max; + next = 1 - previous; + } + else { + previous = 1; + next = 0; + } + //console.log(min + " - " + max + " - " + current); + const property = vertical ? "width" : "height"; + const previous_p = Math.ceil(previous * 100); + const next_p = Math.ceil(next * 100); + apply_view(property, previous_p, next_p); + if (seperator_id) + settings.changeGlobal("seperator-settings-" + seperator_id, JSON.stringify({ + previous: previous_p, + next: next_p, + property: property + })); + }; + const listener_up = (event) => { + document.removeEventListener('mousemove', listener_move); + document.removeEventListener('touchmove', listener_move); + document.removeEventListener('mouseup', listener_up); + document.removeEventListener('touchend', listener_up); + document.removeEventListener('touchcancel', listener_up); + $(document.documentElement).css("user-select", ""); + element.removeClass("seperator-selected"); + next_element.find("[x-divider-require-resize]").trigger('resize'); + previous_element.find("[x-divider-require-resize]").trigger('resize'); + }; + element.on('mousedown', () => { + document.addEventListener('mousemove', listener_move); + document.addEventListener('touchmove', listener_move); + document.addEventListener('mouseup', listener_up); + document.addEventListener('touchend', listener_up); + document.addEventListener('touchcancel', listener_up); + $(document.documentElement).css("user-select", "none"); + element.addClass("seperator-selected"); + }); + element.on('touchstart', () => { + element.trigger('mousedown'); + }); + if (seperator_id) { + try { + const config = JSON.parse(settings.global("seperator-settings-" + seperator_id)); + if (config) { + log.debug(LogCategory.GENERAL, _translations.sXkKn_Vj || (_translations.sXkKn_Vj = tr("Applying previous changed sperator settings for %s: %o")), seperator_id, config); + apply_view(config.property, config.previous, config.next); + } + } + catch (e) { + if (!(e instanceof SyntaxError)) + log.error(LogCategory.GENERAL, _translations.yvApqoiK || (_translations.yvApqoiK = tr("Failed to parse seperator settings for sperator %s: %o")), seperator_id, e); + } + } + }); + return this; + }; +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["419e31bbcbccf7164faea307d49009f573280d58339c5f7ec7f7d00e4dcc73d0"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["419e31bbcbccf7164faea307d49009f573280d58339c5f7ec7f7d00e4dcc73d0"] = "419e31bbcbccf7164faea307d49009f573280d58339c5f7ec7f7d00e4dcc73d0"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var net; +(function (net) { + var graph; + (function (graph) { + /* Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves + * + * Assuming A was the last point in the line plotted and B is the new point, + * we draw a curve with control points P and Q as below. + * + * A---P + * | + * | + * | + * Q---B + * + * Importantly, A and P are at the same y coordinate, as are B and Q. This is + * so adjacent curves appear to flow as one. + */ + class Graph { + constructor(canvas) { + this.style = { + background_color: "#28292b", + //background_color: "red", + separator_color: "#283036", + //separator_color: 'blue', + separator_count: 10, + separator_width: 1, + upload: { + fill: "#2d3f4d", + stroke: "#336e9f", + strike_width: 2, + }, + download: { + fill: "#532c26", + stroke: "#a9321c", + strike_width: 2, + } + }; + this._entries = []; + this._entry_max = { + upload: 1, + download: 1, + }; + this._max_space = 1.12; + this._max_gap = 5; + this._time_span = { + origin: { + begin: 0, + end: 1, + time: 0 + }, + target: { + begin: 0, + end: 1, + time: 1 + } + }; + this._detailed_shown = false; + this.canvas = canvas; + this._animate_loop = () => this.draw(); + this.recalculate_cache(); /* initialize cache */ + } + initialize() { + this._canvas_context = this.canvas.getContext("2d"); + Graph._loops.push(this._animate_loop); + if (Graph._loops.length == 1) { + const static_loop = () => { + Graph._loops.forEach(l => l()); + if (Graph._loops.length > 0) + requestAnimationFrame(static_loop); + else + console.log("STATIC terminate!"); + }; + static_loop(); + } + this.canvas.onmousemove = this.on_mouse_move.bind(this); + this.canvas.onmouseleave = this.on_mouse_leave.bind(this); + } + terminate() { + Graph._loops.remove(this._animate_loop); + } + max_gap_size(value) { return typeof (value) === "number" ? (this._max_gap = value) : this._max_gap; } + recalculate_cache(time_span) { + this._entries = this._entries.sort((a, b) => a.timestamp - b.timestamp); + this._entry_max = { + download: 1, + upload: 1 + }; + if (time_span) { + this._time_span = { + origin: { + begin: 0, + end: 0, + time: 0 + }, + target: { + begin: this._entries.length > 0 ? this._entries[0].timestamp : 0, + end: this._entries.length > 0 ? this._entries.last().timestamp : 0, + time: 0 + } + }; + } + for (const entry of this._entries) { + if (typeof (entry.upload) === "number") + this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload); + if (typeof (entry.download) === "number") + this._entry_max.download = Math.max(this._entry_max.download, entry.download); + } + this._entry_max.upload *= this._max_space; + this._entry_max.download *= this._max_space; + } + insert_entry(entry) { + if (this._entries.length > 0 && entry.timestamp < this._entries.last().timestamp) + throw "invalid timestamp"; + this._entries.push(entry); + if (typeof (entry.upload) === "number") + this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload * this._max_space); + if (typeof (entry.download) === "number") + this._entry_max.download = Math.max(this._entry_max.download, entry.download * this._max_space); + } + insert_entries(entries) { + this._entries.push(...entries); + this.recalculate_cache(); + this.cleanup(); + } + resize() { + this.canvas.style.height = "100%"; + this.canvas.style.width = "100%"; + const cstyle = getComputedStyle(this.canvas); + this.canvas.width = parseInt(cstyle.width); + this.canvas.height = parseInt(cstyle.height); + } + cleanup() { + const time = this.calculate_time_span(); + let index = 0; + for (; index < this._entries.length; index++) { + if (this._entries[index].timestamp < time.begin) + continue; + if (index == 0) + return; + break; + } + /* keep the last entry as a reference point to the left */ + if (index > 1) { + this._entries.splice(0, index - 1); + this.recalculate_cache(); + } + } + calculate_time_span() { + const time = Date.now(); + if (time >= this._time_span.target.time) + return this._time_span.target; + if (time <= this._time_span.origin.time) + return this._time_span.origin; + const ob = this._time_span.origin.begin; + const oe = this._time_span.origin.end; + const ot = this._time_span.origin.time; + const tb = this._time_span.target.begin; + const te = this._time_span.target.end; + const tt = this._time_span.target.time; + const offset = (time - ot) / (tt - ot); + return { + begin: ob + (tb - ob) * offset, + end: oe + (te - oe) * offset, + }; + } + draw() { + let ctx = this._canvas_context; + const height = this.canvas.height; + const width = this.canvas.width; + //console.log("Painting on %ox%o", height, width); + ctx.shadowBlur = 0; + ctx.filter = ""; + ctx.lineCap = "square"; + ctx.fillStyle = this.style.background_color; + ctx.fillRect(0, 0, width, height); + /* first of all print the separators */ + { + const sw = this.style.separator_width; + const swh = this.style.separator_width / 2; + ctx.lineWidth = sw; + ctx.strokeStyle = this.style.separator_color; + ctx.beginPath(); + /* horizontal */ + { + const dw = width / this.style.separator_count; + let dx = dw / 2; + while (dx < width) { + ctx.moveTo(Math.floor(dx - swh) + .5, .5); + ctx.lineTo(Math.floor(dx - swh) + .5, Math.floor(height) + .5); + dx += dw; + } + } + /* vertical */ + { + const dh = height / 3; //tree lines (top, center, bottom) + let dy = dh / 2; + while (dy < height) { + ctx.moveTo(.5, Math.floor(dy - swh) + .5); + ctx.lineTo(Math.floor(width) + .5, Math.floor(dy - swh) + .5); + dy += dh; + } + } + ctx.stroke(); + ctx.closePath(); + } + /* draw the lines */ + { + const t = this.calculate_time_span(); + const tb = t.begin; /* time begin */ + const dt = t.end - t.begin; /* delta time */ + const dtw = width / dt; /* delta time width */ + const draw_graph = (type, direction, max) => { + const hy = Math.floor(height / 2); /* half y */ + const by = hy - direction * this.style[type].strike_width; /* the "base" line */ + const marked_points = []; + ctx.beginPath(); + ctx.moveTo(0, by); + let x, y, lx = 0, ly = by; /* last x, last y */ + const floor = a => a; //Math.floor; + for (const entry of this._entries) { + x = floor((entry.timestamp - tb) * dtw); + if (typeof entry[type] === "number") + y = floor(hy - direction * Math.max(hy * (entry[type] / max), this.style[type].strike_width)); + else + y = hy - direction * this.style[type].strike_width; + if (entry.timestamp < tb) { + lx = x; + ly = y; + continue; + } + if (x - lx > this._max_gap && this._max_gap > 0) { + ctx.lineTo(lx, by); + ctx.lineTo(x, by); + ctx.lineTo(x, y); + lx = x; + ly = y; + continue; + } + ctx.bezierCurveTo((x + lx) / 2, ly, (x + lx) / 2, y, x, y); + if (entry.highlight) + marked_points.push({ x: x, y: y }); + lx = x; + ly = y; + } + ctx.strokeStyle = this.style[type].stroke; + ctx.lineWidth = this.style[type].strike_width; + ctx.lineJoin = "miter"; + ctx.stroke(); + //Close the path and fill + ctx.lineTo(width, hy); + ctx.lineTo(0, hy); + ctx.fillStyle = this.style[type].fill; + ctx.fill(); + ctx.closePath(); + { + ctx.beginPath(); + const radius = 3; + for (const point of marked_points) { + ctx.moveTo(point.x, point.y); + ctx.ellipse(point.x, point.y, radius, radius, 0, 0, 2 * Math.PI, false); + } + ctx.stroke(); + ctx.fill(); + ctx.closePath(); + } + }; + const shared_max = Math.max(this._entry_max.upload, this._entry_max.download); + draw_graph("upload", 1, shared_max); + draw_graph("download", -1, shared_max); + } + } + on_mouse_move(event) { + const offset = event.offsetX; + const max_offset = this.canvas.width; + if (offset < 0) + return; + if (offset > max_offset) + return; + const time_span = this.calculate_time_span(); + const time = time_span.begin + (time_span.end - time_span.begin) * (offset / max_offset); + let index = 0; + for (; index < this._entries.length; index++) { + if (this._entries[index].timestamp > time) + break; + } + const entry_before = this._entries[index - 1]; /* In JS negative array access is allowed and returns undefined */ + const entry_next = this._entries[index]; /* In JS negative array access is allowed and returns undefined */ + let entry; + if (!entry_before || !entry_next) { + entry = entry_before || entry_next; + } + else { + const dn = entry_next.timestamp - time; + const db = time - entry_before.timestamp; + if (dn > db) + entry = entry_before; + else + entry = entry_next; + } + if (!entry) { + this.on_mouse_leave(event); + } + else { + this._entries.forEach(e => e.highlight = false); + this._detailed_shown = true; + entry.highlight = true; + if (this.callback_detailed_info) + this.callback_detailed_info(entry.upload, entry.download, entry.timestamp, event); + } + } + on_mouse_leave(event) { + if (!this._detailed_shown) + return; + this._detailed_shown = false; + this._entries.forEach(e => e.highlight = false); + if (this.callback_detailed_hide) + this.callback_detailed_hide(); + } + } + Graph._loops = []; + graph.Graph = Graph; + })(graph = net.graph || (net.graph = {})); +})(net || (net = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c82d4662614adf5608bef1fd70a57019738a40114a7c07126b0c1de2799c39de"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c82d4662614adf5608bef1fd70a57019738a40114a7c07126b0c1de2799c39de"] = "c82d4662614adf5608bef1fd70a57019738a40114a7c07126b0c1de2799c39de"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +function sliderfy(slider, options) { + options = Object.assign({ + initial_value: 0, + min_value: 0, + max_value: 100, + step: 1, + unit: '%', + value_field: [] + }, options); + if (!Array.isArray(options.value_field)) + options.value_field = [options.value_field]; + if (options.min_value >= options.max_value) + throw "invalid range"; + if (options.step > (options.max_value - options.min_value)) + throw "invalid step size"; + const tool = tooltip(slider); /* add the tooltip functionality */ + const filler = slider.find(".filler"); + const thumb = slider.find(".thumb"); + const tooltip_text = slider.find(".tooltip a"); + let _current_value; + const update_value = (value, trigger_change) => { + _current_value = value; + const offset = Math.min(100, Math.max(0, ((value - options.min_value) * 100) / (options.max_value - options.min_value))); + filler.css('width', offset + '%'); + thumb.css('left', offset + '%'); + tooltip_text.text(value.toFixed(0) + options.unit); + slider.attr("value", value); + if (trigger_change) + slider.trigger('change'); + for (const field of options.value_field) + field.text(value + options.unit); + tool.update(); + }; + const mouse_up_listener = () => { + document.removeEventListener('mousemove', mouse_listener); + document.removeEventListener('touchmove', mouse_listener); + document.removeEventListener('mouseup', mouse_up_listener); + document.removeEventListener('touchend', mouse_up_listener); + document.removeEventListener('touchcancel', mouse_up_listener); + tool.hide(); + slider.removeClass("active"); + console.log("Events removed"); + }; + const mouse_listener = (event) => { + const parent_offset = slider.offset(); + const min = parent_offset.left; + const max = parent_offset.left + slider.width(); + const current = event instanceof MouseEvent ? event.pageX : event.touches[event.touches.length - 1].clientX; + const range = options.max_value - options.min_value; + const offset = Math.round(((current - min) * (range / options.step)) / (max - min)) * options.step; + let value = Math.min(options.max_value, Math.max(options.min_value, options.min_value + offset)); + //console.log("Min: %o | Max: %o | %o (%o)", min, max, current, offset); + update_value(value, true); + }; + slider.on('mousedown', event => { + document.addEventListener('mousemove', mouse_listener); + document.addEventListener('touchmove', mouse_listener); + document.addEventListener('mouseup', mouse_up_listener); + document.addEventListener('touchend', mouse_up_listener); + document.addEventListener('touchcancel', mouse_up_listener); + tool.show(); + slider.addClass("active"); + }); + update_value(options.initial_value, false); + return { + value(value) { + if (typeof (value) !== "undefined" && value !== _current_value) + update_value(value, true); + return _current_value; + } + }; +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["2ab9d94c857ad8d8d402323b7cdb56fdfb7a29b68fa5484661c38be7b4f6a528"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["2ab9d94c857ad8d8d402323b7cdb56fdfb7a29b68fa5484661c38be7b4f6a528"] = "2ab9d94c857ad8d8d402323b7cdb56fdfb7a29b68fa5484661c38be7b4f6a528"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "z4Yri5dV", path: "D:/TeaSpeak/web/shared/js/ui/elements/tab.ts (26,18)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +if (typeof (customElements) !== "undefined") { + try { + class X_Tab extends HTMLElement { + } + class X_Entry extends HTMLElement { + } + class X_Tag extends HTMLElement { + } + class X_Content extends HTMLElement { + } + customElements.define('x-tab', X_Tab, { extends: 'div' }); + customElements.define('x-entry', X_Entry, { extends: 'div' }); + customElements.define('x-tag', X_Tag, { extends: 'div' }); + customElements.define('x-content', X_Content, { extends: 'div' }); + } + catch (error) { + console.warn("failed to define costum elements"); + } +} +else { + console.warn(_translations.z4Yri5dV || (_translations.z4Yri5dV = tr("Could not defied tab customElements!"))); +} +var TabFunctions = { + tabify(template, copy = true) { + console.log("Tabify: copy=" + copy); + console.log(template); + let tag = $.spawn("div"); + tag.addClass("tab"); + let header = $.spawn("div"); + header.addClass("tab-header"); + let content = $.spawn("div"); + content.addClass("tab-content"); + content.append($.spawn("div").addClass("height-watcher")); + let silentContent = $.spawn("div"); + silentContent.addClass("tab-content-invisible"); + /* add some kind of min height */ + const update_height = () => { + const height_watcher = tag.find("> .tab-content .height-watcher"); + const entries = tag.find("> .tab-content-invisible x-content, > .tab-content x-content"); + console.error(entries); + let max_height = 0; + entries.each((_, _e) => { + const entry = $(_e); + const height = entry.visible_height(); + if (height > max_height) + max_height = height; + }); + height_watcher.css('min-height', max_height + "px"); + tag.find(".window-resize-listener").trigger('resize'); + }; + template.find("x-entry").each((_, _entry) => { + const entry = $(_entry); + const tag_header = $.spawn("div").addClass("entry"); + const tag_content = copy ? entry.find("x-content").clone(true, true) : entry.find("x-content"); + { + const header_tag = entry.find("x-tag"); + const header_data = copy ? header_tag.contents().clone(true, true) : header_tag.contents(); + if (header_tag.attr("x-entry-class")) + tag_header.addClass(header_tag.attr("x-entry-class")); + if (header_tag.attr("x-entry-id")) + tag_header.attr("x-id", header_tag.attr("x-entry-id")); + tag_header.append(header_data); + /* listener if the tab might got removed */ + tag_header.addClass("window-resize-listener"); + tag_header.on('resize', event => { + if (!tag_header.is(':visible') && tag_header.hasClass('selected')) { + let element = tag_header.next('.entry:visible'); + if (element.length == 0) + element = tag_header.prev('.entry:visible'); + if (element.length == 0) { + tag_header.removeClass("selected"); + tag_content.hide(); + } + else { + element.first().trigger('click'); + } + console.log("Next: %o", tag_header.next('.entry:visible')); + console.log("prev: %o", tag_header.prev('.entry:visible')); + } + }); + } + content.append(tag_content.hide()); + tag_header.on("click", () => { + if (tag_header.hasClass("selected")) + return; + tag.find(".tab-header .selected").removeClass("selected"); + tag_header.addClass("selected"); + content.find("> x-content").hide(); + /* don't show many nodes at once */ + let entries = tag_content.find(".tab-show-partitional"); + entries.hide(); + const show_next = index => { + console.log("Show " + index); + if (index >= entries.length) + return; + entries.eq(index).show(); + setTimeout(show_next.bind(undefined, index + 1), 0); + }; + show_next(0); + tag_content.trigger('show'); + tag_content.show(); + }); + console.log(this); + header.append(tag_header); + }); + setTimeout(() => header.find(".entry").first().trigger("click"), 0); + tag.append(header); + tag.append(content); + tag.append(silentContent); + tag.on('tab.resize', update_height); + return tag; + } +}; +if (!$.fn.asTabWidget) { + $.fn.asTabWidget = function (copy) { + if ($(this).prop("tagName") == "X-TAB") + return TabFunctions.tabify($(this), typeof (copy) === "boolean" ? copy : true); + else { + throw "Invalid tag! " + $(this).prop("tagName"); + } + }; +} +if (!$.fn.tabify) { + $.fn.tabify = function (copy) { + const wrapped_tag = $.spawn("div").append(this); + wrapped_tag.find("x-tab").each((_, _element) => { + const element = $(_element); + element.replaceWith(element.asTabWidget(copy)); + }); + return wrapped_tag.children(); + }; +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["faa2a3135a562096ca469b0457951b9bde59e4203f728ea2934fbf639827e1cd"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["faa2a3135a562096ca469b0457951b9bde59e4203f728ea2934fbf639827e1cd"] = "faa2a3135a562096ca469b0457951b9bde59e4203f728ea2934fbf639827e1cd"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +function tooltip(entry) { + return tooltip.initialize(entry); +} +(function (tooltip) { + let _global_tooltip; + function initialize(entry, callbacks) { + if (!callbacks) + callbacks = {}; + let _show; + let _hide; + let _shown; + let _update; + entry.find(".container-tooltip").each((index, _node) => { + const node = $(_node); + const node_content = node.find(".tooltip"); + let _force_show = false, _flag_shown = false; + const mouseenter = (event) => { + const bounds = node[0].getBoundingClientRect(); + if (!_global_tooltip) { + _global_tooltip = $("#global-tooltip"); + } + _global_tooltip[0].style.left = (bounds.left + bounds.width / 2) + "px"; + _global_tooltip[0].style.top = bounds.top + "px"; + _global_tooltip[0].classList.add("shown"); + _global_tooltip[0].innerHTML = node_content[0].innerHTML; + callbacks.on_show && callbacks.on_show(_global_tooltip); + _flag_shown = _flag_shown || !!event; /* if event is undefined then it has been triggered by hand */ + }; + const mouseexit = () => { + if (_global_tooltip) { + if (!_force_show) { + callbacks.on_hide && callbacks.on_hide(_global_tooltip); + _global_tooltip[0].classList.remove("shown"); + } + _flag_shown = false; + } + }; + _node.addEventListener("mouseenter", mouseenter); + _node.addEventListener("mouseleave", mouseexit); + _show = () => { + _force_show = true; + mouseenter(); + }; + _hide = () => { + _force_show = false; + if (!_flag_shown) + mouseexit(); + }; + _update = () => { + if (_flag_shown || _force_show) + mouseenter(); + }; + _shown = () => _flag_shown || _force_show; + }); + return { + hide: _hide || (() => { }), + show: _show || (() => { }), + is_shown: _shown || (() => false), + update: _update || (() => { }) + }; + } + tooltip.initialize = initialize; +})(tooltip || (tooltip = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c4e96182350831bf38d121b82c1e03618a32cef6c1c05d93fe5ce55633bb3e0e"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c4e96182350831bf38d121b82c1e03618a32cef6c1c05d93fe5ce55633bb3e0e"] = "c4e96182350831bf38d121b82c1e03618a32cef6c1c05d93fe5ce55633bb3e0e"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "YHk3QMgK", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (233,44)" }, { name: "IQurT9kP", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (238,71)" }, { name: "WLGU6bnP", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (242,76)" }, { name: "IRFS6dUE", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (311,45)" }, { name: "trzsdqss", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (312,37)" }, { name: "WzWvOBTm", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (327,37)" }, { name: "tgGIp6eC", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (336,37)" }, { name: "XMwtJTlt", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (349,41)" }, { name: "YDk3ehXm", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (359,45)" }, { name: "ZLJ7E5ad", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (364,45)" }, { name: "Dcy8u586", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (366,37)" }, { name: "SGY3uiR3", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (373,37)" }, { name: "dWnfDpjs", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (380,37)" }, { name: "Qn5Y_BQT", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (387,37)" }, { name: "JR56gU5A", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (394,37)" }, { name: "S9GOLSrc", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (402,37)" }, { name: "ZrrAICSr", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (405,34)" }, { name: "tMyCKezh", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (405,57)" }, { name: "kjIOXUWT", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (409,37)" }, { name: "wga4YG6i", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (413,34)" }, { name: "r620ua7H", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (413,51)" }, { name: "IKaKTFJ0", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (421,45)" }, { name: "bhDNOIR7", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (421,62)" }, { name: "v5LsXjCv", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (424,46)" }, { name: "mtxGQQO1", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (424,91)" }, { name: "iwtQgyN6", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (432,45)" }, { name: "GZEpQEf5", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (448,37)" }, { name: "CsIwCkwd", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (456,42)" }, { name: "DyqpEtZd", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (456,78)" }, { name: "RgaMSFmp", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (460,38)" }, { name: "LWN4R9Z1", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (460,70)" }, { name: "A8Et845b", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (465,37)" }, { name: "VQsO5xjg", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (473,42)" }, { name: "EeaoSfA3", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (473,78)" }, { name: "BamyNO0n", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (477,38)" }, { name: "hT3TIxtD", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (477,70)" }, { name: "dmjFiiJh", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (482,37)" }, { name: "zQ2y7y4l", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (490,42)" }, { name: "Jbgud9rC", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (490,78)" }, { name: "p5GLEnDz", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (494,38)" }, { name: "b17gFELJ", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (494,70)" }, { name: "mkHwLaEd", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (500,37)" }, { name: "S0zdQo8Q", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (506,45)" }, { name: "TL7JhBKN", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (509,41)" }, { name: "XRlZ6m6L", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (512,41)" }, { name: "NdyN4NkV", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (516,37)" }, { name: "Zw4iJWN_", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (519,37)" }, { name: "W4o454QI", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (524,41)" }, { name: "JJbVuuAX", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (527,41)" }, { name: "rjgt_m0J", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (532,52)" }, { name: "TZWg3Vt_", path: "D:/TeaSpeak/web/shared/js/ui/frames/MenuBar.ts (532,73)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var top_menu; +(function (top_menu) { + let _driver; + function driver() { + return _driver; + } + top_menu.driver = driver; + function set_driver(driver) { + _driver = driver; + } + top_menu.set_driver = set_driver; + let html; + (function (html) { + class HTMLHrItem { + constructor() { + this.html_tag = $.spawn("hr"); + } + } + class HTMLMenuItem { + constructor(label, mode) { + this._items = []; + this._label = label; + this.html_tag = $.spawn("div").addClass("container-menu-item type-" + mode); + this._label_tag = $.spawn("div").addClass("menu-item"); + this._label_icon_tag = $.spawn("div").addClass("container-icon").appendTo(this._label_tag); + $.spawn("div").addClass("container-label").append(this._label_text_tag = $.spawn("a").text(label)).appendTo(this._label_tag); + this._label_tag.on('click', event => { + if (event.isDefaultPrevented()) + return; + const disabled = this.html_tag.hasClass("disabled"); + if (this._callback_click && !disabled) { + this._callback_click(); + } + event.preventDefault(); + if (disabled) + event.stopPropagation(); + else + HTMLMenuBarDriver.instance().close(); + }); + this._submenu_tag = $.spawn("div").addClass("sub-menu"); + this.html_tag.append(this._label_tag); + this.html_tag.append(this._submenu_tag); + } + append_item(label) { + const item = new HTMLMenuItem(label, "side"); + this._items.push(item); + this._submenu_tag.append(item.html_tag); + this.html_tag.addClass('sub-entries'); + return item; + } + append_hr() { + const item = new HTMLHrItem(); + this._items.push(item); + this._submenu_tag.append(item.html_tag); + return item; + } + delete_item(item) { + this._items.remove(item); + item.html_tag.detach(); + this.html_tag.toggleClass('sub-entries', this._items.length > 0); + } + disabled(value) { + if (typeof (value) === "undefined") + return this.html_tag.hasClass("disabled"); + this.html_tag.toggleClass("disabled", value); + return value; + } + items() { + return this._items; + } + label(value) { + if (typeof (value) === "undefined" || this._label === value) + return this._label; + return this._label; + } + visible(value) { + if (typeof (value) === "undefined") + return this.html_tag.is(':visible'); //FIXME! + this.html_tag.toggle(!!value); + return value; + } + click(callback) { + this._callback_click = callback; + return this; + } + icon(klass) { + this._label_icon_tag.children().remove(); + if (typeof (klass) === "string") + $.spawn("div").addClass("icon_em " + klass).appendTo(this._label_icon_tag); + else + IconManager.generate_tag(klass).appendTo(this._label_icon_tag); + return ""; + } + } + class HTMLMenuBarDriver { + constructor() { + this._items = []; + this.html_tag = $.spawn("div").addClass("top-menu-bar"); + } + static instance() { + if (!this._instance) + this._instance = new HTMLMenuBarDriver(); + return this._instance; + } + append_item(label) { + const item = new HTMLMenuItem(label, "down"); + this._items.push(item); + this.html_tag.append(item.html_tag); + item._label_tag.on('click', enable_event => { + enable_event.preventDefault(); + this.close(); + item.html_tag.addClass("active"); + setTimeout(() => { + $(document).one('click focusout', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + item.html_tag.removeClass("active"); + }); + }, 0); + }); + return item; + } + close() { + this.html_tag.find(".active").removeClass("active"); + } + delete_item(item) { + return undefined; + } + items() { + return this._items; + } + flush_changes() { } + initialize() { + $("#top-menu-bar").replaceWith(this.html_tag); + } + } + html.HTMLMenuBarDriver = HTMLMenuBarDriver; + })(html || (html = {})); + let _items_bookmark; + function rebuild_bookmarks() { + if (!_items_bookmark) { + _items_bookmark = { + root: driver().append_item(_translations.YHk3QMgK || (_translations.YHk3QMgK = tr("Favorites"))), + add_current: undefined, + manage: undefined + }; + _items_bookmark.manage = _items_bookmark.root.append_item(_translations.IQurT9kP || (_translations.IQurT9kP = tr("Manage bookmarks"))); + _items_bookmark.manage.icon("client-bookmark_manager"); + _items_bookmark.manage.click(() => Modals.spawnBookmarkModal()); + _items_bookmark.add_current = _items_bookmark.root.append_item(_translations.WLGU6bnP || (_translations.WLGU6bnP = tr("Add current server to bookmarks"))); + _items_bookmark.add_current.icon('client-bookmark_add'); + _items_bookmark.add_current.click(() => bookmarks.add_current_server()); + _state_updater["bookmarks.ac"] = { item: _items_bookmark.add_current, conditions: [condition_connected] }; + } + _items_bookmark.root.items().filter(e => e !== _items_bookmark.add_current && e !== _items_bookmark.manage).forEach(e => { + _items_bookmark.root.delete_item(e); + }); + _items_bookmark.root.append_hr(); + const build_bookmark = (root, entry) => { + if (entry.type == bookmarks.BookmarkType.DIRECTORY) { + const directory = entry; + const item = root.append_item(directory.display_name); + item.icon('client-folder'); + for (const entry of directory.content) + build_bookmark(item, entry); + if (directory.content.length == 0) + item.disabled(true); + } + else { + const bookmark = entry; + const item = root.append_item(bookmark.display_name); + item.icon(IconManager.load_cached_icon(bookmark.last_icon_id || 0)); + item.click(() => bookmarks.boorkmak_connect(bookmark)); + } + }; + for (const entry of bookmarks.bookmarks().content) + build_bookmark(_items_bookmark.root, entry); + driver().flush_changes(); + } + top_menu.rebuild_bookmarks = rebuild_bookmarks; + /* will be called on connection handler change or on client connect state or mic state change etc... */ + let _state_updater = {}; + function update_state() { + for (const _key of Object.keys(_state_updater)) { + const item = _state_updater[_key]; + if (item.update_handler) { + if (item.update_handler(item.item)) + continue; + } + let enabled = true; + for (const condition of item.conditions) + if (!condition()) { + enabled = false; + break; + } + item.item.disabled(!enabled); + } + driver().flush_changes(); + } + top_menu.update_state = update_state; + const condition_connected = () => { + const scon = server_connections ? server_connections.active_connection_handler() : undefined; + return scon && scon.connected; + }; + function initialize() { + const driver = top_menu.driver(); + driver.initialize(); + /* build connection */ + let item; + { + const menu = driver.append_item(_translations.IRFS6dUE || (_translations.IRFS6dUE = tr("Connection"))); + item = menu.append_item(_translations.trzsdqss || (_translations.trzsdqss = tr("Connect to a server"))); + item.icon('client-connect'); + item.click(() => Modals.spawnConnectModal({})); + const do_disconnect = (handlers) => { + for (const handler of handlers) { + handler.cancel_reconnect(true); + handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message? + server_connections.active_connection_handler().serverConnection.disconnect(); + handler.sound.play(Sound.CONNECTION_DISCONNECTED); + handler.log.log(log.server.Type.DISCONNECTED, {}); + } + control_bar.update_connection_state(); + update_state(); + }; + item = menu.append_item(_translations.WzWvOBTm || (_translations.WzWvOBTm = tr("Disconnect from current server"))); + item.icon('client-disconnect'); + item.disabled(true); + item.click(() => { + const handler = server_connections.active_connection_handler(); + do_disconnect([handler]); + }); + _state_updater["connection.dc"] = { item: item, conditions: [() => condition_connected()] }; + item = menu.append_item(_translations.tgGIp6eC || (_translations.tgGIp6eC = tr("Disconnect from all servers"))); + item.icon('client-disconnect'); + item.click(() => { + do_disconnect(server_connections.server_connection_handlers()); + }); + _state_updater["connection.dca"] = { item: item, conditions: [], update_handler: (item) => { + item.visible(server_connections && server_connections.server_connection_handlers().length > 1); + return true; + } }; + if (!app.is_web()) { + menu.append_hr(); + item = menu.append_item(_translations.XMwtJTlt || (_translations.XMwtJTlt = tr("Quit"))); + item.icon('client-close_button'); + item.click(() => top_menu.native_actions.quit()); + } + } + { + rebuild_bookmarks(); + } + if (false) { + const menu = driver.append_item(_translations.YDk3ehXm || (_translations.YDk3ehXm = tr("Self"))); + /* Microphone | Sound | Away */ + } + { + const menu = driver.append_item(_translations.ZLJ7E5ad || (_translations.ZLJ7E5ad = tr("Permissions"))); + item = menu.append_item(_translations.Dcy8u586 || (_translations.Dcy8u586 = tr("Server Groups"))); + item.icon("client-permission_server_groups"); + item.click(() => { + Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open(); + }); + _state_updater["permission.sg"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.SGY3uiR3 || (_translations.SGY3uiR3 = tr("Client Permissions"))); + item.icon("client-permission_client"); + item.click(() => { + Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open(); + }); + _state_updater["permission.clp"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.dWnfDpjs || (_translations.dWnfDpjs = tr("Channel Client Permissions"))); + item.icon("client-permission_client"); + item.click(() => { + Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open(); + }); + _state_updater["permission.chclp"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.Qn5Y_BQT || (_translations.Qn5Y_BQT = tr("Channel Groups"))); + item.icon("client-permission_channel"); + item.click(() => { + Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open(); + }); + _state_updater["permission.cg"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.JR56gU5A || (_translations.JR56gU5A = tr("Channel Permissions"))); + item.icon("client-permission_channel"); + item.click(() => { + Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open(); + }); + _state_updater["permission.cp"] = { item: item, conditions: [condition_connected] }; + menu.append_hr(); + item = menu.append_item(_translations.S9GOLSrc || (_translations.S9GOLSrc = tr("List Privilege Keys"))); + item.icon("client-token"); + item.click(() => { + createErrorModal(_translations.ZrrAICSr || (_translations.ZrrAICSr = tr("Not implemented")), _translations.tMyCKezh || (_translations.tMyCKezh = tr("Privilege key list is not implemented yet!"))).open(); + }); + _state_updater["permission.pk"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.kjIOXUWT || (_translations.kjIOXUWT = tr("Use Privilege Key"))); + item.icon("client-token_use"); + item.click(() => { + //TODO: Fixeme use one method for the control bar and here! + createInputModal(_translations.wga4YG6i || (_translations.wga4YG6i = tr("Use token")), _translations.r620ua7H || (_translations.r620ua7H = tr("Please enter your token/privilege key")), message => message.length > 0, result => { + if (!result) + return; + const scon = server_connections.active_connection_handler(); + if (scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(_translations.IKaKTFJ0 || (_translations.IKaKTFJ0 = tr("Use token")), _translations.bhDNOIR7 || (_translations.bhDNOIR7 = tr("Toke successfully used!"))).open(); + }).catch(error => { + //TODO tr + createErrorModal(_translations.v5LsXjCv || (_translations.v5LsXjCv = tr("Use token")), MessageHelper.formatMessage(_translations.mtxGQQO1 || (_translations.mtxGQQO1 = tr("Failed to use token: {}")), error instanceof CommandResult ? error.message : error)).open(); + }); + }).open(); + }); + _state_updater["permission.upk"] = { item: item, conditions: [condition_connected] }; + } + { + const menu = driver.append_item(_translations.iwtQgyN6 || (_translations.iwtQgyN6 = tr("Tools"))); + /* + item = menu.append_item(tr("Manage Playlists")); + item.icon('client-music'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if(scon && scon.connected) { + Modals.spawnPlaylistManage(scon); + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); + } + }); + _state_updater["tools.pl"] = { item: item, conditions: [condition_connected]}; + */ + item = menu.append_item(_translations.GZEpQEf5 || (_translations.GZEpQEf5 = tr("Ban List"))); + item.icon('client-ban_list'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if (scon && scon.connected) { + if (scon.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { + Modals.openBanList(scon); + } + else { + createErrorModal(_translations.CsIwCkwd || (_translations.CsIwCkwd = tr("You dont have the permission")), _translations.DyqpEtZd || (_translations.DyqpEtZd = tr("You dont have the permission to view the ban list"))).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + else { + createErrorModal(_translations.RgaMSFmp || (_translations.RgaMSFmp = tr("You have to be connected")), _translations.LWN4R9Z1 || (_translations.LWN4R9Z1 = tr("You have to be connected to use this function!"))).open(); + } + }); + _state_updater["tools.bl"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.A8Et845b || (_translations.A8Et845b = tr("Query List"))); + item.icon('client-server_query'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if (scon && scon.connected) { + if (scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST_OWN).granted(1)) { + Modals.spawnQueryManage(scon); + } + else { + createErrorModal(_translations.VQsO5xjg || (_translations.VQsO5xjg = tr("You dont have the permission")), _translations.EeaoSfA3 || (_translations.EeaoSfA3 = tr("You dont have the permission to view the server query list"))).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + else { + createErrorModal(_translations.BamyNO0n || (_translations.BamyNO0n = tr("You have to be connected")), _translations.hT3TIxtD || (_translations.hT3TIxtD = tr("You have to be connected to use this function!"))).open(); + } + }); + _state_updater["tools.ql"] = { item: item, conditions: [condition_connected] }; + item = menu.append_item(_translations.dmjFiiJh || (_translations.dmjFiiJh = tr("Query Create"))); + item.icon('client-server_query'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if (scon && scon.connected) { + if (scon.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1)) { + Modals.spawnQueryCreate(scon); + } + else { + createErrorModal(_translations.zQ2y7y4l || (_translations.zQ2y7y4l = tr("You dont have the permission")), _translations.Jbgud9rC || (_translations.Jbgud9rC = tr("You dont have the permission to create a server query login"))).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + else { + createErrorModal(_translations.p5GLEnDz || (_translations.p5GLEnDz = tr("You have to be connected")), _translations.b17gFELJ || (_translations.b17gFELJ = tr("You have to be connected to use this function!"))).open(); + } + }); + _state_updater["tools.qc"] = { item: item, conditions: [condition_connected] }; + menu.append_hr(); + item = menu.append_item(_translations.mkHwLaEd || (_translations.mkHwLaEd = tr("Settings"))); + item.icon("client-settings"); + item.click(() => Modals.spawnSettingsModal()); + } + { + const menu = driver.append_item(_translations.S0zdQo8Q || (_translations.S0zdQo8Q = tr("Help"))); + if (!app.is_web()) { + item = menu.append_item(_translations.TL7JhBKN || (_translations.TL7JhBKN = tr("Check for updates"))); + item.click(() => top_menu.native_actions.check_native_update()); + item = menu.append_item(_translations.XRlZ6m6L || (_translations.XRlZ6m6L = tr("Open changelog"))); + item.click(() => top_menu.native_actions.open_change_log()); + } + item = menu.append_item(_translations.NdyN4NkV || (_translations.NdyN4NkV = tr("Visit TeaSpeak.de"))); + item.click(() => window.open('https://teaspeak.de/', '_blank')); + item = menu.append_item(_translations.Zw4iJWN_ || (_translations.Zw4iJWN_ = tr("Visit TeaSpeak forum"))); + item.click(() => window.open('https://forum.teaspeak.de/', '_blank')); + if (!app.is_web() && typeof (top_menu.native_actions.show_dev_tools) === "function" && top_menu.native_actions.show_dev_tools()) { + menu.append_hr(); + item = menu.append_item(_translations.W4o454QI || (_translations.W4o454QI = tr("Open developer tools"))); + item.click(() => top_menu.native_actions.open_dev_tools()); + item = menu.append_item(_translations.JJbVuuAX || (_translations.JJbVuuAX = tr("Reload UI"))); + item.click(() => top_menu.native_actions.reload_page()); + } + menu.append_hr(); + item = menu.append_item(app.is_web() ? _translations.rjgt_m0J || (_translations.rjgt_m0J = tr("About TeaWeb")) : _translations.TZWg3Vt_ || (_translations.TZWg3Vt_ = tr("About TeaClient"))); + item.click(() => Modals.spawnAbout()); + } + update_state(); + } + top_menu.initialize = initialize; + /* default is HTML, the client will override this */ + set_driver(html.HTMLMenuBarDriver.instance()); +})(top_menu || (top_menu = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["cf77d6d8a35237928a5ec1202e1c1ffbf44694d05a62e3e6fd0979af4dadf05b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["cf77d6d8a35237928a5ec1202e1c1ffbf44694d05a62e3e6fd0979af4dadf05b"] = "cf77d6d8a35237928a5ec1202e1c1ffbf44694d05a62e3e6fd0979af4dadf05b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "cSuOVkHn", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (148,33)" }, { name: "yNvFymJQ", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (160,61)" }, { name: "pqXaIVXD", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (166,37)" }, { name: "c1C0TPhH", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (184,33)" }, { name: "_9h4MR1v", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (198,91)" }, { name: "GdZEXcYF", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (202,72)" }, { name: "JEyIfN9E", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (218,36)" }, { name: "IETaAnAP", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (220,36)" }, { name: "sAdL5qG_", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (222,65)" }, { name: "DssXnAbR", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (248,59)" }, { name: "WBXUzAj5", path: "D:/TeaSpeak/web/shared/js/ui/frames/chat_frame.ts (250,59)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/* the bar on the right with the chats (Channel & Client) */ +var chat; +(function (chat) { + let InfoFrameMode; + (function (InfoFrameMode) { + InfoFrameMode["NONE"] = "none"; + InfoFrameMode["CHANNEL_CHAT"] = "channel_chat"; + InfoFrameMode["PRIVATE_CHAT"] = "private_chat"; + InfoFrameMode["CLIENT_INFO"] = "client_info"; + InfoFrameMode["MUSIC_BOT"] = "music_bot"; + })(InfoFrameMode = chat.InfoFrameMode || (chat.InfoFrameMode = {})); + class InfoFrame { + constructor(handle) { + this.handle = handle; + this._build_html_tag(); + this.update_channel_talk(); + this.update_channel_text(); + this.set_mode(InfoFrameMode.CHANNEL_CHAT); + this._ping_updater = setInterval(() => this.update_ping(), 2000); + this.update_ping(); + } + html_tag() { return this._html_tag; } + destroy() { + clearInterval(this._ping_updater); + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._value_ping = undefined; + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_info").renderTag(); + this._html_tag.find(".button-switch-chat-channel").on('click', () => this.handle.show_channel_conversations()); + this._value_ping = this._html_tag.find(".value-ping"); + this._html_tag.find(".chat-counter").on('click', event => this.handle.show_private_conversations()); + this._button_conversation = this._html_tag.find(".button.open-conversation").on('click', event => { + const selected_client = this.handle.client_info().current_client(); + if (!selected_client) + return; + const conversation = selected_client ? this.handle.private_conversations().find_conversation({ + name: selected_client.properties.client_nickname, + unique_id: selected_client.properties.client_unique_identifier, + client_id: selected_client.clientId() + }, { create: true, attach: true }) : undefined; + if (!conversation) + return; + this.handle.private_conversations().set_selected_conversation(conversation); + this.handle.show_private_conversations(); + })[0]; + this._button_bot_manage = this._html_tag.find(".bot-manage").on('click', event => { + const bot = this.handle.music_info().current_bot(); + if (!bot) + return; + Modals.openMusicManage(this.handle.handle, bot); + }); + this._button_song_add = this._html_tag.find(".bot-add-song").on('click', event => { + this.handle.music_info().events.fire("action_song_add"); + }); + } + update_ping() { + this._value_ping.removeClass("very-good good medium poor very-poor"); + const connection = this.handle.handle.serverConnection; + if (!this.handle.handle.connected || !connection) { + this._value_ping.text("Not connected"); + return; + } + const ping = connection.ping(); + if (!ping || typeof (ping.native) !== "number") { + this._value_ping.text("Not available"); + return; + } + let value; + if (typeof (ping.javascript) !== "undefined") { + value = ping.javascript; + this._value_ping.text(ping.javascript.toFixed(0) + "ms").attr('title', 'Native: ' + ping.native.toFixed(3) + "ms \nJavascript: " + ping.javascript.toFixed(3) + "ms"); + } + else { + value = ping.native; + this._value_ping.text(ping.native.toFixed(0) + "ms").attr('title', "Ping: " + ping.native.toFixed(3) + "ms"); + } + if (value <= 10) + this._value_ping.addClass("very-good"); + else if (value <= 30) + this._value_ping.addClass("good"); + else if (value <= 60) + this._value_ping.addClass("medium"); + else if (value <= 150) + this._value_ping.addClass("poor"); + else + this._value_ping.addClass("very-poor"); + } + update_channel_talk() { + const client = this.handle.handle.getClient(); + const channel = client ? client.currentChannel() : undefined; + this._channel_voice = channel; + const html_tag = this._html_tag.find(".value-voice-channel"); + const html_limit_tag = this._html_tag.find(".value-voice-limit"); + html_limit_tag.text(""); + html_tag.children().remove(); + if (channel) { + if (channel.properties.channel_icon_id != 0) + client.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); + $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); + this.update_channel_limit(channel, html_limit_tag); + } + else { + $.spawn("div").text("Not connected").appendTo(html_tag); + } + } + update_channel_text() { + const channel_tree = this.handle.handle.connected ? this.handle.handle.channelTree : undefined; + const current_channel_id = channel_tree ? this.handle.channel_conversations().current_channel() : 0; + const channel = channel_tree ? channel_tree.findChannel(current_channel_id) : undefined; + this._channel_text = channel; + const tag_container = this._html_tag.find(".mode-channel_chat.channel"); + const html_tag_title = tag_container.find(".title"); + const html_tag = tag_container.find(".value-text-channel"); + const html_limit_tag = tag_container.find(".value-text-limit"); + /* reset */ + html_tag_title.text(_translations.cSuOVkHn || (_translations.cSuOVkHn = tr("You're chatting in Channel"))); + html_limit_tag.text(""); + html_tag.children().detach(); + /* initialize */ + if (channel) { + if (channel.properties.channel_icon_id != 0) + this.handle.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); + $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); + this.update_channel_limit(channel, html_limit_tag); + } + else if (channel_tree && current_channel_id > 0) { + html_tag.append(MessageHelper.formatMessage(_translations.yNvFymJQ || (_translations.yNvFymJQ = tr("Unknown channel id {}")), current_channel_id)); + } + else if (channel_tree && current_channel_id == 0) { + const server = this.handle.handle.channelTree.server; + if (server.properties.virtualserver_icon_id != 0) + this.handle.handle.fileManager.icons.generateTag(server.properties.virtualserver_icon_id).appendTo(html_tag); + $.spawn("div").text(server.properties.virtualserver_name).appendTo(html_tag); + html_tag_title.text(_translations.pqXaIVXD || (_translations.pqXaIVXD = tr("You're chatting in Server"))); + this.update_server_limit(server, html_limit_tag); + } + else if (this.handle.handle.connected) { + $.spawn("div").text("No channel selected").appendTo(html_tag); + } + else { + $.spawn("div").text("Not connected").appendTo(html_tag); + } + } + update_channel_client_count(channel) { + if (channel === this._channel_text) + this.update_channel_limit(channel, this._html_tag.find(".value-text-limit")); + if (channel === this._channel_voice) + this.update_channel_limit(channel, this._html_tag.find(".value-voice-limit")); + } + update_channel_limit(channel, tag) { + let channel_limit = _translations.c1C0TPhH || (_translations.c1C0TPhH = tr("Unlimited")); + if (!channel.properties.channel_flag_maxclients_unlimited) + channel_limit = "" + channel.properties.channel_maxclients; + else if (!channel.properties.channel_flag_maxfamilyclients_unlimited) { + if (channel.properties.channel_maxfamilyclients >= 0) + channel_limit = "" + channel.properties.channel_maxfamilyclients; + } + tag.text(channel.clients(false).length + " / " + channel_limit); + } + update_server_limit(server, tag) { + const fn = () => { + let text = server.properties.virtualserver_clientsonline + " / " + server.properties.virtualserver_maxclients; + if (server.properties.virtualserver_reserved_slots) + text += " (" + server.properties.virtualserver_reserved_slots + " " + (_translations._9h4MR1v || (_translations._9h4MR1v = tr("Reserved"))) + ")"; + tag.text(text); + }; + server.updateProperties().then(fn).catch(error => tag.text(_translations.GdZEXcYF || (_translations.GdZEXcYF = tr("Failed to update info")))); + fn(); + } + update_chat_counter() { + const conversations = this.handle.private_conversations().conversations(); + { + const count = conversations.filter(e => e.is_unread()).length; + const count_container = this._html_tag.find(".container-indicator"); + const count_tag = count_container.find(".chat-unread-counter"); + count_container.toggle(count > 0); + count_tag.text(count); + } + { + const count_tag = this._html_tag.find(".chat-counter"); + if (conversations.length == 0) + count_tag.text(_translations.JEyIfN9E || (_translations.JEyIfN9E = tr("No conversations"))); + else if (conversations.length == 1) + count_tag.text(_translations.IETaAnAP || (_translations.IETaAnAP = tr("One conversation"))); + else + count_tag.text(conversations.length + " " + (_translations.sAdL5qG_ || (_translations.sAdL5qG_ = tr("conversations")))); + } + } + current_mode() { + return this._mode; + } + set_mode(mode) { + for (const mode in InfoFrameMode) + this._html_tag.removeClass("mode-" + InfoFrameMode[mode]); + this._html_tag.addClass("mode-" + mode); + if (mode === InfoFrameMode.CLIENT_INFO && this._button_conversation) { + //Will be called every time a client is shown + const selected_client = this.handle.client_info().current_client(); + const conversation = selected_client ? this.handle.private_conversations().find_conversation({ + name: selected_client.properties.client_nickname, + unique_id: selected_client.properties.client_unique_identifier, + client_id: selected_client.clientId() + }, { create: false, attach: false }) : undefined; + const visibility = (selected_client && selected_client.clientId() !== this.handle.handle.clientId) ? "visible" : "hidden"; + if (this._button_conversation.style.visibility !== visibility) + this._button_conversation.style.visibility = visibility; + if (conversation) { + this._button_conversation.innerText = _translations.DssXnAbR || (_translations.DssXnAbR = tr("Open conversation")); + } + else { + this._button_conversation.innerText = _translations.WBXUzAj5 || (_translations.WBXUzAj5 = tr("Start a conversation")); + } + } + else if (mode === InfoFrameMode.MUSIC_BOT) { + //TODO? + } + } + } + chat.InfoFrame = InfoFrame; + let FrameContent; + (function (FrameContent) { + FrameContent[FrameContent["NONE"] = 0] = "NONE"; + FrameContent[FrameContent["PRIVATE_CHAT"] = 1] = "PRIVATE_CHAT"; + FrameContent[FrameContent["CHANNEL_CHAT"] = 2] = "CHANNEL_CHAT"; + FrameContent[FrameContent["CLIENT_INFO"] = 3] = "CLIENT_INFO"; + FrameContent[FrameContent["MUSIC_BOT"] = 4] = "MUSIC_BOT"; + })(FrameContent = chat.FrameContent || (chat.FrameContent = {})); + class Frame { + constructor(handle) { + this.handle = handle; + this._content_type = FrameContent.NONE; + this._info_frame = new InfoFrame(this); + this._conversations = new chat.PrivateConverations(this); + this._channel_conversations = new chat.channel.ConversationManager(this); + this._client_info = new chat.ClientInfo(this); + this._music_info = new chat.MusicInfo(this); + this._build_html_tag(); + this.show_channel_conversations(); + this.info_frame().update_chat_counter(); + } + html_tag() { return this._html_tag; } + info_frame() { return this._info_frame; } + content_type() { return this._content_type; } + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._info_frame && this._info_frame.destroy(); + this._info_frame = undefined; + this._conversations && this._conversations.destroy(); + this._conversations = undefined; + this._client_info && this._client_info.destroy(); + this._client_info = undefined; + this._music_info && this._music_info.destroy(); + this._music_info = undefined; + this._channel_conversations && this._channel_conversations.destroy(); + this._channel_conversations = undefined; + this._container_info && this._container_info.remove(); + this._container_info = undefined; + this._container_chat && this._container_chat.remove(); + this._container_chat = undefined; + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat").renderTag(); + this._container_info = this._html_tag.find(".container-info"); + this._container_chat = this._html_tag.find(".container-chat"); + this._info_frame.html_tag().appendTo(this._container_info); + } + private_conversations() { + return this._conversations; + } + channel_conversations() { + return this._channel_conversations; + } + client_info() { + return this._client_info; + } + music_info() { + return this._music_info; + } + _clear() { + this._content_type = FrameContent.NONE; + this._container_chat.children().detach(); + } + show_private_conversations() { + if (this._content_type === FrameContent.PRIVATE_CHAT) + return; + this._clear(); + this._content_type = FrameContent.PRIVATE_CHAT; + this._container_chat.append(this._conversations.html_tag()); + this._conversations.on_show(); + this._info_frame.set_mode(InfoFrameMode.PRIVATE_CHAT); + } + show_channel_conversations() { + if (this._content_type === FrameContent.CHANNEL_CHAT) + return; + this._clear(); + this._content_type = FrameContent.CHANNEL_CHAT; + this._container_chat.append(this._channel_conversations.html_tag()); + this._channel_conversations.on_show(); + this._info_frame.set_mode(InfoFrameMode.CHANNEL_CHAT); + } + show_client_info(client) { + this._client_info.set_current_client(client); + this._info_frame.set_mode(InfoFrameMode.CLIENT_INFO); /* specially needs an update here to update the conversation button */ + if (this._content_type === FrameContent.CLIENT_INFO) + return; + this._client_info.previous_frame_content = this._content_type; + this._clear(); + this._content_type = FrameContent.CLIENT_INFO; + this._container_chat.append(this._client_info.html_tag()); + } + show_music_player(client) { + this._music_info.set_current_bot(client); + if (this._content_type === FrameContent.MUSIC_BOT) + return; + this._info_frame.set_mode(InfoFrameMode.MUSIC_BOT); + this._music_info.previous_frame_content = this._content_type; + this._clear(); + this._content_type = FrameContent.MUSIC_BOT; + this._container_chat.append(this._music_info.html_tag()); + } + set_content(type) { + if (this._content_type === type) + return; + if (type === FrameContent.CHANNEL_CHAT) + this.show_channel_conversations(); + else if (type === FrameContent.PRIVATE_CHAT) + this.show_private_conversations(); + else { + this._clear(); + this._content_type = FrameContent.NONE; + this._info_frame.set_mode(InfoFrameMode.NONE); + } + } + } + chat.Frame = Frame; +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["f9d2d9cd4442d865648ce19099773f225ebe72215759a124bcb54cada2fd51ef"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["f9d2d9cd4442d865648ce19099773f225ebe72215759a124bcb54cada2fd51ef"] = "f9d2d9cd4442d865648ce19099773f225ebe72215759a124bcb54cada2fd51ef"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +let server_connections; +class ServerConnectionManager { + constructor(tag) { + this.connection_handlers = []; + this._tag = tag; + if (settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION, false)) + this._tag.hide(); + this._tag_connection_entries = this._tag.find(".connection-handlers"); + this._tag_buttons_scoll = this._tag.find(".container-scroll"); + this._tag_button_scoll_left = this._tag_buttons_scoll.find(".button-scroll-left"); + this._tag_button_scoll_right = this._tag_buttons_scoll.find(".button-scroll-right"); + this._tag_button_scoll_left.on('click', this._button_scroll_left_clicked.bind(this)); + this._tag_button_scoll_right.on('click', this._button_scroll_right_clicked.bind(this)); + this._container_log_server = $("#server-log"); + this._container_channel_tree = $("#channelTree"); + this._container_hostbanner = $("#hostbanner"); + this._container_chat = $("#chat"); + this.set_active_connection_handler(undefined); + } + spawn_server_connection_handler() { + const handler = new ConnectionHandler(); + this.connection_handlers.push(handler); + control_bar.update_button_away(); + control_bar.initialize_connection_handler_state(handler); + handler.tag_connection_handler.appendTo(this._tag_connection_entries); + this._tag.toggleClass("shown", this.connection_handlers.length > 1); + this._update_scroll(); + return handler; + } + destroy_server_connection_handler(handler) { + this.connection_handlers.remove(handler); + handler.tag_connection_handler.remove(); + this._update_scroll(); + this._tag.toggleClass("shown", this.connection_handlers.length > 1); + if (handler.serverConnection) { + const connected = handler.connected; + handler.serverConnection.disconnect("handler destroyed"); + handler.handleDisconnect(DisconnectReason.HANDLER_DESTROYED, connected); + } + if (handler === this.active_handler) + this.set_active_connection_handler(this.connection_handlers[0]); + /* destroy all elements */ + handler.destroy(); + } + set_active_connection_handler(handler) { + if (handler && this.connection_handlers.indexOf(handler) == -1) + throw "Handler hasn't been registered or is already obsolete!"; + this._tag_connection_entries.find(".active").removeClass("active"); + this._container_channel_tree.children().detach(); + this._container_chat.children().detach(); + this._container_log_server.children().detach(); + this._container_hostbanner.children().detach(); + if (handler) { + handler.tag_connection_handler.addClass("active"); + this._container_hostbanner.append(handler.hostbanner.html_tag); + this._container_channel_tree.append(handler.channelTree.tag_tree()); + this._container_chat.append(handler.side_bar.html_tag()); + this._container_log_server.append(handler.log.html_tag()); + if (handler.invoke_resized_on_activate) + handler.resize_elements(); + } + this.active_handler = handler; + control_bar.set_connection_handler(handler); + top_menu.update_state(); + } + active_connection_handler() { + return this.active_handler; + } + server_connection_handlers() { + return this.connection_handlers; + } + update_ui() { + this._update_scroll(); + } + _update_scroll() { + const has_scroll = this._tag_connection_entries.hasScrollBar("width") + && this._tag_connection_entries.width() + 10 >= this._tag_connection_entries.parent().width(); + this._tag_buttons_scoll.toggleClass("enabled", has_scroll); + this._tag.toggleClass("scrollbar", has_scroll); + if (has_scroll) + this._update_scroll_buttons(); + } + _button_scroll_right_clicked() { + this._tag_connection_entries.scrollLeft((this._tag_connection_entries.scrollLeft() || 0) + 50); + this._update_scroll_buttons(); + } + _button_scroll_left_clicked() { + this._tag_connection_entries.scrollLeft((this._tag_connection_entries.scrollLeft() || 0) - 50); + this._update_scroll_buttons(); + } + _update_scroll_buttons() { + const scroll = this._tag_connection_entries.scrollLeft() || 0; + this._tag_button_scoll_left.toggleClass("disabled", scroll <= 0); + this._tag_button_scoll_right.toggleClass("disabled", scroll + this._tag_connection_entries.width() + 2 >= this._tag_connection_entries[0].scrollWidth); + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["aad43bc9987777ba1c29e2b3fe4eac7892a4efc3d2fe8f83a2308f49a3da326c"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["aad43bc9987777ba1c29e2b3fe4eac7892a4efc3d2fe8f83a2308f49a3da326c"] = "aad43bc9987777ba1c29e2b3fe4eac7892a4efc3d2fe8f83a2308f49a3da326c"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "eoM0uyT9", path: "D:/TeaSpeak/web/shared/js/ui/frames/hostbanner.ts (44,43)" }, { name: "WvGf45xH", path: "D:/TeaSpeak/web/shared/js/ui/frames/hostbanner.ts (64,42)" }, { name: "qS3_dAAx", path: "D:/TeaSpeak/web/shared/js/ui/frames/hostbanner.ts (84,30)" }, { name: "mLgbtjnu", path: "D:/TeaSpeak/web/shared/js/ui/frames/hostbanner.ts (97,43)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +class Hostbanner { + constructor(client) { + this._destryed = false; + this.client = client; + this.html_tag = $.spawn("div").addClass("container-hostbanner"); + this.html_tag.on('click', event => { + const server = this.client.channelTree.server; + if (!server || !server.properties.virtualserver_hostbanner_url) + return; + window.open(server.properties.virtualserver_hostbanner_url, '_blank'); + }); + this.update(); + } + destroy() { + if (this.updater) { + clearTimeout(this.updater); + this.updater = undefined; + } + if (this.html_tag) { + this.html_tag.remove(); + } + this._destryed = true; + } + update() { + if (this._destryed) + return; + if (this.updater) { + clearTimeout(this.updater); + this.updater = undefined; + } + this.html_tag.toggleClass("no-background", !settings.static_global(Settings.KEY_HOSTBANNER_BACKGROUND)); + const tag = this.generate_tag(); + tag.then(element => { + log.debug(LogCategory.CLIENT, _translations.eoM0uyT9 || (_translations.eoM0uyT9 = tr("Regenerated hostbanner tag. Replacing it: %o")), element); + if (!element) { + this.html_tag.empty().addClass("disabled"); + return; + } + const children = this.html_tag.children(); + this.html_tag.append(element).removeClass("disabled"); + /* allow the new image be loaded from cache URL */ + { + children + .css('z-index', '2') + .css('position', 'absolute') + .css('height', '100%') + .css('width', '100%'); + setTimeout(() => { + children.detach(); + }, 250); + } + }).catch(error => { + log.warn(LogCategory.CLIENT, _translations.WvGf45xH || (_translations.WvGf45xH = tr("Failed to load the hostbanner: %o")), error); + this.html_tag.empty().addClass("disabled"); + }); + const server = this.client.channelTree.server; + this.html_tag.attr('title', server ? server.properties.virtualserver_hostbanner_url : undefined); + } + static generate_tag(banner_url, gfx_interval, mode) { + return __awaiter(this, void 0, void 0, function* () { + if (!banner_url) + return undefined; + if (gfx_interval > 0) { + const update_interval = Math.max(gfx_interval, 60); + const update_timestamp = (Math.floor((Date.now() / 1000) / update_interval) * update_interval).toString(); + try { + const url = new URL(banner_url); + if (url.search.length == 0) + banner_url += "?_ts=" + update_timestamp; + else + banner_url += "&_ts=" + update_timestamp; + } + catch (error) { + console.warn(_translations.qS3_dAAx || (_translations.qS3_dAAx = tr("Failed to parse banner URL: %o. Using default '&' append.")), error); + banner_url += "&_ts=" + update_timestamp; + } + } + /* first now load the image */ + const image_element = document.createElement("img"); + yield new Promise((resolve, reject) => { + image_element.onload = resolve; + image_element.onerror = reject; + image_element.src = banner_url; + image_element.style.display = 'none'; + document.body.append(image_element); + log.debug(LogCategory.CLIENT, _translations.mLgbtjnu || (_translations.mLgbtjnu = tr("Successfully loaded hostbanner image."))); + }); + image_element.parentNode.removeChild(image_element); + image_element.style.display = 'unset'; + return $.spawn("div").addClass("hostbanner-image-container hostbanner-mode-" + mode).append($(image_element)); + }); + } + generate_tag() { + return __awaiter(this, void 0, void 0, function* () { + if (!this.client.connected) + return undefined; + const server = this.client.channelTree.server; + if (!server) + return undefined; + if (!server.properties.virtualserver_hostbanner_gfx_url) + return undefined; + const timeout = server.properties.virtualserver_hostbanner_gfx_interval; + const tag = Hostbanner.generate_tag(server.properties.virtualserver_hostbanner_gfx_url, server.properties.virtualserver_hostbanner_gfx_interval, server.properties.virtualserver_hostbanner_mode); + if (timeout > 0) + this.updater = setTimeout(() => this.update(), timeout * 1000); + return tag; + }); + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["956e29b868c2e9a6289cd1b992fe709ac449075b7c754e43a53fef956517a791"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["956e29b868c2e9a6289cd1b992fe709ac449075b7c754e43a53fef956517a791"] = "956e29b868c2e9a6289cd1b992fe709ac449075b7c754e43a53fef956517a791"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var image_preview; +(function (image_preview) { + let preview_overlay; + let container_image; + let button_open_in_browser; + function preview_image(url, original_url) { + if (!preview_overlay) + return; + container_image.empty(); + $.spawn("img").attr({ + "src": url, + "title": original_url, + "x-original-src": original_url + }).appendTo(container_image); + preview_overlay.removeClass("hidden"); + button_open_in_browser.show(); + } + image_preview.preview_image = preview_image; + function preview_image_tag(tag) { + if (!preview_overlay) + return; + container_image.empty(); + container_image.append(tag); + preview_overlay.removeClass("hidden"); + button_open_in_browser.hide(); + } + image_preview.preview_image_tag = preview_image_tag; + function current_url() { + const image_tag = container_image.find("img"); + return image_tag.attr("x-original-src") || image_tag.attr("src") || ""; + } + image_preview.current_url = current_url; + function close_preview() { + preview_overlay.addClass("hidden"); + } + image_preview.close_preview = close_preview; + loader.register_task(loader.Stage.LOADED, { + priority: 0, + name: "image preview init", + function: () => __awaiter(this, void 0, void 0, function* () { + preview_overlay = $("#overlay-image-preview"); + container_image = preview_overlay.find(".container-image"); + preview_overlay.find("img").on('click', event => event.preventDefault()); + preview_overlay.on('click', event => { + if (event.isDefaultPrevented()) + return; + close_preview(); + }); + preview_overlay.find(".button-close").on('click', event => { + event.preventDefault(); + close_preview(); + }); + preview_overlay.find(".button-download").on('click', event => { + event.preventDefault(); + const link = document.createElement('a'); + link.href = current_url(); + link.target = "_blank"; + const findex = link.href.lastIndexOf("/") + 1; + link.download = link.href.substr(findex); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }); + button_open_in_browser = preview_overlay.find(".button-open-in-window"); + button_open_in_browser.on('click', event => { + event.preventDefault(); + const win = window.open(current_url(), '_blank'); + win.focus(); + }); + }) + }); +})(image_preview || (image_preview = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5fc74f145b59b8238703696afb5cc22ca2d480fef1ab02d2b3bcec80e04b5038"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5fc74f145b59b8238703696afb5cc22ca2d480fef1ab02d2b3bcec80e04b5038"] = "5fc74f145b59b8238703696afb5cc22ca2d480fef1ab02d2b3bcec80e04b5038"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "OtbdfpKf", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (338,49)" }, { name: "TjslEtuu", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (388,52)" }, { name: "tgKTt68B", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (391,142)" }, { name: "EwoTjp_H", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (392,144)" }, { name: "VLiF_jTd", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (393,153)" }, { name: "wQTTZBOL", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (395,121)" }, { name: "kYlrLsJh", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (396,123)" }, { name: "O27TI6gw", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (397,129)" }, { name: "DMjxS8Gy", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (400,52)" }, { name: "ekA1Jk45", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (400,155)" }, { name: "ZjRwAH8e", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (400,167)" }, { name: "BiOcwyQS", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (404,52)" }, { name: "Ds9JwNz3", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (413,79)" }, { name: "aEaFAUSl", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (413,121)" }, { name: "s3gggN2V", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (415,79)" }, { name: "LQfEHDHq", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (415,121)" }, { name: "eJVz0xy_", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (419,79)" }, { name: "rHoYTw2I", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (419,143)" }, { name: "a6eWAgVb", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (426,79)" }, { name: "za6usZnN", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (426,134)" }, { name: "GyEUhfQH", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (434,79)" }, { name: "pqtlwDzo", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (434,147)" }, { name: "ssaVXJ6s", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (442,79)" }, { name: "HloiwTjB", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (442,138)" }, { name: "T94YFRKJ", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (455,74)" }, { name: "aRnZWAxP", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (455,127)" }, { name: "mC_Bj6QF", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (462,74)" }, { name: "M3RlPff9", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (462,119)" }, { name: "OC81J6RI", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (468,74)" }, { name: "OzN4pqWV", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (468,145)" }, { name: "nI0lFUtf", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (481,75)" }, { name: "PAxjJKgM", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (481,128)" }, { name: "RX3ynvys", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (483,56)" }, { name: "KkAPpmPk", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (485,56)" }, { name: "KLX2eoxD", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (487,75)" }, { name: "mBmCZEyz", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (487,127)" }, { name: "b_y3zLOL", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (497,56)" }, { name: "Mcvtq5Rd", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (504,56)" }, { name: "owzm_mo1", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (506,75)" }, { name: "iNLIAxK4", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (506,142)" }, { name: "hQ_fh6ph", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (522,56)" }, { name: "LrNB3SF3", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (524,56)" }, { name: "NjTFIAZI", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (532,81)" }, { name: "DX3AQjzT", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (535,92)" }, { name: "H2Ej_Ul2", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (549,47)" }, { name: "BwNSE0JI", path: "D:/TeaSpeak/web/shared/js/ui/frames/server_log.ts (549,104)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var log; +(function (log) { + let server; + (function (server) { + let Type; + (function (Type) { + Type["CONNECTION_BEGIN"] = "connection_begin"; + Type["CONNECTION_HOSTNAME_RESOLVE"] = "connection_hostname_resolve"; + Type["CONNECTION_HOSTNAME_RESOLVE_ERROR"] = "connection_hostname_resolve_error"; + Type["CONNECTION_HOSTNAME_RESOLVED"] = "connection_hostname_resolved"; + Type["CONNECTION_LOGIN"] = "connection_login"; + Type["CONNECTION_CONNECTED"] = "connection_connected"; + Type["CONNECTION_FAILED"] = "connection_failed"; + Type["DISCONNECTED"] = "disconnected"; + Type["CONNECTION_VOICE_SETUP_FAILED"] = "connection_voice_setup_failed"; + Type["CONNECTION_COMMAND_ERROR"] = "connection_command_error"; + Type["GLOBAL_MESSAGE"] = "global_message"; + Type["SERVER_WELCOME_MESSAGE"] = "server_welcome_message"; + Type["SERVER_HOST_MESSAGE"] = "server_host_message"; + Type["SERVER_HOST_MESSAGE_DISCONNECT"] = "server_host_message_disconnect"; + Type["SERVER_CLOSED"] = "server_closed"; + Type["SERVER_BANNED"] = "server_banned"; + Type["SERVER_REQUIRES_PASSWORD"] = "server_requires_password"; + Type["CLIENT_VIEW_ENTER"] = "client_view_enter"; + Type["CLIENT_VIEW_LEAVE"] = "client_view_leave"; + Type["CLIENT_VIEW_MOVE"] = "client_view_move"; + Type["CLIENT_NICKNAME_CHANGED"] = "client_nickname_changed"; + Type["CLIENT_NICKNAME_CHANGE_FAILED"] = "client_nickname_change_failed"; + Type["CLIENT_SERVER_GROUP_ADD"] = "client_server_group_add"; + Type["CLIENT_SERVER_GROUP_REMOVE"] = "client_server_group_remove"; + Type["CLIENT_CHANNEL_GROUP_CHANGE"] = "client_channel_group_change"; + Type["CHANNEL_CREATE"] = "channel_create"; + Type["CHANNEL_DELETE"] = "channel_delete"; + Type["ERROR_CUSTOM"] = "error_custom"; + Type["ERROR_PERMISSION"] = "error_permission"; + Type["RECONNECT_SCHEDULED"] = "reconnect_scheduled"; + Type["RECONNECT_EXECUTE"] = "reconnect_execute"; + Type["RECONNECT_CANCELED"] = "reconnect_canceled"; + })(Type = server.Type || (server.Type = {})); + server.MessageBuilders = { + "error_custom": (data, options) => { + return [$.spawn("div").addClass("log-error").text(data.message)]; + } + }; + })(server = log.server || (log.server = {})); + class ServerLog { + constructor(handle) { + this.history_length = 100; + this._log = []; + this.handle = handle; + this.auto_follow = true; + this._html_tag = $.spawn("div").addClass("container-log"); + this._log_container = $.spawn("div").addClass("container-messages"); + this._log_container.appendTo(this._html_tag); + this._html_tag.on('scroll', event => { + if (Date.now() - this._ignore_event < 100) { + this._ignore_event = 0; + return; + } + this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; + }); + } + log(type, data) { + const event = { + data: data, + timestamp: Date.now(), + type: type + }; + this._log.push(event); + while (this._log.length > this.history_length) + this._log.pop_front(); + this.append_log(event); + } + html_tag() { + return this._html_tag; + } + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._log_container = undefined; + this._log = undefined; + } + append_log(message) { + let container = $.spawn("div").addClass("log-message"); + /* build timestamp */ + { + const num = number => ('00' + number).substr(-2); + const date = new Date(message.timestamp); + $.spawn("div") + .addClass("timestamp") + .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") + .appendTo(container); + } + /* build message data */ + { + const builder = server.MessageBuilders[message.type]; + if (!builder) { + MessageHelper.formatMessage(_translations.OtbdfpKf || (_translations.OtbdfpKf = tr("missing log message builder {0}!")), message.type).forEach(e => e.addClass("log-error").appendTo(container)); + } + else { + const elements = builder(message.data, {}); + if (!elements || elements.length == 0) + return; /* discard message */ + container.append(...elements); + } + } + this._ignore_event = Date.now(); + this._log_container.append(container); + /* max history messages! */ + const messages = this._log_container.children(); + let index = 0; + while (messages.length - index > this.history_length) + index++; + const hide_elements = messages.filter(idx => idx < index); + hide_elements.hide(250, () => hide_elements.remove()); + if (this.auto_follow) { + clearTimeout(this._scroll_task); + /* do not enforce a recalculate style here */ + this._scroll_task = setTimeout(() => { + this._html_tag.scrollTop(this._html_tag[0].scrollHeight); + this._scroll_task = 0; + }, 5); + } + } + } + log.ServerLog = ServerLog; +})(log || (log = {})); +/* impl of the parsers */ +(function (log) { + let server; + (function (server) { + let impl; + (function (impl) { + const client_tag = (client, braces) => htmltags.generate_client_object({ + client_unique_id: client.client_unique_id, + client_id: client.client_id, + client_name: client.client_name, + add_braces: braces + }); + const channel_tag = (channel, braces) => htmltags.generate_channel_object({ + channel_display_name: channel.channel_name, + channel_name: channel.channel_name, + channel_id: channel.channel_id, + add_braces: braces + }); + server.MessageBuilders["connection_begin"] = (data, options) => { + return MessageHelper.formatMessage(_translations.TjslEtuu || (_translations.TjslEtuu = tr("Connecting to {0}{1}")), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); + }; + server.MessageBuilders["connection_hostname_resolve"] = (data, options) => MessageHelper.formatMessage(_translations.tgKTt68B || (_translations.tgKTt68B = tr("Resolving hostname"))); + server.MessageBuilders["connection_hostname_resolved"] = (data, options) => MessageHelper.formatMessage(_translations.EwoTjp_H || (_translations.EwoTjp_H = tr("Hostname resolved successfully to {0}:{1}")), data.address.server_hostname, data.address.server_port); + server.MessageBuilders["connection_hostname_resolve_error"] = (data, options) => MessageHelper.formatMessage(_translations.VLiF_jTd || (_translations.VLiF_jTd = tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}")), data.message); + server.MessageBuilders["connection_login"] = (data, options) => MessageHelper.formatMessage(_translations.wQTTZBOL || (_translations.wQTTZBOL = tr("Logging in..."))); + server.MessageBuilders["connection_failed"] = (data, options) => MessageHelper.formatMessage(_translations.kYlrLsJh || (_translations.kYlrLsJh = tr("Connect failed."))); + server.MessageBuilders["connection_connected"] = (data, options) => MessageHelper.formatMessage(_translations.O27TI6gw || (_translations.O27TI6gw = tr("Connected as {0}")), client_tag(data.own_client, true)); + server.MessageBuilders["connection_voice_setup_failed"] = (data, options) => { + return MessageHelper.formatMessage(_translations.DMjxS8Gy || (_translations.DMjxS8Gy = tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}")), data.reason, data.reconnect_delay > 0 ? _translations.ekA1Jk45 || (_translations.ekA1Jk45 = tr("yes")) : _translations.ZjRwAH8e || (_translations.ZjRwAH8e = tr("no"))); + }; + server.MessageBuilders["error_permission"] = (data, options) => { + return MessageHelper.formatMessage(_translations.BiOcwyQS || (_translations.BiOcwyQS = tr("Insufficient client permissions. Failed on permission {0}")), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); + }; + server.MessageBuilders["client_view_enter"] = (data, options) => { + if (data.reason == ViewReasonId.VREASON_SYSTEM) { + return undefined; + } + if (data.reason == ViewReasonId.VREASON_USER_ACTION) { + /* client appeared */ + if (data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? _translations.Ds9JwNz3 || (_translations.Ds9JwNz3 = tr("{0} appeared from {1} to your {2}")) : _translations.aEaFAUSl || (_translations.aEaFAUSl = tr("{0} appeared from {1} to {2}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } + else { + return MessageHelper.formatMessage(data.own_channel ? _translations.s3gggN2V || (_translations.s3gggN2V = tr("{0} connected to your channel {1}")) : _translations.LQfEHDHq || (_translations.LQfEHDHq = tr("{0} connected to channel {1}")), client_tag(data.client), channel_tag(data.channel_to)); + } + } + else if (data.reason == ViewReasonId.VREASON_MOVED) { + if (data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? _translations.eJVz0xy_ || (_translations.eJVz0xy_ = tr("{0} appeared from {1} to your channel {2}, moved by {3}")) : _translations.rHoYTw2I || (_translations.rHoYTw2I = tr("{0} appeared from {1} to {2}, moved by {3}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); + } + else { + return MessageHelper.formatMessage(data.own_channel ? _translations.a6eWAgVb || (_translations.a6eWAgVb = tr("{0} appeared to your channel {1}, moved by {2}")) : _translations.za6usZnN || (_translations.za6usZnN = tr("{0} appeared to {1}, moved by {2}")), client_tag(data.client), channel_tag(data.channel_to), client_tag(data.invoker)); + } + } + else if (data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + if (data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? _translations.GyEUhfQH || (_translations.GyEUhfQH = tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}")) : _translations.pqtlwDzo || (_translations.pqtlwDzo = tr("{0} appeared from {1} to {2}, kicked by {3}{4}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + else { + return MessageHelper.formatMessage(data.own_channel ? _translations.ssaVXJ6s || (_translations.ssaVXJ6s = tr("{0} appeared to your channel {1}, kicked by {2}{3}")) : _translations.HloiwTjB || (_translations.HloiwTjB = tr("{0} appeared to {1}, kicked by {2}{3}")), client_tag(data.client), channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + } + return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; + }; + server.MessageBuilders["client_view_move"] = (data, options) => { + if (data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.client_own ? _translations.T94YFRKJ || (_translations.T94YFRKJ = tr("You was moved by {3} from channel {1} to {2}")) : _translations.aRnZWAxP || (_translations.aRnZWAxP = tr("{0} was moved from channel {1} to {2} by {3}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); + } + else if (data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.client_own ? _translations.mC_Bj6QF || (_translations.mC_Bj6QF = tr("You switched from channel {1} to {2}")) : _translations.M3RlPff9 || (_translations.M3RlPff9 = tr("{0} switched from channel {1} to {2}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } + else if (data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.client_own ? _translations.OC81J6RI || (_translations.OC81J6RI = tr("You got kicked out of the channel {1} to channel {2} by {3}{4}")) : _translations.OzN4pqWV || (_translations.OzN4pqWV = tr("{0} got kicked from channel {1} to {2} by {3}{4}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; + }; + server.MessageBuilders["client_view_leave"] = (data, options) => { + if (data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.own_channel ? _translations.nI0lFUtf || (_translations.nI0lFUtf = tr("{0} disappeared from your channel {1} to {2}")) : _translations.PAxjJKgM || (_translations.PAxjJKgM = tr("{0} disappeared from {1} to {2}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } + else if (data.reason == ViewReasonId.VREASON_SERVER_LEFT) { + return MessageHelper.formatMessage(_translations.RX3ynvys || (_translations.RX3ynvys = tr("{0} left the server{1}")), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } + else if (data.reason == ViewReasonId.VREASON_SERVER_KICK) { + return MessageHelper.formatMessage(_translations.KkAPpmPk || (_translations.KkAPpmPk = tr("{0} was kicked from the server by {1}.{2}")), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + else if (data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.own_channel ? _translations.KLX2eoxD || (_translations.KLX2eoxD = tr("{0} was kicked from your channel by {2}.{3}")) : _translations.mBmCZEyz || (_translations.mBmCZEyz = tr("{0} was kicked from channel {1} by {2}.{3}")), client_tag(data.client), channel_tag(data.channel_from), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + else if (data.reason == ViewReasonId.VREASON_BAN) { + let duration = "permanently"; + if (data.ban_time) + duration = "for " + formatDate(data.ban_time); + return MessageHelper.formatMessage(_translations.b_y3zLOL || (_translations.b_y3zLOL = tr("{0} was banned {1} by {2}.{3}")), client_tag(data.client), duration, client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } + else if (data.reason == ViewReasonId.VREASON_TIMEOUT) { + return MessageHelper.formatMessage(_translations.Mcvtq5Rd || (_translations.Mcvtq5Rd = tr("{0} timed out{1}")), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } + else if (data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.own_channel ? _translations.owzm_mo1 || (_translations.owzm_mo1 = tr("{0} disappeared from your channel {1} to {2}, moved by {3}")) : _translations.iNLIAxK4 || (_translations.iNLIAxK4 = tr("{0} disappeared from {1} to {2}, moved by {3}")), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); + } + return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; + }; + server.MessageBuilders["server_welcome_message"] = (data, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; + server.MessageBuilders["server_host_message"] = (data, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; + server.MessageBuilders["client_nickname_changed"] = (data, options) => { + if (data.own_client) { + return MessageHelper.formatMessage(_translations.hQ_fh6ph || (_translations.hQ_fh6ph = tr("Nickname successfully changed."))); + } + else { + return MessageHelper.formatMessage(_translations.LrNB3SF3 || (_translations.LrNB3SF3 = tr("{0} changed his nickname from \"{1}\" to \"{2}\"")), client_tag(data.client), data.old_name, data.new_name); + } + }; + server.MessageBuilders["global_message"] = (data, options) => { + return []; /* we do not show global messages within log */ + }; + server.MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(_translations.NjTFIAZI || (_translations.NjTFIAZI = tr("Disconnected from server"))); + server.MessageBuilders["reconnect_scheduled"] = (data, options) => { + return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, _translations.DX3AQjzT || (_translations.DX3AQjzT = tr("now")))); + }; + server.MessageBuilders["reconnect_canceled"] = (data, options) => { + return tra("Canceled reconnect."); + }; + server.MessageBuilders["reconnect_execute"] = (data, options) => { + return tra("Reconnecting..."); + }; + server.MessageBuilders["server_banned"] = (data, options) => { + let result; + const time = data.time == 0 ? _translations.H2Ej_Ul2 || (_translations.H2Ej_Ul2 = tr("ever")) : MessageHelper.format_time(data.time * 1000, _translations.BwNSE0JI || (_translations.BwNSE0JI = tr("one second"))); + if (data.invoker.client_id > 0) { + if (data.message) + result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); + else + result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); + } + else { + if (data.message) + result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); + else + result = tra("You've been banned from the server for {0}.", time); + } + return result.map(e => e.addClass("log-error")); + }; + })(impl || (impl = {})); + })(server = log.server || (log.server = {})); +})(log || (log = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["918f834521cd8b02e67c6b743aecf2431853a74ac702b87b960137ed0f080dd0"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["918f834521cd8b02e67c6b743aecf2431853a74ac702b87b960137ed0f080dd0"] = "918f834521cd8b02e67c6b743aecf2431853a74ac702b87b960137ed0f080dd0"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var chat; +(function (chat) { + class ChatBox { + constructor() { + this._message_history = []; + this._message_history_length = 100; + this._message_history_index = 0; + this.typing_interval = 2000; /* update frequency */ + this._enabled = true; + this.__callback_key_up = this._callback_key_up.bind(this); + this.__callback_key_down = this._callback_key_down.bind(this); + this.__callback_text_changed = this._callback_text_changed.bind(this); + this.__callback_paste = event => this._callback_paste(event); + this._build_html_tag(); + this._initialize_listener(); + } + html_tag() { + return this._html_tag; + } + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._html_input = undefined; + clearTimeout(this._typing_timeout); + this.__callback_text_changed = undefined; + this.__callback_key_down = undefined; + this.__callback_paste = undefined; + this.callback_text = undefined; + this.callback_typing = undefined; + } + _initialize_listener() { + this._html_input.on("cut paste drop keydown keyup", (event) => this.__callback_text_changed(event)); + this._html_input.on("change", this.__callback_text_changed); + this._html_input.on("keydown", this.__callback_key_down); + this._html_input.on("keyup", this.__callback_key_up); + this._html_input.on("paste", this.__callback_paste); + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_chatbox").renderTag({ + emojy_support: settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES) + }); + this._html_input = this._html_tag.find(".textarea"); + const tag = this._html_tag.find('.button-emoji'); + tag.lsxEmojiPicker({ + width: 300, + height: 400, + twemoji: typeof (window.twemoji) !== "undefined", + onSelect: emoji => this._html_input.html(this._html_input.html() + emoji.value), + closeOnSelect: false + }); + } + _callback_text_changed(event) { + if (event && event.defaultPrevented) + return; + /* Auto resize */ + const text = this._html_input[0]; + text.style.height = "1em"; + text.style.height = text.scrollHeight + 'px'; + if (!event || (event.type !== "keydown" && event.type !== "keyup" && event.type !== "change")) + return; + this._typing_last_event = Date.now(); + if (this._typing_timeout) + return; + const _trigger_typing = (last_time) => { + if (this._typing_last_event <= last_time) { + this._typing_timeout = 0; + return; + } + try { + if (this.callback_typing) + this.callback_typing(); + } + finally { + this._typing_timeout = setTimeout(_trigger_typing, this.typing_interval, this._typing_last_event); + } + }; + _trigger_typing(0); /* We def want that*/ + } + _text(element) { + if (typeof (element) !== "object") + return element; + if (element instanceof HTMLImageElement) + return element.alt || element.title; + if (element instanceof HTMLBRElement) { + return '\n'; + } + if (element.childNodes.length > 0) + return [...element.childNodes].map(e => this._text(e)).join(""); + if (element.nodeType == Node.TEXT_NODE) + return element.textContent; + return typeof (element.innerText) === "string" ? element.innerText : ""; + } + htmlEscape(message) { + const div = document.createElement('div'); + div.innerText = message; + message = div.innerHTML; + return message.replace(/ /g, ' '); + } + _callback_paste(event) { + const _event = event.originalEvent || event; + const clipboard = _event.clipboardData || window.clipboardData; + if (!clipboard) + return; + const raw_text = clipboard.getData('text/plain'); + const selection = window.getSelection(); + if (!selection.rangeCount) + return false; + let html_xml = clipboard.getData('text/html'); + if (!html_xml) + html_xml = $.spawn("div").text(raw_text).html(); + const parser = new DOMParser(); + const nodes = parser.parseFromString(html_xml, "text/html"); + let data = this._text(nodes.body); + /* fix prefix & suffix new lines */ + { + let prefix_length = 0, suffix_length = 0; + { + for (let i = 0; i < raw_text.length; i++) + if (raw_text.charAt(i) === '\n') + prefix_length++; + else if (raw_text.charAt(i) !== '\r') + break; + for (let i = raw_text.length - 1; i >= 0; i++) + if (raw_text.charAt(i) === '\n') + suffix_length++; + else if (raw_text.charAt(i) !== '\r') + break; + } + data = data.replace(/^[\n\r]+|[\n\r]+$/g, ''); + data = "\n".repeat(prefix_length) + data + "\n".repeat(suffix_length); + } + event.preventDefault(); + selection.deleteFromDocument(); + document.execCommand('insertHTML', false, this.htmlEscape(data)); + } + test_message(message) { + message = message + .replace(/ /gi, "") + .replace(/
/gi, "") + .replace(/\n/gi, "") + .replace(//gi, ""); + return message.length > 0; + } + _callback_key_down(event) { + if (event.key.toLowerCase() === "enter" && !event.shiftKey) { + event.preventDefault(); + /* deactivate chatbox when no callback? */ + let text = this._html_input[0].innerText; + if (!this.test_message(text)) + return; + this._message_history.push(text); + this._message_history_index = this._message_history.length; + if (this._message_history.length > this._message_history_length) + this._message_history = this._message_history.slice(this._message_history.length - this._message_history_length); + if (this.callback_text) { + this.callback_text(chat.helpers.preprocess_chat_message(text)); + } + if (this._typing_timeout) + clearTimeout(this._typing_timeout); + this._typing_timeout = 1; /* enforce no typing update while sending */ + this._html_input.text(""); + setTimeout(() => { + this.__callback_text_changed(); + this._typing_timeout = 0; /* enable text change listener again */ + }); + } + else if (event.key.toLowerCase() === "arrowdown") { + //TODO: Test for at the last line within the box + if (this._message_history_index < 0) + return; + if (this._message_history_index >= this._message_history.length) + return; /* OOB, even with the empty message */ + this._message_history_index++; + this._html_input[0].innerText = this._message_history[this._message_history_index] || ""; /* OOB just returns "undefined" */ + } + else if (event.key.toLowerCase() === "arrowup") { + //TODO: Test for at the first line within the box + if (this._message_history_index <= 0) + return; /* we cant go "down" */ + this._message_history_index--; + this._html_input[0].innerText = this._message_history[this._message_history_index]; + } + else { + if (this._message_history_index >= 0) { + if (this._message_history_index >= this._message_history.length) { + if ("" !== this._html_input[0].innerText) + this._message_history_index = -1; + } + else if (this._message_history[this._message_history_index] !== this._html_input[0].innerText) + this._message_history_index = -1; + } + } + } + _callback_key_up(event) { + if ("" === this._html_input[0].innerText) + this._message_history_index = this._message_history.length; + } + set_enabled(flag) { + if (this._enabled === flag) + return; + if (!this._context_task) { + this._enabled = flag; + /* Allow the browser to asynchronously recalculate everything */ + this._context_task = setTimeout(() => { + this._context_task = undefined; + this._html_input.each((_, e) => { e.contentEditable = this._enabled ? "true" : "false"; }); + }); + this._html_tag.find('.button-emoji').toggleClass("disabled", !flag); + } + } + is_enabled() { + return this._enabled; + } + focus_input() { + this._html_input.focus(); + } + } + chat.ChatBox = ChatBox; +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c5be3188cf21fcbc775719d2ac72dd358cb3e8e285fbafe64efad84171587ae9"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c5be3188cf21fcbc775719d2ac72dd358cb3e8e285fbafe64efad84171587ae9"] = "c5be3188cf21fcbc775719d2ac72dd358cb3e8e285fbafe64efad84171587ae9"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "fLceMi2w", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (15,56)" }, { name: "p3JBDN23", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (176,52)" }, { name: "p_7G78re", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (179,52)" }, { name: "HIvttmjm", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (352,73)" }, { name: "yUP_LiSX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (352,94)" }, { name: "i_VB2Zg6", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (380,74)" }, { name: "dDilhGjF", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (384,45)" }, { name: "uZp2aiUD", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (386,85)" }, { name: "SLaP2Hpu", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (407,45)" }, { name: "LVc9nB1W", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (409,44)" }, { name: "hVY41Mvc", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (411,45)" }, { name: "BGNiWoVt", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (413,47)" }, { name: "dE7l9KPA", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (415,47)" }, { name: "WWKyCzCv", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/chat_helper.ts (417,30)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var chat; +(function (chat) { + let helpers; + (function (helpers) { + //https://regex101.com/r/YQbfcX/2 + //static readonly URL_REGEX = /^(?([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?(?:[^\s?]+)?)(?:\?(?\S+))?)?$/gm; + const URL_REGEX = /^(([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/((?:[^\s?]+)?)(?:\?(\S+))?)?$/gm; + function process_urls(message) { + const words = message.split(/[ \n]/); + for (let index = 0; index < words.length; index++) { + const flag_escaped = words[index].startsWith('!'); + const unescaped = flag_escaped ? words[index].substr(1) : words[index]; + _try: try { + const url = new URL(unescaped); + log.debug(LogCategory.GENERAL, _translations.fLceMi2w || (_translations.fLceMi2w = tr("Chat message contains URL: %o")), url); + if (url.protocol !== 'http:' && url.protocol !== 'https:') + break _try; + if (flag_escaped) { + message = undefined; + words[index] = unescaped; + } + else { + message = undefined; + words[index] = "[url=" + url.toString() + "]" + url.toString() + "[/url]"; + } + } + catch (e) { /* word isn't an url */ } + if (unescaped.match(URL_REGEX)) { + if (flag_escaped) { + message = undefined; + words[index] = unescaped; + } + else { + message = undefined; + words[index] = "[url=" + unescaped + "]" + unescaped + "[/url]"; + } + } + } + return message || words.join(" "); + } + let md2bbc; + (function (md2bbc) { + class Renderer { + render(tokens, options, env) { + this.last_paragraph = undefined; + this._options = options; + let result = ''; + //TODO: Escape BB-Codes + for (let index = 0; index < tokens.length; index++) { + if (tokens[index].type === 'inline') { + result += this.render_inline(tokens[index].children, index); + } + else { + result += this.render_token(tokens[index], index); + } + } + this._options = undefined; + return result; + } + render_token(token, index) { + log.debug(LogCategory.GENERAL, _translations.p3JBDN23 || (_translations.p3JBDN23 = tr("Render Markdown token: %o")), token); + const renderer = Renderer.renderers[token.type]; + if (typeof (renderer) === "undefined") { + log.warn(LogCategory.CHAT, _translations.p_7G78re || (_translations.p_7G78re = tr("Missing markdown to bbcode renderer for token %s: %o")), token.type, token); + return token.content || ""; + } + const result = renderer(this, token, index); + if (token.type === "paragraph_open") + this.last_paragraph = token; + return result; + } + render_inline(tokens, index) { + let result = ''; + for (let index = 0; index < tokens.length; index++) { + result += this.render_token(tokens[index], index); + } + return result; + } + options() { + return this._options; + } + maybe_escape_bb(text) { + if (this._options.escape_bb) + return xbbcode.escape(text); + return text; + } + } + Renderer.renderers = { + "text": (renderer, token) => renderer.options().process_url ? process_urls(renderer.maybe_escape_bb(token.content)) : renderer.maybe_escape_bb(token.content), + "softbreak": () => "\n", + "hardbreak": () => "\n", + "paragraph_open": (renderer, token) => { + const last_line = !renderer.last_paragraph || !renderer.last_paragraph.lines ? 0 : renderer.last_paragraph.lines[1]; + const lines = token.lines[0] - last_line; + return [...new Array(lines)].map(e => "[br]").join(""); + }, + "paragraph_close": () => "", + "strong_open": (renderer, token) => "[b]", + "strong_close": (renderer, token) => "[/b]", + "em_open": (renderer, token) => "[i]", + "em_close": (renderer, token) => "[/i]", + "del_open": () => "[s]", + "del_close": () => "[/s]", + "sup": (renderer, token) => "[sup]" + renderer.maybe_escape_bb(token.content) + "[/sup]", + "sub": (renderer, token) => "[sub]" + renderer.maybe_escape_bb(token.content) + "[/sub]", + "bullet_list_open": () => "[ul]", + "bullet_list_close": () => "[/ul]", + "ordered_list_open": () => "[ol]", + "ordered_list_close": () => "[/ol]", + "list_item_open": () => "[li]", + "list_item_close": () => "[/li]", + "table_open": () => "[table]", + "table_close": () => "[/table]", + "thead_open": () => "", + "thead_close": () => "", + "tbody_open": () => "", + "tbody_close": () => "", + "tr_open": () => "[tr]", + "tr_close": () => "[/tr]", + "th_open": (renderer, token) => "[th" + (token.align ? ("=" + token.align) : "") + "]", + "th_close": () => "[/th]", + "td_open": () => "[td]", + "td_close": () => "[/td]", + "link_open": (renderer, token) => "[url" + (token.href ? ("=" + token.href) : "") + "]", + "link_close": () => "[/url]", + "image": (renderer, token) => "[img=" + (token.src) + "]" + (token.alt || token.src) + "[/img]", + //footnote_ref + //"content": "==Marked text==", + //mark_open + //mark_close + //++Inserted text++ + "ins_open": () => "[u]", + "ins_close": () => "[/u]", + /* +``` +test +[/code] +test +``` + */ + "code": (renderer, token) => "[i-code]" + xbbcode.escape(token.content) + "[/i-code]", + "fence": (renderer, token) => "[code" + (token.params ? ("=" + token.params) : "") + "]" + xbbcode.escape(token.content) + "[/code]", + "heading_open": (renderer, token) => "[size=" + (9 - Math.min(4, token.hLevel)) + "]", + "heading_close": (renderer, token) => "[/size][hr]", + "hr": () => "[hr]", + }; + md2bbc.Renderer = Renderer; + })(md2bbc || (md2bbc = {})); + let _renderer; + function process_markdown(message, options) { + if (typeof (window.remarkable) === "undefined") + return (options.process_url ? process_urls(message) : message); + if (!_renderer) { + _renderer = new window.remarkable.Remarkable('full'); + _renderer.set({ + typographer: true + }); + _renderer.renderer = new md2bbc.Renderer(); + _renderer.inline.ruler.disable(['newline', 'autolink']); + } + _renderer.set({ + process_url: !!options.process_url, + escape_bb: !!options.escape_bb + }); + let result = _renderer.render(message); + if (result.endsWith("\n")) + result = result.substr(0, result.length - 1); + return result; + } + function preprocess_chat_message(message) { + const process_url = settings.static_global(Settings.KEY_CHAT_TAG_URLS); + const parse_markdown = settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN); + const escape_bb = !settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE); + if (parse_markdown) + return process_markdown(message, { + process_url: process_url, + escape_bb: escape_bb + }); + if (escape_bb) + message = xbbcode.escape(message); + return process_url ? process_urls(message) : message; + } + helpers.preprocess_chat_message = preprocess_chat_message; + let history; + (function (history) { + let _local_cache; + function get_cache() { + return __awaiter(this, void 0, void 0, function* () { + if (_local_cache) + return _local_cache; + if (!('caches' in window)) + throw "missing cache extension!"; + return (_local_cache = yield caches.open('chat_history')); + }); + } + function load_history(key) { + return __awaiter(this, void 0, void 0, function* () { + const cache = yield get_cache(); + const request = new Request("https://_local_cache/cache_request_" + key); + const cached_response = yield cache.match(request); + if (!cached_response) + return undefined; + return yield cached_response.json(); + }); + } + history.load_history = load_history; + function save_history(key, value) { + return __awaiter(this, void 0, void 0, function* () { + const cache = yield get_cache(); + const request = new Request("https://_local_cache/cache_request_" + key); + const data = JSON.stringify(value); + const new_headers = new Headers(); + new_headers.set("Content-type", "application/json"); + new_headers.set("Content-length", data.length.toString()); + yield cache.put(request, new Response(data, { + headers: new_headers + })); + }); + } + history.save_history = save_history; + })(history = helpers.history || (helpers.history = {})); + let date; + (function (date) { + function same_day(a, b) { + a = a instanceof Date ? a : new Date(a); + b = b instanceof Date ? b : new Date(b); + if (a.getDate() !== b.getDate()) + return false; + if (a.getMonth() !== b.getMonth()) + return false; + return a.getFullYear() === b.getFullYear(); + } + date.same_day = same_day; + })(date = helpers.date || (helpers.date = {})); + })(helpers = chat.helpers || (chat.helpers = {})); + let format; + (function (format_1) { + let date; + (function (date_1) { + let ColloquialFormat; + (function (ColloquialFormat) { + ColloquialFormat[ColloquialFormat["YESTERDAY"] = 0] = "YESTERDAY"; + ColloquialFormat[ColloquialFormat["TODAY"] = 1] = "TODAY"; + ColloquialFormat[ColloquialFormat["GENERAL"] = 2] = "GENERAL"; + })(ColloquialFormat = date_1.ColloquialFormat || (date_1.ColloquialFormat = {})); + function date_format(date, now, ignore_settings) { + if (!ignore_settings && !settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)) + return ColloquialFormat.GENERAL; + let delta_day = now.getDate() - date.getDate(); + if (delta_day < 1) /* month change? */ + delta_day = date.getDate() - now.getDate(); + if (delta_day == 0) + return ColloquialFormat.TODAY; + else if (delta_day == 1) + return ColloquialFormat.YESTERDAY; + return ColloquialFormat.GENERAL; + } + date_1.date_format = date_format; + function format_date_general(date, hours) { + return ('00' + date.getDate()).substr(-2) + "." + + ('00' + date.getMonth()).substr(-2) + "." + + date.getFullYear() + + (typeof (hours) === "undefined" || hours ? " at " + + ('00' + date.getHours()).substr(-2) + ":" + + ('00' + date.getMinutes()).substr(-2) + : ""); + } + date_1.format_date_general = format_date_general; + function format_date_colloquial(date, current_timestamp) { + const format = date_format(date, current_timestamp); + if (format == ColloquialFormat.GENERAL) { + return { + result: format_date_general(date), + format: format + }; + } + else { + let hrs = date.getHours(); + let time = "AM"; + if (hrs > 12) { + hrs -= 12; + time = "PM"; + } + return { + result: (format == ColloquialFormat.YESTERDAY ? _translations.HIvttmjm || (_translations.HIvttmjm = tr("Yesterday at")) : _translations.yUP_LiSX || (_translations.yUP_LiSX = tr("Today at"))) + " " + hrs + ":" + date.getMinutes() + " " + time, + format: format + }; + } + } + date_1.format_date_colloquial = format_date_colloquial; + function format_chat_time(date) { + const timestamp = date.getTime(); + const current_timestamp = new Date(); + const result = { + result: "", + next_update: 0 + }; + if (settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)) { + const format = format_date_colloquial(date, current_timestamp); + result.result = format.result; + result.next_update = 0; /* TODO: Update on day change? */ + } + else { + const delta = current_timestamp.getTime() - timestamp; + if (delta < 2000) { + result.result = "now"; + result.next_update = 2500 - delta; /* update after two seconds */ + } + else if (delta < 30000) { /* 30 seconds */ + result.result = Math.floor(delta / 1000) + " " + (_translations.i_VB2Zg6 || (_translations.i_VB2Zg6 = tr("seconds ago"))); + result.next_update = 1000; /* update every second */ + } + else if (delta < 30 * 60 * 1000) { /* 30 minutes */ + if (delta < 120 * 1000) + result.result = _translations.dDilhGjF || (_translations.dDilhGjF = tr("one minute ago")); + else + result.result = Math.floor(delta / (1000 * 60)) + " " + (_translations.uZp2aiUD || (_translations.uZp2aiUD = tr("minutes ago"))); + result.next_update = 60000; /* updater after a minute */ + } + else { + result.result = format_date_colloquial(date, current_timestamp).result; + result.next_update = 0; /* TODO: Update on day change? */ + } + } + return result; + } + date_1.format_chat_time = format_chat_time; + })(date = format_1.date || (format_1.date = {})); + let time; + (function (time) { + function format_online_time(secs) { + let years = Math.floor(secs / (60 * 60 * 24 * 365)); + let days = Math.floor(secs / (60 * 60 * 24)) % 365; + let hours = Math.floor(secs / (60 * 60)) % 24; + let minutes = Math.floor(secs / 60) % 60; + let seconds = Math.floor(secs % 60); + let result = ""; + if (years > 0) + result += years + " " + (_translations.SLaP2Hpu || (_translations.SLaP2Hpu = tr("years"))) + " "; + if (years > 0 || days > 0) + result += days + " " + (_translations.LVc9nB1W || (_translations.LVc9nB1W = tr("days"))) + " "; + if (years > 0 || days > 0 || hours > 0) + result += hours + " " + (_translations.hVY41Mvc || (_translations.hVY41Mvc = tr("hours"))) + " "; + if (years > 0 || days > 0 || hours > 0 || minutes > 0) + result += minutes + " " + (_translations.BGNiWoVt || (_translations.BGNiWoVt = tr("minutes"))) + " "; + if (years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) + result += seconds + " " + (_translations.dE7l9KPA || (_translations.dE7l9KPA = tr("seconds"))) + " "; + else + result = (_translations.WWKyCzCv || (_translations.WWKyCzCv = tr("now"))) + " "; + return result.substr(0, result.length - 1); + } + time.format_online_time = format_online_time; + })(time = format_1.time || (format_1.time = {})); + })(format = chat.format || (chat.format = {})); +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["b95f363e0f5bc44d4fb25a34df7141f88562c49ef5fb94014debc75d5db9cb0b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["b95f363e0f5bc44d4fb25a34df7141f88562c49ef5fb94014debc75d5db9cb0b"] = "b95f363e0f5bc44d4fb25a34df7141f88562c49ef5fb94014debc75d5db9cb0b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Dp1iLhtX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (109,67)" }, { name: "D09E5x0q", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (148,40)" }, { name: "EsaodnJz", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (150,40)" }, { name: "AtQnQgBE", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (152,40)" }, { name: "vwdjmn_F", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (175,51)" }, { name: "l65uhuvX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (186,51)" }, { name: "Qdw6LANe", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (194,51)" }, { name: "iSy8yGYs", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (202,51)" }, { name: "M0LdnKoE", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (210,51)" }, { name: "Urfvx1ZJ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (218,51)" }, { name: "KNP4yV0s", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (238,73)" }, { name: "xrYdPJ1G", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (238,131)" }, { name: "rVlVp788", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (246,77)" }, { name: "NKtvfR4K", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (265,77)" }, { name: "pgVV72hD", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (269,66)" }, { name: "OyhbrRno", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/client_info.ts (269,110)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var chat; +(function (chat) { + class ClientInfo { + constructor(handle) { + this.handle = handle; + this._build_html_tag(); + } + html_tag() { + return this._html_tag; + } + destroy() { + clearInterval(this._online_time_updater); + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._current_client = undefined; + this.previous_frame_content = undefined; + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_client_info").renderTag(); + this._html_tag.find(".button-close").on('click', () => { + if (this.previous_frame_content === chat.FrameContent.CLIENT_INFO) + this.previous_frame_content = chat.FrameContent.NONE; + this.handle.set_content(this.previous_frame_content); + }); + this._html_tag.find(".button-more").on('click', () => { + if (!this._current_client) + return; + Modals.openClientInfo(this._current_client); + }); + this._html_tag.find('.container-avatar-edit').on('click', () => this.handle.handle.update_avatar()); + } + current_client() { + return this._current_client; + } + set_current_client(client, enforce) { + if (client) + client.updateClientVariables(); /* just to ensure */ + if (client === this._current_client && (typeof (enforce) === "undefined" || !enforce)) + return; + this._current_client = client; + /* updating the header */ + { + const client_name = this._html_tag.find(".client-name"); + client_name.children().remove(); + htmltags.generate_client_object({ + add_braces: false, + client_name: client ? client.clientNickName() : "undefined", + client_unique_id: client ? client.clientUid() : "", + client_id: client ? client.clientId() : 0 + }).appendTo(client_name); + const client_description = this._html_tag.find(".client-description"); + client_description.text(client ? client.properties.client_description : "").toggle(!!client.properties.client_description); + const is_local_entry = client instanceof LocalClientEntry; + const container_avatar = this._html_tag.find(".container-avatar"); + container_avatar.find(".avatar").remove(); + if (client) { + const avatar = this.handle.handle.fileManager.avatars.generate_chat_tag({ id: client.clientId() }, client.clientUid()); + if (!is_local_entry) { + avatar.css("cursor", "pointer").on('click', event => { + image_preview.preview_image_tag(this.handle.handle.fileManager.avatars.generate_chat_tag({ id: client.clientId() }, client.clientUid())); + }); + } + avatar.appendTo(container_avatar); + } + else + this.handle.handle.fileManager.avatars.generate_chat_tag(undefined, undefined).appendTo(container_avatar); + container_avatar.toggleClass("editable", is_local_entry); + } + /* updating the info fields */ + { + const online_time = this._html_tag.find(".client-online-time"); + online_time.text(chat.format.time.format_online_time(client ? client.calculateOnlineTime() : 0)); + if (this._online_time_updater) { + clearInterval(this._online_time_updater); + this._online_time_updater = 0; + } + if (client) { + this._online_time_updater = setInterval(() => { + const client = this._current_client; + if (!client) { + clearInterval(this._online_time_updater); + this._online_time_updater = undefined; + return; + } + if (client.currentChannel()) /* If he has no channel then he might be disconnected */ + online_time.text(chat.format.time.format_online_time(client.calculateOnlineTime())); + else { + online_time.text(online_time.text() + (_translations.Dp1iLhtX || (_translations.Dp1iLhtX = tr(" (left view)")))); + clearInterval(this._online_time_updater); + } + }, 1000); + } + const country = this._html_tag.find(".client-country"); + country.children().detach(); + const country_code = (client ? client.properties.client_country : undefined) || "xx"; + $.spawn("div").addClass("country flag-" + country_code.toLowerCase()).appendTo(country); + $.spawn("a").text(i18n.country_name(country_code.toUpperCase())).appendTo(country); + const version = this._html_tag.find(".client-version"); + version.children().detach(); + if (client) { + let platform = client.properties.client_platform; + if (platform.indexOf("Win32") != 0 && (client.properties.client_version.indexOf("Win64") != -1 || client.properties.client_version.indexOf("WOW64") != -1)) + platform = platform.replace("Win32", "Win64"); + $.spawn("a").attr("title", client.properties.client_version).text(client.properties.client_version.split(" ")[0] + " on " + platform).appendTo(version); + } + const volume = this._html_tag.find(".client-local-volume"); + volume.text((client && client.get_audio_handle() ? (client.get_audio_handle().get_volume() * 100) : -1).toFixed(0) + "%"); + } + /* teaspeak forum */ + { + const container_forum = this._html_tag.find(".container-teaforo"); + if (client && client.properties.client_teaforo_id) { + container_forum.show(); + const container_data = container_forum.find(".client-teaforo-account"); + container_data.children().remove(); + let text = client.properties.client_teaforo_name; + if ((client.properties.client_teaforo_flags & 0x01) > 0) + text += " (" + (_translations.D09E5x0q || (_translations.D09E5x0q = tr("Banned"))) + ")"; + if ((client.properties.client_teaforo_flags & 0x02) > 0) + text += " (" + (_translations.EsaodnJz || (_translations.EsaodnJz = tr("Stuff"))) + ")"; + if ((client.properties.client_teaforo_flags & 0x04) > 0) + text += " (" + (_translations.AtQnQgBE || (_translations.AtQnQgBE = tr("Premium"))) + ")"; + $.spawn("a") + .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) + .attr("target", "_blank") + .text(text) + .appendTo(container_data); + } + else { + container_forum.hide(); + } + } + /* update the client status */ + { + //TODO Implement client status! + const container_status = this._html_tag.find(".container-client-status"); + const container_status_entries = container_status.find(".client-status"); + container_status_entries.children().detach(); + if (client) { + if (client.properties.client_away) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-away"), $.spawn("a").text(_translations.vwdjmn_F || (_translations.vwdjmn_F = tr("Away"))), client.properties.client_away_message ? + $.spawn("a").addClass("away-message").text("(" + client.properties.client_away_message + ")") : + undefined)); + } + if (client.is_muted()) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-input_muted_local"), $.spawn("a").text(_translations.l65uhuvX || (_translations.l65uhuvX = tr("Client local muted"))))); + } + if (!client.properties.client_output_hardware) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-hardware_output_muted"), $.spawn("a").text(_translations.Qdw6LANe || (_translations.Qdw6LANe = tr("Speakers/Headphones disabled"))))); + } + if (!client.properties.client_input_hardware) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-hardware_input_muted"), $.spawn("a").text(_translations.iSy8yGYs || (_translations.iSy8yGYs = tr("Microphone disabled"))))); + } + if (client.properties.client_output_muted) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-output_muted"), $.spawn("a").text(_translations.M0LdnKoE || (_translations.M0LdnKoE = tr("Speakers/Headphones Muted"))))); + } + if (client.properties.client_input_muted) { + container_status_entries.append($.spawn("div").addClass("status-entry").append($.spawn("div").addClass("icon_em client-input_muted"), $.spawn("a").text(_translations.Urfvx1ZJ || (_translations.Urfvx1ZJ = tr("Microphone Muted"))))); + } + } + container_status.toggle(container_status_entries.children().length > 0); + } + /* update client server groups */ + { + const container_groups = this._html_tag.find(".client-group-server"); + container_groups.children().detach(); + if (client) { + const invalid_groups = []; + const groups = client.assignedServerGroupIds().map(group_id => { + const result = this.handle.handle.groups.serverGroup(group_id); + if (!result) + invalid_groups.push(group_id); + return result; + }).filter(e => !!e).sort(GroupManager.sorter()); + for (const invalid_id of invalid_groups) { + container_groups.append($.spawn("a").text("{" + (_translations.KNP4yV0s || (_translations.KNP4yV0s = tr("server group "))) + invalid_groups + "}").attr("title", (_translations.xrYdPJ1G || (_translations.xrYdPJ1G = tr("Missing server group id!"))) + " (" + invalid_groups + ")")); + } + for (let group of groups) { + container_groups.append($.spawn("div").addClass("group-container") + .append(this.handle.handle.fileManager.icons.generateTag(group.properties.iconid)).append($.spawn("a").text(group.name).attr("title", (_translations.rVlVp788 || (_translations.rVlVp788 = tr("Group id: "))) + group.id))); + } + } + } + /* update client channel group */ + { + const container_group = this._html_tag.find(".client-group-channel"); + container_group.children().detach(); + if (client) { + const group_id = client.assignedChannelGroup(); + let group = this.handle.handle.groups.channelGroup(group_id); + if (group) { + container_group.append($.spawn("div").addClass("group-container") + .append(this.handle.handle.fileManager.icons.generateTag(group.properties.iconid)).append($.spawn("a").text(group.name).attr("title", (_translations.NKtvfR4K || (_translations.NKtvfR4K = tr("Group id: "))) + group_id))); + } + else { + container_group.append($.spawn("a").text(_translations.pgVV72hD || (_translations.pgVV72hD = tr("Invalid channel group!"))).attr("title", (_translations.OyhbrRno || (_translations.OyhbrRno = tr("Missing channel group id!"))) + " (" + group_id + ")")); + } + } + } + } + } + chat.ClientInfo = ClientInfo; +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c8d174f4cbf19f8187e08cd0cc4bb35c191fd340838f5553eaec62915ed394f9"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c8d174f4cbf19f8187e08cd0cc4bb35c191fd340838f5553eaec62915ed394f9"] = "c8d174f4cbf19f8187e08cd0cc4bb35c191fd340838f5553eaec62915ed394f9"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "hh__Ub6e", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (105,72)" }, { name: "TQ1xVaxQ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (106,81)" }, { name: "gusrL0gQ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (222,49)" }, { name: "Rghf6HNS", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (248,49)" }, { name: "lfcMsCaE", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (433,49)" }, { name: "mwRMbLH_", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (436,38)" }, { name: "keOq3AeP", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (436,98)" }, { name: "MQA1F_tv", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (438,47)" }, { name: "RbUV5nYv", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/conversations.ts (608,52)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var chat; +(function (chat) { + let channel; + (function (channel) { + class Conversation { + constructor(handle, channel_id) { + this._container_no_permissions_shown = false; + this._container_is_private_shown = false; + this._container_no_support_shown = false; + this._view_max_messages = 40; /* reset to 40 again as soon we tab out :) */ + this._view_entries = []; + this._last_messages = []; + this._last_messages_timestamp = 0; + this.handle = handle; + this.channel_id = channel_id; + this._build_html_tag(); + } + html_tag() { return this._html_tag; } + destroy() { + this._first_unread_message_pointer.html_element.detach(); + this._first_unread_message_pointer = undefined; + this._view_older_messages.html_element.detach(); + this._view_older_messages = undefined; + for (const view_entry of this._view_entries) { + view_entry.html_element.detach(); + clearTimeout(view_entry.update_timer); + } + this._view_entries = []; + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_channel_messages").renderTag(); + this._container_new_message = this._html_tag.find(".new-message"); + this._container_no_permissions = this._html_tag.find(".no-permissions").hide(); + this._container_is_private = this._html_tag.find(".private-conversation").hide(); + this._container_no_support = this._html_tag.find(".not-supported").hide(); + this._container_messages = this._html_tag.find(".container-messages"); + this._container_messages.on('scroll', event => { + const exact_position = this._container_messages[0].scrollTop + this._container_messages[0].clientHeight; + const current_view = exact_position + this._container_messages[0].clientHeight * .125; + if (current_view > this._container_messages[0].scrollHeight) { + this._scroll_position = undefined; + } + else { + this._scroll_position = this._container_messages[0].scrollTop; + } + const will_visible = !!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position; + const is_visible = this._container_new_message[0].classList.contains("shown"); + if (!is_visible && will_visible) + this._container_new_message[0].classList.add("shown"); + if (is_visible && !will_visible) + this._container_new_message[0].classList.remove("shown"); + //This causes a Layout recalc (Forced reflow) + //this._container_new_message.toggleClass("shown",!!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position); + }); + this._view_older_messages = this._generate_view_spacer(_translations.hh__Ub6e || (_translations.hh__Ub6e = tr("Load older messages")), "old"); + this._first_unread_message_pointer = this._generate_view_spacer(_translations.TQ1xVaxQ || (_translations.TQ1xVaxQ = tr("Unread messages")), "new"); + this._view_older_messages.html_element.appendTo(this._container_messages).on('click', event => { + this.fetch_older_messages(); + }); + this._container_new_message.on('click', event => { + if (!this._first_unread_message) + return; + this._scroll_position = this._first_unread_message_pointer.html_element[0].offsetTop; + this.fix_scroll(true); + }); + this._container_messages.on('click', event => { + if (this._container_new_message.hasClass('shown')) + return; /* we have clicked, but no chance to see the unread message pointer */ + this._mark_read(); + }); + this.set_flag_private(false); + } + is_unread() { return !!this._first_unread_message; } + mark_read() { this._mark_read(); } + _mark_read() { + if (this._first_unread_message) { + this._first_unread_message = undefined; + const ctree = this.handle.handle.handle.channelTree; + if (ctree && ctree.tag_tree()) + ctree.tag_tree().find(".marker-text-unread[conversation='" + this.channel_id + "']").addClass("hidden"); + } + this._first_unread_message_pointer.html_element.detach(); + } + _generate_view_message(data) { + const response = data; + if (response.html_element) + return response; + const timestamp = new Date(data.timestamp); + let time = chat.format.date.format_chat_time(timestamp); + response.html_element = $("#tmpl_frame_chat_channel_message").renderTag({ + timestamp: time.result, + client_name: htmltags.generate_client_object({ + add_braces: false, + client_name: data.sender_name, + client_unique_id: data.sender_unique_id, + client_id: 0 + }), + message: MessageHelper.bbcode_chat(data.message), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({ database_id: data.sender_database_id }, data.sender_unique_id) + }); + response.html_element.find(".button-delete").on('click', () => this.delete_message(data)); + if (time.next_update > 0) { + const _updater = () => { + time = chat.format.date.format_chat_time(timestamp); + response.html_element.find(".info .timestamp").text(time.result); + if (time.next_update > 0) + response.update_timer = setTimeout(_updater, time.next_update); + else + response.update_timer = 0; + }; + response.update_timer = setTimeout(_updater, time.next_update); + } + else { + response.update_timer = 0; + } + return response; + } + _generate_view_spacer(message, type) { + const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ + message: message + }).addClass("type-" + type); + return { + html_element: tag, + update_timer: 0 + }; + } + last_messages_timestamp() { + return this._last_messages_timestamp; + } + fetch_last_messages() { + const fetch_count = this._view_max_messages - this._last_messages.length; + const fetch_timestamp_end = this._last_messages_timestamp + 1; /* we want newer messages then the last message we have */ + //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] + this.handle.handle.handle.serverConnection.send_command("conversationhistory", { + cid: this.channel_id, + timestamp_end: fetch_timestamp_end, + message_count: fetch_count + }, { flagset: ["merge"], process_result: false }).catch(error => { + this._view_older_messages.html_element.toggleClass('shown', false); + if (error instanceof CommandResult) { + if (error.id == ErrorID.CONVERSATION_MORE_DATA) { + if (typeof (this._has_older_messages) === "undefined") + this._has_older_messages = true; + this._view_older_messages.html_element.toggleClass('shown', true); + return; + } + else if (error.id == ErrorID.PERMISSION_ERROR) { + this._container_no_permissions.show(); + this._container_no_permissions_shown = true; + } + else if (error.id == ErrorID.CONVERSATION_IS_PRIVATE) { + this.set_flag_private(true); + } + /* + else if(error.id == ErrorID.NOT_IMPLEMENTED || error.id == ErrorID.COMMAND_NOT_FOUND) { + this._container_no_support.show(); + this._container_no_support_shown = true; + } + */ + } + //TODO log and handle! + log.error(LogCategory.CHAT, _translations.gusrL0gQ || (_translations.gusrL0gQ = tr("Failed to fetch conversation history. %o")), error); + }).then(() => { + this.fix_scroll(true); + this.handle.update_chat_box(); + }); + } + fetch_older_messages() { + this._view_older_messages.html_element.toggleClass('shown', false); + const entry = this._view_entries.slice().reverse().find(e => 'timestamp' in e); + //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] + this.handle.handle.handle.serverConnection.send_command("conversationhistory", { + cid: this.channel_id, + timestamp_begin: entry.timestamp - 1, + message_count: this._view_max_messages + }, { flagset: ["merge"] }).catch(error => { + this._view_older_messages.html_element.toggleClass('shown', false); + if (error instanceof CommandResult) { + if (error.id == ErrorID.CONVERSATION_MORE_DATA) { + this._view_older_messages.html_element.toggleClass('shown', true); + this.handle.update_chat_box(); + return; + } + } + //TODO log and handle! + log.error(LogCategory.CHAT, _translations.Rghf6HNS || (_translations.Rghf6HNS = tr("Failed to fetch conversation history. %o")), error); + }).then(() => { + this.fix_scroll(true); + }); + } + register_new_message(message, update_view) { + /* lets insert the message at the right index */ + let _new_message = false; + { + let spliced = false; + for (let index = 0; index < this._last_messages.length; index++) { + if (this._last_messages[index].timestamp < message.timestamp) { + this._last_messages.splice(index, 0, message); + spliced = true; + _new_message = index == 0; /* only set flag if this has been inserted at the front */ + break; + } + else if (this._last_messages[index].timestamp == message.timestamp && this._last_messages[index].sender_database_id == message.sender_database_id) { + return; /* we already have that message */ + } + } + if (!spliced && this._last_messages.length < this._view_max_messages) { + this._last_messages.push(message); + } + this._last_messages_timestamp = this._last_messages[0].timestamp; + while (this._last_messages.length > this._view_max_messages) { + if (this._last_messages[this._last_messages.length - 1] == this._first_unread_message) + break; + this._last_messages.pop(); + } + } + /* message is within view */ + { + const entry = this._generate_view_message(message); + let previous; + for (let index = 0; index < this._view_entries.length; index++) { + const current_entry = this._view_entries[index]; + if (!('timestamp' in current_entry)) + continue; + if (current_entry.timestamp < message.timestamp) { + this._view_entries.splice(index, 0, entry); + previous = current_entry; + break; + } + } + if (!previous) + this._view_entries.push(entry); + if (previous) + entry.html_element.insertAfter(previous.html_element); + else + entry.html_element.insertAfter(this._view_older_messages.html_element); /* last element is already the current element */ + if (_new_message && (typeof (this._scroll_position) === "number" || this.handle.current_channel() !== this.channel_id || this.handle.handle.content_type() !== chat.FrameContent.CHANNEL_CHAT)) { + if (typeof (this._first_unread_message) === "undefined") + this._first_unread_message = entry; + this._first_unread_message_pointer.html_element.insertBefore(entry.html_element); + this._container_messages.trigger('scroll'); /* updates the new message stuff */ + } + if (typeof (update_view) !== "boolean" || update_view) + this.fix_scroll(true); + } + /* update chat state */ + this._container_no_permissions.hide(); + this._container_no_permissions_shown = false; + if (update_view) + this.handle.update_chat_box(); + } + fix_scroll(animate) { + if (this._scroll_fix_timer) { + this._scroll_animate = this._scroll_animate && animate; + return; + } + this._scroll_fix_timer = setTimeout(() => { + this._scroll_fix_timer = undefined; + let offset; + if (this._first_unread_message) { + offset = this._first_unread_message.html_element[0].offsetTop; + } + else if (typeof (this._scroll_position) !== "undefined") { + offset = this._scroll_position; + } + else { + offset = this._container_messages[0].scrollHeight; + } + if (this._scroll_animate) { + this._container_messages.stop(true).animate({ + scrollTop: offset + }, 'slow'); + } + else { + this._container_messages.stop(true).scrollTop(offset); + } + }, 5); + } + fix_view_size() { + this._view_older_messages.html_element.toggleClass('shown', !!this._has_older_messages); + let count = 0; + for (let index = 0; index < this._view_entries.length; index++) { + if ('timestamp' in this._view_entries[index]) + count++; + if (count > this._view_max_messages) { + this._view_entries.splice(index, this._view_entries.length - index).forEach(e => { + clearTimeout(e.update_timer); + e.html_element.remove(); + }); + this._has_older_messages = true; + this._view_older_messages.html_element.toggleClass('shown', true); + break; + } + } + } + chat_available() { + return !this._container_no_permissions_shown && !this._container_is_private_shown && !this._container_no_support_shown; + } + text_send_failed(error) { + log.warn(LogCategory.CHAT, "Failed to send text message! (%o)", error); + //TODO: Log if message send failed? + if (error instanceof CommandResult) { + if (error.id == ErrorID.PERMISSION_ERROR) { + //TODO: Split up between channel_text_message_send permission and no view permission + if (error.json["failed_permid"] == 0) { + this._container_no_permissions_shown = true; + this._container_no_permissions.show(); + this.handle.update_chat_box(); + } + } + } + } + set_flag_private(flag) { + if (this._flag_private === flag) + return; + this._flag_private = flag; + this.update_private_state(); + if (!flag) + this.fetch_last_messages(); + } + update_private_state() { + if (!this._flag_private) { + this._container_is_private.hide(); + this._container_is_private_shown = false; + } + else { + const client = this.handle.handle.handle.getClient(); + if (client && client.currentChannel() && client.currentChannel().channelId === this.channel_id) { + this._container_is_private_shown = false; + this._container_is_private.hide(); + } + else { + this._container_is_private.show(); + this._container_is_private_shown = true; + } + } + } + delete_message(message) { + //TODO A lot of checks! + //conversationmessagedelete cid=2 timestamp_begin= timestamp_end= cldbid= limit=1 + this.handle.handle.handle.serverConnection.send_command('conversationmessagedelete', { + cid: this.channel_id, + cldbid: message.sender_database_id, + timestamp_begin: message.timestamp - 1, + timestamp_end: message.timestamp + 1, + limit: 1 + }).then(() => { + return; /* in general it gets deleted via notify */ + }).catch(error => { + log.error(LogCategory.CHAT, _translations.lfcMsCaE || (_translations.lfcMsCaE = tr("Failed to delete conversation message for conversation %o: %o")), this.channel_id, error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.mwRMbLH_ || (_translations.mwRMbLH_ = tr("Failed to delete message")), MessageHelper.formatMessage(_translations.keOq3AeP || (_translations.keOq3AeP = tr("Failed to delete conversation message{:br:}Error: {}")), error)).open(); + }); + log.debug(LogCategory.CLIENT, _translations.MQA1F_tv || (_translations.MQA1F_tv = tr("Deleting text message %o")), message); + } + delete_messages(begin, end, sender, limit) { + let count = 0; + for (const message of this._view_entries.slice()) { + if (!('sender_database_id' in message)) + continue; + const cmsg = message; + if (end != 0 && cmsg.timestamp > end) + continue; + if (begin != 0 && cmsg.timestamp < begin) + break; + if (cmsg.sender_database_id !== sender) + continue; + this._delete_message(message); + if (--count >= limit) + return; + } + //TODO remove in cache? (_last_messages) + } + _delete_message(message) { + if ('html_element' in message) { + const cmessage = message; + cmessage.html_element.remove(); + clearTimeout(cmessage.update_timer); + this._view_entries.remove(message); + } + this._last_messages.remove(message); + } + } + channel.Conversation = Conversation; + class ConversationManager { + constructor(handle) { + this._conversations = []; + this._needed_listener = () => this.update_chat_box(); + this.handle = handle; + this._chat_box = new chat.ChatBox(); + this._build_html_tag(); + this._chat_box.callback_text = text => { + if (!this._current_conversation) + return; + const conv = this._current_conversation; + this.handle.handle.serverConnection.send_command("sendtextmessage", { targetmode: conv.channel_id == 0 ? 3 : 2, cid: conv.channel_id, msg: text }, { process_result: false }).catch(error => { + conv.text_send_failed(error); + }); + }; + this.update_chat_box(); + } + initialize_needed_listener() { + this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); + this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); + } + html_tag() { return this._html_tag; } + destroy() { + if (this.handle.handle.permissions) + this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); + this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); + this._needed_listener = undefined; + this._chat_box && this._chat_box.destroy(); + this._chat_box = undefined; + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._container_conversation = undefined; + for (const conversation of this._conversations) + conversation.destroy(); + this._conversations = []; + this._current_conversation = undefined; + } + update_chat_box() { + let flag = true; + flag = flag && !!this._current_conversation; /* test if we have a conversation */ + flag = flag && !!this.handle.handle.permissions; /* test if we got permissions to test with */ + flag = flag && this.handle.handle.permissions.neededPermission(this._current_conversation.channel_id == 0 ? PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND : PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND).granted(1); + flag = flag && this._current_conversation.chat_available(); + this._chat_box.set_enabled(flag); + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_channel").renderTag({ + chatbox: this._chat_box.html_tag() + }); + this._container_conversation = this._html_tag.find(".container-chat"); + this._chat_box.html_tag().on('focus', event => { + if (this._current_conversation) + this._current_conversation.mark_read(); + }); + this.update_input_format_helper(); + } + set_current_channel(channel_id, update_info_frame) { + if (this._current_conversation && this._current_conversation.channel_id === channel_id) + return; + let conversation = this.conversation(channel_id); + this._current_conversation = conversation; + if (this._current_conversation) { + this._container_conversation.children().detach(); + this._container_conversation.append(conversation.html_tag()); + this._current_conversation.fix_view_size(); + this._current_conversation.fix_scroll(false); + this.update_chat_box(); + } + if (typeof (update_info_frame) === "undefined" || update_info_frame) + this.handle.info_frame().update_channel_text(); + } + current_channel() { return this._current_conversation ? this._current_conversation.channel_id : 0; } + /* Used by notifychanneldeleted */ + delete_conversation(channel_id) { + const entry = this._conversations.find(e => e.channel_id === channel_id); + if (!entry) + return; + this._conversations.remove(entry); + entry.html_tag().detach(); + entry.destroy(); + } + reset() { + while (this._conversations.length > 0) + this.delete_conversation(this._conversations[0].channel_id); + } + conversation(channel_id, create) { + let conversation = this._conversations.find(e => e.channel_id === channel_id); + if (!conversation && channel_id >= 0 && (typeof (create) === "undefined" || create)) { + conversation = new Conversation(this, channel_id); + this._conversations.push(conversation); + conversation.fetch_last_messages(); + } + return conversation; + } + on_show() { + if (this._current_conversation) + this._current_conversation.fix_scroll(false); + } + update_input_format_helper() { + const tag = this._html_tag.find(".container-format-helper"); + if (settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { + tag.removeClass("hidden").text(_translations.RbUV5nYv || (_translations.RbUV5nYv = tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more..."))); + } + else { + tag.addClass("hidden"); + } + } + } + channel.ConversationManager = ConversationManager; + })(channel = chat.channel || (chat.channel = {})); +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["878e3b2427be39ef52c1cc697e3e57ad5bc79eb84ce8ccf6202267edc530f2d7"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["878e3b2427be39ef52c1cc697e3e57ad5bc79eb84ce8ccf6202267edc530f2d7"] = "878e3b2427be39ef52c1cc697e3e57ad5bc79eb84ce8ccf6202267edc530f2d7"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "E8EoU11A", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (269,54)" }, { name: "ZjhaCM4A", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (306,56)" }, { name: "CpuEwfbP", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (310,82)" }, { name: "P9gCZwQr", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (310,127)" }, { name: "cypDJ0_f", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (362,55)" }, { name: "jMSYQz4l", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (373,55)" }, { name: "dD2jftte", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (375,42)" }, { name: "XsVWXGSI", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (375,75)" }, { name: "X6UV2IrX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (392,51)" }, { name: "uGfSbNyd", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (394,38)" }, { name: "RDZR0ORC", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (394,65)" }, { name: "lFZTPJsX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (401,34)" }, { name: "cjVItg91", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (401,56)" }, { name: "NWuLJecu", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (418,55)" }, { name: "zkm5Y7oM", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (421,42)" }, { name: "iM96OH3b", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (421,71)" }, { name: "QxjOjqK0", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (438,51)" }, { name: "JcVfEfgT", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (441,38)" }, { name: "iZ6FfzUr", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (441,67)" }, { name: "gZJMtY87", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (452,50)" }, { name: "qGMVH8CP", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (517,54)" }, { name: "K933Nc0J", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (541,31)" }, { name: "tSljglBN", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (541,93)" }, { name: "h1azsSHK", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (542,57)" }, { name: "YnsxXHDT", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (542,111)" }, { name: "POFVPD93", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (635,59)" }, { name: "TLoVNGxd", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (638,46)" }, { name: "ZsiAh6Et", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (638,76)" }, { name: "hjLbCBgy", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (729,46)" }, { name: "HCGbFx4a", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (745,51)" }, { name: "vjourOhk", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (762,50)" }, { name: "HDVNAONI", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (768,50)" }, { name: "TNiqTN3i", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/music_info.ts (794,23)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var chat; +(function (chat) { + var PlayerState = connection.voice.PlayerState; + class MusicInfo { + constructor(handle) { + this.update_song_info = 0; /* timestamp when we force update the info */ + this.time_select = { active: false, current_select_time: 0, max_time: 0, current_player_time: 0 }; + this.song_reorder = { active: false, song_id: 0, previous_entry: 0, html: undefined, indicator: $.spawn("div").addClass("reorder-indicator") }; + this._playlist_subscribed = false; + this.events = new events.Registry(); + this.handle = handle; + this.events.enable_debug("music-info"); + this.initialize_listener(); + this._build_html_tag(); + this.set_current_bot(undefined, true); + } + html_tag() { + return this._html_tag; + } + destroy() { + this.set_current_bot(undefined); + this.events.destory(); + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._current_bot = undefined; + this.previous_frame_content = undefined; + } + format_time(value) { + if (value == 0) + return "--:--:--"; + value /= 1000; + let hours = 0, minutes = 0; + while (value >= 60 * 60) { + hours++; + value -= 60 * 60; + } + while (value >= 60) { + minutes++; + value -= 60; + } + return ("0" + hours).substr(-2) + ":" + ("0" + minutes).substr(-2) + ":" + ("0" + value.toFixed(0)).substr(-2); + } + ; + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_music_info").renderTag(); + this._container_playlist = this._html_tag.find(".container-playlist"); + this._html_tag.find(".button-close").on('click', () => { + if (this.previous_frame_content === chat.FrameContent.CLIENT_INFO) + this.previous_frame_content = chat.FrameContent.NONE; + this.handle.set_content(this.previous_frame_content); + }); + this._html_tag.find(".button-reload-playlist").on('click', () => this.events.fire("action_playlist_reload")); + this._html_tag.find(".button-song-add").on('click', () => this.events.fire("action_song_add")); + this._html_tag.find(".thumbnail").on('click', event => { + const image = this._html_tag.find(".thumbnail img"); + const url = image.attr("x-thumbnail-url"); + if (!url) + return; + image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); + }); + { + const button_play = this._html_tag.find(".control-buttons .button-play"); + const button_pause = this._html_tag.find(".control-buttons .button-pause"); + button_play.on('click', () => this.events.fire("action_play")); + button_pause.on('click', () => this.events.fire("action_pause")); + this.events.on(["bot_change", "bot_property_update"], event => { + if (event.type === "bot_property_update" && event.as().properties.indexOf("player_state") == -1) + return; + button_play.toggleClass("hidden", this._current_bot === undefined || this._current_bot.properties.player_state < PlayerState.STOPPING); + }); + this.events.on(["bot_change", "bot_property_update"], event => { + if (event.type === "bot_property_update" && event.as().properties.indexOf("player_state") == -1) + return; + button_pause.toggleClass("hidden", this._current_bot !== undefined && this._current_bot.properties.player_state >= PlayerState.STOPPING); + }); + this._html_tag.find(".control-buttons .button-rewind").on('click', () => this.events.fire("action_rewind")); + this._html_tag.find(".control-buttons .button-forward").on('click', () => this.events.fire("action_forward")); + } + /* timeline updaters */ + { + const container = this._html_tag.find(".container-timeline"); + const timeline = container.find(".timeline"); + const indicator_playtime = container.find(".indicator-playtime"); + const indicator_buffered = container.find(".indicator-buffered"); + const thumb = container.find(".thumb"); + const timestamp_current = container.find(".timestamps .current"); + const timestamp_max = container.find(".timestamps .max"); + thumb.on('mousedown', event => event.button === 0 && this.events.fire("playtime_move_begin")); + this.events.on(["bot_change", "player_song_change", "player_time_update", "playtime_move_end"], event => { + if (!this._current_bot) { + this.time_select.max_time = 0; + indicator_buffered.each((_, e) => { e.style.width = "0%"; }); + indicator_playtime.each((_, e) => { e.style.width = "0%"; }); + thumb.each((_, e) => { e.style.marginLeft = "0%"; }); + timestamp_current.text("--:--:--"); + timestamp_max.text("--:--:--"); + return; + } + if (event.type === "playtime_move_end" && !event.as().canceled) + return; + const update_info = Date.now() > this.update_song_info; + this._current_bot.requestPlayerInfo(update_info ? 1000 : 60 * 1000).then(data => { + if (update_info) + this.display_song_info(data); + let played, buffered; + if (event.type !== "player_time_update") { + played = data.player_replay_index; + buffered = data.player_buffered_index; + } + else { + played = event.as().player_replay_index; + buffered = event.as().player_buffered_index; + } + this.time_select.current_player_time = played; + this.time_select.max_time = data.player_max_index; + timestamp_max.text(data.player_max_index ? this.format_time(data.player_max_index) : "--:--:--"); + if (this.time_select.active) + return; + let wplayed, wbuffered; + if (data.player_max_index) { + wplayed = (played * 100 / data.player_max_index).toFixed(2) + "%"; + wbuffered = (buffered * 100 / data.player_max_index).toFixed(2) + "%"; + timestamp_current.text(this.format_time(played)); + } + else { + wplayed = "100%"; + wbuffered = "100%"; + timestamp_current.text(this.format_time(played)); + } + indicator_buffered.each((_, e) => { e.style.width = wbuffered; }); + indicator_playtime.each((_, e) => { e.style.width = wplayed; }); + thumb.each((_, e) => { e.style.marginLeft = wplayed; }); + }); + }); + const move_callback = (event) => { + const x_min = timeline.offset().left; + const x_max = x_min + timeline.width(); + let current = event.pageX; + if (current < x_min) + current = x_min; + else if (current > x_max) + current = x_max; + const percent = (current - x_min) / (x_max - x_min); + this.time_select.current_select_time = percent * this.time_select.max_time; + timestamp_current.text(this.format_time(this.time_select.current_select_time)); + const w = (percent * 100).toFixed(2) + "%"; + indicator_playtime.each((_, e) => { e.style.width = w; }); + thumb.each((_, e) => { e.style.marginLeft = w; }); + }; + const up_callback = (event) => { + if (event.type === "mouseup") + if (event.button !== 0) + return; + this.events.fire("playtime_move_end", { + canceled: event.type !== "mouseup", + target_time: this.time_select.current_select_time + }); + }; + this.events.on("playtime_move_begin", event => { + if (this.time_select.max_time <= 0) + return; + this.time_select.active = true; + indicator_buffered.each((_, e) => { e.style.width = "0"; }); + document.addEventListener("mousemove", move_callback); + document.addEventListener("mouseleave", up_callback); + document.addEventListener("blur", up_callback); + document.addEventListener("mouseup", up_callback); + document.body.style.userSelect = "none"; + }); + this.events.on(["bot_change", "player_song_change", "playtime_move_end"], event => { + document.removeEventListener("mousemove", move_callback); + document.removeEventListener("mouseleave", up_callback); + document.removeEventListener("blur", up_callback); + document.removeEventListener("mouseup", up_callback); + document.body.style.userSelect = undefined; + this.time_select.active = false; + if (event.type === "playtime_move_end") { + const data = event.as(); + if (data.canceled) + return; + const offset = data.target_time - this.time_select.current_player_time; + this.events.fire(offset > 0 ? "action_forward_ms" : "action_rewind_ms", { units: Math.abs(offset) }); + } + }); + } + /* song info handlers */ + this.events.on(["bot_change", "player_song_change"], event => { + let song; + /* update the player info so we dont get old data */ + if (this._current_bot) { + this.update_song_info = 0; + this._current_bot.requestPlayerInfo(1000).then(data => { + this.display_song_info(data); + }).catch(error => { + log.warn(LogCategory.CLIENT, _translations.E8EoU11A || (_translations.E8EoU11A = tr("Failed to update current song for side bar: %o")), error); + }); + } + if (event.type === "bot_change") { + song = undefined; + } + else { + song = event.as().song; + } + this.display_song_info(song); + }); + } + display_song_info(song) { + if (song) { + if (!song.song_loaded) { + console.log("Awaiting a loaded song info."); + this.update_song_info = 0; + } + else { + console.log("Song info loaded."); + this.update_song_info = Date.now() + 60 * 1000; + } + } + if (!song) + song = new SongInfo(); + const container_thumbnail = this._html_tag.find(".player .container-thumbnail"); + const container_info = this._html_tag.find(".player .container-song-info"); + container_thumbnail.find("img") + .attr("src", song.song_thumbnail || "img/music/no-thumbnail.png") + .attr("x-thumbnail-url", encodeURIComponent(song.song_thumbnail)) + .css("cursor", song.song_thumbnail ? "pointer" : null); + if (song.song_id) + container_info.find(".song-name").text(song.song_title || song.song_url).attr("title", song.song_title || song.song_url); + else + container_info.find(".song-name").text(_translations.ZjhaCM4A || (_translations.ZjhaCM4A = tr("No song selected"))); + if (song.song_description) { + container_info.find(".song-description").removeClass("hidden").text(song.song_description).attr("title", song.song_description); + } + else { + container_info.find(".song-description").addClass("hidden").text(_translations.CpuEwfbP || (_translations.CpuEwfbP = tr("Song has no description"))).attr("title", _translations.P9gCZwQr || (_translations.P9gCZwQr = tr("Song has no description"))); + } + } + initialize_listener() { + //Must come at first! + this.events.on("player_song_change", event => { + if (!this._current_bot) + return; + this._current_bot.requestPlayerInfo(0); /* enforce an info refresh */ + }); + /* bot property listener */ + const callback_property = event => this.events.fire("bot_property_update", { properties: event.properties }); + const callback_time_update = event => this.events.fire("player_time_update", event); + const callback_song_change = event => this.events.fire("player_song_change", event); + this.events.on("bot_change", event => { + if (event.old) { + event.old.events.off(callback_property); + event.old.events.off(callback_time_update); + event.old.events.off(callback_song_change); + event.old.events.disconnect_all(this.events); + } + if (event.new) { + event.new.events.on("property_update", callback_property); + event.new.events.on("music_status_update", callback_time_update); + event.new.events.on("music_song_change", callback_song_change); + event.new.events.connect("playlist_song_add", this.events); + event.new.events.connect("playlist_song_remove", this.events); + event.new.events.connect("playlist_song_reorder", this.events); + event.new.events.connect("playlist_song_loaded", this.events); + } + }); + /* basic player actions */ + { + const action_map = { + "action_play": 1, + "action_pause": 2, + "action_forward": 3, + "action_rewind": 4, + "action_forward_ms": 5, + "action_rewind_ms": 6 + }; + this.events.on(Object.keys(action_map), event => { + if (!this._current_bot) + return; + const action_id = action_map[event.type]; + if (typeof action_id === "undefined") { + log.warn(LogCategory.GENERAL, _translations.cypDJ0_f || (_translations.cypDJ0_f = tr("Invalid music bot action event detected: %s. This should not happen!")), event.type); + return; + } + const data = { + bot_id: this._current_bot.properties.client_database_id, + action: action_id, + units: event.units + }; + this.handle.handle.serverConnection.send_command("musicbotplayeraction", data).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + log.error(LogCategory.CLIENT, _translations.jMSYQz4l || (_translations.jMSYQz4l = tr("Failed to perform action %s on bot: %o")), event.type, error); + //TODO: Better error dialog + createErrorModal(_translations.dD2jftte || (_translations.dD2jftte = tr("Failed to perform action.")), _translations.XsVWXGSI || (_translations.XsVWXGSI = tr("Failed to perform action for music bot."))).open(); + }); + }); + } + this.events.on("action_song_set", event => { + if (!this._current_bot) + return; + const connection = this.handle.handle.serverConnection; + if (!connection || !connection.connected()) + return; + connection.send_command("playlistsongsetcurrent", { + playlist_id: this._current_bot.properties.client_playlist_id, + song_id: event.song_id + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + log.error(LogCategory.CLIENT, _translations.X6UV2IrX || (_translations.X6UV2IrX = tr("Failed to set current song on bot: %o")), event.type, error); + //TODO: Better error dialog + createErrorModal(_translations.uGfSbNyd || (_translations.uGfSbNyd = tr("Failed to set song.")), _translations.RDZR0ORC || (_translations.RDZR0ORC = tr("Failed to set current replaying song."))).open(); + }); + }); + this.events.on("action_song_add", () => { + if (!this._current_bot) + return; + createInputModal(_translations.lFZTPJsX || (_translations.lFZTPJsX = tr("Enter song URL")), _translations.cjVItg91 || (_translations.cjVItg91 = tr("Please enter the target song URL")), text => { + try { + new URL(text); + return true; + } + catch (error) { + return false; + } + }, result => { + if (!result || !this._current_bot) + return; + const connection = this.handle.handle.serverConnection; + connection.send_command("playlistsongadd", { + playlist_id: this._current_bot.properties.client_playlist_id, + url: result + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + log.error(LogCategory.CLIENT, _translations.NWuLJecu || (_translations.NWuLJecu = tr("Failed to add song to bot playlist: %o")), error); + //TODO: Better error description + createErrorModal(_translations.zkm5Y7oM || (_translations.zkm5Y7oM = tr("Failed to insert song")), _translations.iM96OH3b || (_translations.iM96OH3b = tr("Failed to append song to the playlist."))).open(); + }); + }).open(); + }); + this.events.on("action_song_delete", event => { + if (!this._current_bot) + return; + const connection = this.handle.handle.serverConnection; + if (!connection || !connection.connected()) + return; + connection.send_command("playlistsongremove", { + playlist_id: this._current_bot.properties.client_playlist_id, + song_id: event.song_id + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + log.error(LogCategory.CLIENT, _translations.QxjOjqK0 || (_translations.QxjOjqK0 = tr("Failed to delete song from bot playlist: %o")), error); + //TODO: Better error description + createErrorModal(_translations.JcVfEfgT || (_translations.JcVfEfgT = tr("Failed to delete song")), _translations.iZ6FfzUr || (_translations.iZ6FfzUr = tr("Failed to remove song from the playlist."))).open(); + }); + }); + /* bot subscription */ + this.events.on("bot_change", () => { + const connection = this.handle.handle.serverConnection; + if (!connection || !connection.connected()) + return; + const bot_id = this._current_bot ? this._current_bot.properties.client_database_id : 0; + this.handle.handle.serverConnection.send_command("musicbotsetsubscription", { bot_id: bot_id }).catch(error => { + log.warn(LogCategory.CLIENT, _translations.gZJMtY87 || (_translations.gZJMtY87 = tr("Failed to subscribe to displayed bot within the side bar: %o")), error); + }); + }); + /* playlist stuff */ + this.events.on(["bot_change", "action_playlist_reload"], event => { + this.playlist_subscribe(true); + this.update_playlist(); + }); + this.events.on("playlist_song_add", event => { + const animation = typeof event.insert_effect === "boolean" ? event.insert_effect : true; + const html_entry = this.build_playlist_entry(event.song, animation); + const playlist = this._container_playlist.find(".playlist"); + const previous = playlist.find(".entry[song-id=" + event.song.song_previous_song_id + "]"); + if (previous.length) + html_entry.insertAfter(previous); + else + html_entry.appendTo(playlist); + if (event.song.song_loaded) + this.events.fire("playlist_song_loaded", { + html_entry: html_entry, + metadata: event.song.song_metadata, + success: true, + song_id: event.song.song_id + }); + if (animation) + setTimeout(() => html_entry.addClass("shown"), 50); + }); + this.events.on("playlist_song_remove", event => { + const playlist = this._container_playlist.find(".playlist"); + const song = playlist.find(".entry[song-id=" + event.song_id + "]"); + song.addClass("deleted"); + setTimeout(() => song.remove(), 5000); /* to play some animations */ + }); + this.events.on("playlist_song_reorder", event => { + const playlist = this._container_playlist.find(".playlist"); + const entry = playlist.find(".entry[song-id=" + event.song_id + "]"); + if (!entry) + return; + console.log(event); + const previous = playlist.find(".entry[song-id=" + event.previous_song_id + "]"); + if (previous.length) { + entry.insertAfter(previous); + } + else { + entry.insertBefore(playlist.find(".entry")[0]); + } + }); + this.events.on("playlist_song_loaded", event => { + const entry = event.html_entry || this._container_playlist.find(".playlist .entry[song-id=" + event.song_id + "]"); + const thumbnail = entry.find(".container-thumbnail img"); + const name = entry.find(".name"); + const description = entry.find(".description"); + const length = entry.find(".length"); + if (event.success) { + let meta; + try { + meta = JSON.parse(event.metadata); + } + catch (error) { + log.warn(LogCategory.CLIENT, _translations.qGMVH8CP || (_translations.qGMVH8CP = tr("Failed to decode song metadata"))); + meta = { + description: "", + title: "", + metadata: {}, + length: 0, + url: entry.attr("song-url") + }; + } + if (!meta.title && meta.description) { + meta.title = meta.description.split("\n")[0]; + meta.description = meta.description.split("\n").slice(1).join("\n"); + } + meta.title = meta.title || meta.url; + name.text(meta.title); + description.text(meta.description); + length.text(this.format_time(meta.length || 0)); + if (meta.thumbnail) { + thumbnail.attr("src", meta.thumbnail) + .attr("x-thumbnail-url", encodeURIComponent(meta.thumbnail)); + } + } + else { + name.text((_translations.K933Nc0J || (_translations.K933Nc0J = tr("failed to load "))) + entry.attr("song-url")).attr("title", (_translations.tSljglBN || (_translations.tSljglBN = tr("failed to load "))) + entry.attr("song-url")); + description.text(event.error_msg || (_translations.h1azsSHK || (_translations.h1azsSHK = tr("unknown error")))).attr("title", event.error_msg || (_translations.YnsxXHDT || (_translations.YnsxXHDT = tr("unknown error")))); + } + }); + /* song reorder */ + { + const move_callback = (event) => { + if (!this.song_reorder.html) + return; + this.song_reorder.html.each((_, e) => { + e.style.left = (event.pageX - this.song_reorder.mouse.x) + "px"; + e.style.top = (event.pageY - this.song_reorder.mouse.y) + "px"; + }); + const entries = this._container_playlist.find(".playlist .entry"); + let before; + for (const entry of entries) { + const off = $(entry).offset().top; + if (off > event.pageY) { + this.song_reorder.indicator.insertBefore(entry); + this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; + return; + } + before = entry; + } + this.song_reorder.indicator.insertAfter(entries.last()); + this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; + }; + const up_callback = (event) => { + if (event.type === "mouseup") + if (event.button !== 0) + return; + this.events.fire("reorder_end", { + canceled: event.type !== "mouseup", + song_id: this.song_reorder.song_id, + entry: this.song_reorder.html, + previous_entry: this.song_reorder.previous_entry + }); + }; + this.events.on("reorder_begin", event => { + this.song_reorder.song_id = event.song_id; + this.song_reorder.html = event.entry; + const width = this.song_reorder.html.width() + "px"; + this.song_reorder.html.each((_, e) => { e.style.width = width; }); + this.song_reorder.active = true; + this.song_reorder.html.addClass("reordering"); + document.addEventListener("mousemove", move_callback); + document.addEventListener("mouseleave", up_callback); + document.addEventListener("blur", up_callback); + document.addEventListener("mouseup", up_callback); + document.body.style.userSelect = "none"; + }); + this.events.on(["bot_change", "playlist_song_remove", "reorder_end"], event => { + if (event.type === "playlist_song_remove" && event.as().song_id !== this.song_reorder.song_id) + return; + document.removeEventListener("mousemove", move_callback); + document.removeEventListener("mouseleave", up_callback); + document.removeEventListener("blur", up_callback); + document.removeEventListener("mouseup", up_callback); + document.body.style.userSelect = undefined; + this.song_reorder.active = false; + this.song_reorder.indicator.remove(); + if (this.song_reorder.html) { + this.song_reorder.html.each((_, e) => { + e.style.width = null; + e.style.left = null; + e.style.top = null; + }); + this.song_reorder.html.removeClass("reordering"); + } + if (event.type === "reorder_end") { + const data = event.as(); + if (data.canceled) + return; + const connection = this.handle.handle.serverConnection; + if (!connection || !connection.connected()) + return; + if (!this._current_bot) + return; + connection.send_command("playlistsongreorder", { + playlist_id: this._current_bot.properties.client_playlist_id, + song_id: data.song_id, + song_previous_song_id: data.previous_entry + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + log.error(LogCategory.CLIENT, _translations.POFVPD93 || (_translations.POFVPD93 = tr("Failed to add song to bot playlist: %o")), error); + //TODO: Better error description + createErrorModal(_translations.TLoVNGxd || (_translations.TLoVNGxd = tr("Failed to reorder song")), _translations.ZsiAh6Et || (_translations.ZsiAh6Et = tr("Failed to reorder song within the playlist."))).open(); + }); + console.log("Reorder to %d", data.previous_entry); + } + }); + this.events.on(["bot_change", "player_song_change"], event => { + if (!this._current_bot) { + this._html_tag.find(".playlist .current-song").removeClass("current-song"); + return; + } + this._current_bot.requestPlayerInfo(1000).then(data => { + const song_id = data ? data.song_id : 0; + this._html_tag.find(".playlist .current-song").removeClass("current-song"); + this._html_tag.find(".playlist .entry[song-id=" + song_id + "]").addClass("current-song"); + }); + }); + } + } + set_current_bot(client, enforce) { + if (client) + client.updateClientVariables(); /* just to ensure */ + if (client === this._current_bot && (typeof (enforce) === "undefined" || !enforce)) + return; + const old = this._current_bot; + this._current_bot = client; + this.events.fire("bot_change", { + new: client, + old: old + }); + } + current_bot() { + return this._current_bot; + } + sort_songs(data) { + const result = []; + let appendable = []; + for (const song of data) { + if (song.song_id == 0 || data.findIndex(e => e.song_id === song.song_previous_song_id) == -1) + result.push(song); + else + appendable.push(song); + } + let iters; + while (appendable.length) { + do { + iters = 0; + const left = []; + for (const song of appendable) { + const index = data.findIndex(e => e.song_id === song.song_previous_song_id); + if (index == -1) { + left.push(song); + continue; + } + result.splice(index + 1, 0, song); + iters++; + } + appendable = left; + } while (iters > 0); + if (appendable.length) + result.push(appendable.pop_front()); + } + return result; + } + /* playlist stuff */ + update_playlist() { + this.playlist_subscribe(true); + this._container_playlist.find(".overlay").toggleClass("hidden", true); + const playlist = this._container_playlist.find(".playlist"); + playlist.empty(); + if (!this.handle.handle.serverConnection || !this.handle.handle.serverConnection.connected() || !this._current_bot) { + this._container_playlist.find(".overlay-empty").removeClass("hidden"); + return; + } + const overlay_loading = this._container_playlist.find(".overlay-loading"); + overlay_loading.removeClass("hidden"); + this._current_bot.updateClientVariables(true).catch(error => { + log.warn(LogCategory.CLIENT, _translations.hjLbCBgy || (_translations.hjLbCBgy = tr("Failed to update music bot variables: %o")), error); + }).then(() => { + this.handle.handle.serverConnection.command_helper.request_playlist_songs(this._current_bot.properties.client_playlist_id).then(songs => { + this.playlist_subscribe(false); /* we're allowed to see the playlist */ + if (!songs) { + this._container_playlist.find(".overlay-empty").removeClass("hidden"); + return; + } + for (const song of this.sort_songs(songs)) + this.events.fire("playlist_song_add", { song: song, insert_effect: false }); + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { + this._container_playlist.find(".overlay-no-permissions").removeClass("hidden"); + return; + } + log.error(LogCategory.CLIENT, _translations.HCGbFx4a || (_translations.HCGbFx4a = tr("Failed to load bot playlist: %o")), error); + this._container_playlist.find(".overlay.overlay-error").removeClass("hidden"); + }).then(() => { + overlay_loading.addClass("hidden"); + }); + }); + } + playlist_subscribe(unsubscribe) { + if (!this.handle.handle.serverConnection) + return; + if (unsubscribe || !this._current_bot) { + if (!this._playlist_subscribed) + return; + this._playlist_subscribed = false; + this.handle.handle.serverConnection.send_command("playlistsetsubscription", { playlist_id: 0 }).catch(error => { + log.warn(LogCategory.CLIENT, _translations.vjourOhk || (_translations.vjourOhk = tr("Failed to unsubscribe from last playlist: %o")), error); + }); + } + else { + this.handle.handle.serverConnection.send_command("playlistsetsubscription", { + playlist_id: this._current_bot.properties.client_playlist_id + }).then(() => this._playlist_subscribed = true).catch(error => { + log.warn(LogCategory.CLIENT, _translations.HDVNAONI || (_translations.HDVNAONI = tr("Failed to subscribe to bots playlist: %o")), error); + }); + } + } + build_playlist_entry(data, insert_effect) { + const tag = $("#tmpl_frame_music_playlist_entry").renderTag(); + tag.attr({ + "song-id": data.song_id, + "song-url": data.song_url + }); + const thumbnail = tag.find(".container-thumbnail img"); + const name = tag.find(".name"); + const description = tag.find(".description"); + const length = tag.find(".length"); + tag.find(".button-delete").on('click', () => this.events.fire("action_song_delete", { song_id: data.song_id })); + tag.find(".container-thumbnail").on('click', event => { + const target = tag.find(".container-thumbnail img"); + const url = target.attr("x-thumbnail-url"); + if (!url) + return; + image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); + }); + tag.on('dblclick', event => this.events.fire("action_song_set", { song_id: data.song_id })); + name.text(_translations.TNiqTN3i || (_translations.TNiqTN3i = tr("loading..."))); + description.text(data.song_url); + tag.on('mousedown', event => { + if (event.button !== 0) + return; + this.song_reorder.mouse = { + x: event.pageX, + y: event.pageY + }; + const baseOff = tag.offset(); + const off = { x: event.pageX - baseOff.left, y: event.pageY - baseOff.top }; + const move_listener = (event) => { + const distance = Math.pow(event.pageX - this.song_reorder.mouse.x, 2) + Math.pow(event.pageY - this.song_reorder.mouse.y, 2); + if (distance < 50) + return; + document.removeEventListener("blur", up_listener); + document.removeEventListener("mouseup", up_listener); + document.removeEventListener("mousemove", move_listener); + this.song_reorder.mouse = off; + this.events.fire("reorder_begin", { + entry: tag, + song_id: data.song_id + }); + }; + const up_listener = event => { + if (event.type === "mouseup" && event.button !== 0) + return; + document.removeEventListener("blur", up_listener); + document.removeEventListener("mouseup", up_listener); + document.removeEventListener("mousemove", move_listener); + }; + document.addEventListener("blur", up_listener); + document.addEventListener("mouseup", up_listener); + document.addEventListener("mousemove", move_listener); + }); + if (this._current_bot) { + this._current_bot.requestPlayerInfo(60 * 1000).then(pdata => { + if (pdata.song_id === data.song_id) + tag.addClass("current-song"); + }); + } + if (insert_effect) { + tag.removeClass("shown"); + tag.addClass("animation"); + } + return tag; + } + } + chat.MusicInfo = MusicInfo; +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["52993fa72b08c5cb4366d0fab99788e3d4126eddfe5dfaa34a89c3679daa82be"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["52993fa72b08c5cb4366d0fab99788e3d4126eddfe5dfaa34a89c3679daa82be"] = "52993fa72b08c5cb4366d0fab99788e3d4126eddfe5dfaa34a89c3679daa82be"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "pRHnlG22", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (109,44)" }, { name: "U6OPHjhG", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (116,44)" }, { name: "IQ73bGpZ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (302,65)" }, { name: "vAvu9aXX", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (308,65)" }, { name: "Usc8Z0dA", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (417,70)" }, { name: "usbbbJmF", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (419,70)" }, { name: "YB1tzr5O", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (436,78)" }, { name: "zIQR20pQ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (438,78)" }, { name: "wcP0LRC8", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (451,61)" }, { name: "bhESUduw", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (489,28)" }, { name: "wUufMAi5", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (531,85)" }, { name: "OEugjVYr", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (558,27)" }, { name: "rQyF4uB3", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (560,87)" }, { name: "aQ79wdKI", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (560,129)" }, { name: "YpJr2VKc", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (562,27)" }, { name: "kY_eo4IV", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (564,27)" }, { name: "qCa4M1e6", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (624,44)" }, { name: "a3TO9oPJ", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (683,48)" }, { name: "oXV6BFdj", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (691,48)" }, { name: "P7dP7UKz", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (782,51)" }, { name: "HQmGkDk1", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (791,51)" }, { name: "XZu8l5EV", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (794,47)" }, { name: "WTCSSX6D", path: "D:/TeaSpeak/web/shared/js/ui/frames/side/private_conversations.ts (890,48)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/* the bar on the right with the chats (Channel & Client) */ +var chat; +(function (chat) { + let PrivateConversationState; + (function (PrivateConversationState) { + PrivateConversationState[PrivateConversationState["OPEN"] = 0] = "OPEN"; + PrivateConversationState[PrivateConversationState["CLOSED"] = 1] = "CLOSED"; + PrivateConversationState[PrivateConversationState["DISCONNECTED"] = 2] = "DISCONNECTED"; + PrivateConversationState[PrivateConversationState["DISCONNECTED_SELF"] = 3] = "DISCONNECTED_SELF"; + })(PrivateConversationState = chat.PrivateConversationState || (chat.PrivateConversationState = {})); + class PrivateConveration { + constructor(handle, client_unique_id, client_name, client_id) { + this._message_history = []; + this._last_typing = 0; + this._typing_timeout = 4000; + this._displayed_messages = []; + this._displayed_messages_length = 500; + this.handle = handle; + this.client_name = client_name; + this.client_unique_id = client_unique_id; + this.client_id = client_id; + this._state = PrivateConversationState.OPEN; + this._build_entry_tag(); + this.set_unread_flag(false); + this.load_history(); + } + history_key() { return this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier + "_" + this.client_unique_id; } + load_history() { + chat.helpers.history.load_history(this.history_key()).then((data) => { + if (!data) + return; + const flag_unread = !!this._spacer_unread_message; + for (const message of data.slice(data.length > this._displayed_messages_length ? data.length - this._displayed_messages_length : 0)) { + this.append_message(message.message, { + type: message.sender, + name: message.sender_name, + unique_id: message.sender_unique_id, + client_id: message.sender_client_id + }, new Date(message.timestamp), false); + } + if (!flag_unread) + this.set_unread_flag(false); + this.fix_scroll(false); + this.save_history(); + }).catch(error => { + log.warn(LogCategory.CHAT, _translations.pRHnlG22 || (_translations.pRHnlG22 = tr("Failed to load private conversation history for user %s on server %s: %o")), this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); + }); + } + save_history() { + chat.helpers.history.save_history(this.history_key(), this._message_history).catch(error => { + log.warn(LogCategory.CHAT, _translations.U6OPHjhG || (_translations.U6OPHjhG = tr("Failed to save private conversation history for user %s on server %s: %o")), this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); + }); + } + entry_tag() { + return this._html_entry_tag; + } + destroy() { + this._html_message_container = undefined; /* we do not own this container */ + this.clear_messages(false); + this._html_entry_tag && this._html_entry_tag.remove(); + this._html_entry_tag = undefined; + this._message_history = undefined; + if (this._typing_timeout_task) + clearTimeout(this._typing_timeout_task); + } + _2d_flat(array) { + const result = []; + for (const a of array) + result.push(...a.filter(e => typeof (e) !== "undefined")); + return result; + } + messages_tags() { + return this._2d_flat(this._displayed_messages.slice().reverse().map(e => [ + e.tag_timepointer ? e.tag_timepointer.html_tag : undefined, + e.tag_unread ? e.tag_unread.html_tag : undefined, + e.tag_message + ])); + } + append_message(message, sender, timestamp, save_history) { + const message_date = timestamp || new Date(); + const message_timestamp = message_date.getTime(); + const packed_message = { + message: message, + sender: sender.type, + sender_name: sender.name, + sender_client_id: sender.client_id, + sender_unique_id: sender.unique_id, + timestamp: message_date.getTime(), + message_id: 'undefined' + }; + /* first of all register message in message history */ + { + let index = 0; + for (; index < this._message_history.length; index++) { + if (this._message_history[index].timestamp > message_timestamp) + continue; + this._message_history.splice(index, 0, packed_message); + break; + } + if (index > 100) + return; /* message is too old to be displayed */ + if (index >= this._message_history.length) + this._message_history.push(packed_message); + while (this._message_history.length > 100) + this._message_history.pop(); + } + if (sender.type === "partner") { + clearTimeout(this._typing_timeout_task); + this._typing_timeout_task = 0; + if (this.typing_active()) { + this._last_typing = 0; + this.typing_expired(); + } + else { + this._update_message_timestamp(); + } + } + else { + this._update_message_timestamp(); + } + if (typeof (save_history) !== "boolean" || save_history) + this.save_history(); + /* insert in view */ + { + const basic_view_entry = this._build_message(packed_message); + this._register_displayed_message({ + timestamp: basic_view_entry.timestamp, + message: basic_view_entry, + message_type: "message", + tag_message: basic_view_entry.html_tag, + tag_timepointer: undefined, + tag_unread: undefined + }, true); + } + } + _displayed_message_first_tag(message) { + const tp = message.tag_timepointer ? message.tag_timepointer.html_tag : undefined; + const tu = message.tag_unread ? message.tag_unread.html_tag : undefined; + return tp || tu || message.tag_message; + } + _destroy_displayed_message(message, update_pointers) { + if (update_pointers) { + const index = this._displayed_messages.indexOf(message); + if (index != -1 && index > 0) { + const next = this._displayed_messages[index - 1]; + if (!next.tag_timepointer && message.tag_timepointer) { + next.tag_timepointer = message.tag_timepointer; + message.tag_timepointer = undefined; + } + if (!next.tag_unread && message.tag_unread) { + this._spacer_unread_message = next; + next.tag_unread = message.tag_unread; + message.tag_unread = undefined; + } + } + if (message == this._spacer_unread_message) + this._spacer_unread_message = undefined; + } + this._displayed_messages.remove(message); + if (message.tag_timepointer) + this._destroy_view_entry(message.tag_timepointer); + if (message.tag_unread) + this._destroy_view_entry(message.tag_unread); + this._destroy_view_entry(message.message); + } + clear_messages(save) { + this._message_history = []; + while (this._displayed_messages.length > 0) { + this._destroy_displayed_message(this._displayed_messages[0], false); + } + this._spacer_unread_message = undefined; + this._update_message_timestamp(); + if (save) + this.save_history(); + } + fix_scroll(animate) { + if (!this._html_message_container) + return; + let offset; + if (this._spacer_unread_message) { + offset = this._displayed_message_first_tag(this._spacer_unread_message)[0].offsetTop; + } + else if (typeof (this._scroll_position) !== "undefined") { + offset = this._scroll_position; + } + else { + offset = this._html_message_container[0].scrollHeight; + } + if (animate) { + this._html_message_container.stop(true).animate({ + scrollTop: offset + }, 'slow'); + } + else { + this._html_message_container.stop(true).scrollTop(offset); + } + } + _update_message_timestamp() { + if (this._last_message_updater_id) + clearTimeout(this._last_message_updater_id); + if (!this._html_entry_tag) + return; /* we got deleted, not need for updates */ + if (this.typing_active()) { + this._html_entry_tag.find(".last-message").text(_translations.IQ73bGpZ || (_translations.IQ73bGpZ = tr("currently typing..."))); + return; + } + const last_message = this._message_history[0]; + if (!last_message) { + this._html_entry_tag.find(".last-message").text(_translations.vAvu9aXX || (_translations.vAvu9aXX = tr("no history"))); + return; + } + const timestamp = new Date(last_message.timestamp); + let time = chat.format.date.format_chat_time(timestamp); + this._html_entry_tag.find(".last-message").text(time.result); + if (time.next_update > 0) { + this._last_message_updater_id = setTimeout(() => this._update_message_timestamp(), time.next_update); + } + else { + this._last_message_updater_id = 0; + } + } + _destroy_message(message) { + if (message.time_update_id) + clearTimeout(message.time_update_id); + } + _build_message(message) { + const result = message; + if (result.html_tag) + return result; + const timestamp = new Date(message.timestamp); + let time = chat.format.date.format_chat_time(timestamp); + result.html_tag = $("#tmpl_frame_chat_private_message").renderTag({ + timestamp: time.result, + message_id: message.message_id, + client_name: htmltags.generate_client_object({ + add_braces: false, + client_name: message.sender_name, + client_unique_id: message.sender_unique_id, + client_id: message.sender_client_id + }), + message: MessageHelper.bbcode_chat(message.message), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({ id: message.sender_client_id }, message.sender_unique_id) + }); + if (time.next_update > 0) { + const _updater = () => { + time = chat.format.date.format_chat_time(timestamp); + result.html_tag.find(".info .timestamp").text(time.result); + if (time.next_update > 0) + result.time_update_id = setTimeout(_updater, time.next_update); + else + result.time_update_id = 0; + }; + result.time_update_id = setTimeout(_updater, time.next_update); + } + else { + result.time_update_id = 0; + } + return result; + } + _build_spacer(message, type) { + const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ + message: message + }).addClass("type-" + type); + return { + html_tag: tag + }; + } + _register_displayed_message(message, update_new) { + const message_date = new Date(message.timestamp); + /* before := older message; after := newer message */ + let entry_before, entry_after; + let index = 0; + for (; index < this._displayed_messages.length; index++) { + if (this._displayed_messages[index].timestamp > message.timestamp) + continue; + entry_after = index > 0 ? this._displayed_messages[index - 1] : undefined; + entry_before = this._displayed_messages[index]; + this._displayed_messages.splice(index, 0, message); + break; + } + if (index >= this._displayed_messages_length) { + return; /* message is out of view region */ + } + if (index >= this._displayed_messages.length) { + entry_before = undefined; + entry_after = this._displayed_messages.last(); + this._displayed_messages.push(message); + } + while (this._displayed_messages.length > this._displayed_messages_length) + this._destroy_displayed_message(this._displayed_messages.last(), true); + const flag_new_message = update_new && index == 0 && (message.message_type === "spacer" || message.message.sender === "partner"); + /* Timeline for before - now */ + { + let append_pointer = false; + if (entry_before) { + if (!chat.helpers.date.same_day(message.timestamp, entry_before.timestamp)) { + append_pointer = true; + } + } + else { + append_pointer = true; + } + if (append_pointer) { + const diff = chat.format.date.date_format(message_date, new Date()); + if (diff == chat.format.date.ColloquialFormat.YESTERDAY) + message.tag_timepointer = this._build_spacer(_translations.Usc8Z0dA || (_translations.Usc8Z0dA = tr("Yesterday")), "date"); + else if (diff == chat.format.date.ColloquialFormat.TODAY) + message.tag_timepointer = this._build_spacer(_translations.usbbbJmF || (_translations.usbbbJmF = tr("Today")), "date"); + else if (diff == chat.format.date.ColloquialFormat.GENERAL) + message.tag_timepointer = this._build_spacer(chat.format.date.format_date_general(message_date, false), "date"); + } + } + /* Timeline not and after */ + { + if (entry_after) { + if (chat.helpers.date.same_day(message_date, entry_after.timestamp)) { + if (entry_after.tag_timepointer) { + this._destroy_view_entry(entry_after.tag_timepointer); + entry_after.tag_timepointer = undefined; + } + } + else if (!entry_after.tag_timepointer) { + const diff = chat.format.date.date_format(new Date(entry_after.timestamp), new Date()); + if (diff == chat.format.date.ColloquialFormat.YESTERDAY) + entry_after.tag_timepointer = this._build_spacer(_translations.YB1tzr5O || (_translations.YB1tzr5O = tr("Yesterday")), "date"); + else if (diff == chat.format.date.ColloquialFormat.TODAY) + entry_after.tag_timepointer = this._build_spacer(_translations.zIQR20pQ || (_translations.zIQR20pQ = tr("Today")), "date"); + else if (diff == chat.format.date.ColloquialFormat.GENERAL) + entry_after.tag_timepointer = this._build_spacer(chat.format.date.format_date_general(message_date, false), "date"); + entry_after.tag_timepointer.html_tag.insertBefore(entry_after.tag_message); + } + } + } + /* new message flag */ + if (flag_new_message) { + if (!this._spacer_unread_message) { + this._spacer_unread_message = message; + message.tag_unread = this._build_spacer(_translations.wcP0LRC8 || (_translations.wcP0LRC8 = tr("Unread messages")), "new"); + this.set_unread_flag(true); + } + } + if (this._html_message_container) { + if (entry_before) { + message.tag_message.insertAfter(entry_before.tag_message); + } + else if (entry_after) { + message.tag_message.insertBefore(this._displayed_message_first_tag(entry_after)); + } + else { + this._html_message_container.append(message.tag_message); + } + /* first time pointer */ + if (message.tag_timepointer) + message.tag_timepointer.html_tag.insertBefore(message.tag_message); + /* the unread */ + if (message.tag_unread) + message.tag_unread.html_tag.insertBefore(message.tag_message); + } + this.fix_scroll(true); + } + _destroy_view_entry(entry) { + if (!entry.html_tag) + return; + entry.html_tag.remove(); + if ('sender' in entry) + this._destroy_message(entry); + } + _build_entry_tag() { + this._html_entry_tag = $("#tmpl_frame_chat_private_entry").renderTag({ + client_name: this.client_name, + last_time: _translations.bhESUduw || (_translations.bhESUduw = tr("error no timestamp")), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({ id: this.client_id }, this.client_unique_id) + }); + this._html_entry_tag.on('click', event => { + if (event.isDefaultPrevented()) + return; + this.handle.set_selected_conversation(this); + }); + this._html_entry_tag.find('.button-close').on('click', event => { + event.preventDefault(); + this.close_conversation(); + }); + this._update_message_timestamp(); + } + update_avatar() { + const container = this._html_entry_tag.find(".container-avatar"); + container.find(".avatar").remove(); + container.append(this.handle.handle.handle.fileManager.avatars.generate_chat_tag({ id: this.client_id }, this.client_unique_id)); + } + close_conversation() { + this.handle.delete_conversation(this, true); + } + set_client_name(name) { + if (this.client_name === name) + return; + this.client_name = name; + this._html_entry_tag.find(".client-name").text(name); + } + set_unread_flag(flag, update_chat_counter) { + /* unread message pointer */ + if (flag != (typeof (this._spacer_unread_message) !== "undefined")) { + if (flag) { + if (this._displayed_messages.length > 0) /* without messages we cant be unread */ + return; + if (!this._spacer_unread_message) { + this._spacer_unread_message = this._displayed_messages[0]; + this._spacer_unread_message.tag_unread = this._build_spacer(_translations.wUufMAi5 || (_translations.wUufMAi5 = tr("Unread messages")), "new"); + this._spacer_unread_message.tag_unread.html_tag.insertBefore(this._spacer_unread_message.tag_message); + } + } + else { + const ctree = this.handle.handle.handle.channelTree; + if (ctree && ctree.tag_tree() && this.client_id) + ctree.tag_tree().find(".marker-text-unread[private-conversation='" + this.client_id + "']").addClass("hidden"); + if (this._spacer_unread_message) { + this._destroy_view_entry(this._spacer_unread_message.tag_unread); + this._spacer_unread_message.tag_unread = undefined; + this._spacer_unread_message = undefined; + } + } + } + /* general notify */ + this._html_entry_tag.toggleClass("unread", flag); + if (typeof (update_chat_counter) !== "boolean" || update_chat_counter) + this.handle.handle.info_frame().update_chat_counter(); + } + is_unread() { return !!this._spacer_unread_message; } + _append_state_change(state) { + let message; + if (state == "closed") + message = _translations.OEugjVYr || (_translations.OEugjVYr = tr("Your chat partner has closed the conversation")); + else if (state == "reconnect") + message = this._state === PrivateConversationState.DISCONNECTED_SELF ? _translations.rQyF4uB3 || (_translations.rQyF4uB3 = tr("You've reconnected to the server")) : _translations.aQ79wdKI || (_translations.aQ79wdKI = tr("Your chat partner has reconnected")); + else if (state === "disconnect") + message = _translations.YpJr2VKc || (_translations.YpJr2VKc = tr("Your chat partner has disconnected")); + else + message = _translations.kY_eo4IV || (_translations.kY_eo4IV = tr("You've disconnected from the server")); + const spacer = this._build_spacer(message, state); + this._register_displayed_message({ + timestamp: Date.now(), + message: spacer, + message_type: "spacer", + tag_message: spacer.html_tag, + tag_timepointer: undefined, + tag_unread: undefined + }, state === "disconnect"); + } + state() { + return this._state; + } + set_state(state) { + if (this._state == state) + return; + if (state == PrivateConversationState.DISCONNECTED) { + this._append_state_change("disconnect"); + this.client_id = 0; + } + else if (state == PrivateConversationState.OPEN && this._state != PrivateConversationState.CLOSED) + this._append_state_change("reconnect"); + else if (state == PrivateConversationState.CLOSED) + this._append_state_change("closed"); + else if (state == PrivateConversationState.DISCONNECTED_SELF) + this._append_state_change("disconnect_self"); + this._state = state; + } + set_text_callback(callback, update_enabled_state) { + this._callback_message = callback; + if (typeof (update_enabled_state) !== "boolean" || update_enabled_state) + this.handle.update_chatbox_state(); + } + chat_enabled() { + return typeof (this._callback_message) !== "undefined" && (this._state == PrivateConversationState.OPEN || this._state == PrivateConversationState.CLOSED); + } + append_error(message, date) { + const spacer = this._build_spacer(message, "error"); + this._register_displayed_message({ + timestamp: date || Date.now(), + message: spacer, + message_type: "spacer", + tag_message: spacer.html_tag, + tag_timepointer: undefined, + tag_unread: undefined + }, true); + } + call_message(message) { + if (this._callback_message) + this._callback_message(message); + else { + log.warn(LogCategory.CHAT, _translations.qCa4M1e6 || (_translations.qCa4M1e6 = tr("Dropping conversation message for client %o because of no message callback.")), { + client_name: this.client_name, + client_id: this.client_id, + client_unique_id: this.client_unique_id + }); + } + } + typing_expired() { + this._update_message_timestamp(); + if (this.handle.current_conversation() === this) + this.handle.update_typing_state(); + } + trigger_typing() { + let _new = Date.now() - this._last_typing > this._typing_timeout; + this._last_typing = Date.now(); + if (this._typing_timeout_task) + clearTimeout(this._typing_timeout_task); + if (_new) + this._update_message_timestamp(); + if (this.handle.current_conversation() === this) + this.handle.update_typing_state(); + this._typing_timeout_task = setTimeout(() => this.typing_expired(), this._typing_timeout); + } + typing_active() { + return Date.now() - this._last_typing < this._typing_timeout; + } + } + chat.PrivateConveration = PrivateConveration; + class PrivateConverations { + constructor(handle) { + this._conversations = []; + this._current_conversation = undefined; + this.handle = handle; + this._chat_box = new chat.ChatBox(); + this._build_html_tag(); + this.update_chatbox_state(); + this.update_typing_state(); + this._chat_box.callback_text = message => { + if (!this._current_conversation) { + log.warn(LogCategory.CHAT, _translations.a3TO9oPJ || (_translations.a3TO9oPJ = tr("Dropping conversation message because of no active conversation."))); + return; + } + this._current_conversation.call_message(message); + }; + this._chat_box.callback_typing = () => { + if (!this._current_conversation) { + log.warn(LogCategory.CHAT, _translations.oXV6BFdj || (_translations.oXV6BFdj = tr("Dropping conversation typing action because of no active conversation."))); + return; + } + const connection = this.handle.handle.serverConnection; + if (!connection || !connection.connected()) + return; + connection.send_command("clientchatcomposing", { + clid: this._current_conversation.client_id + }); + }; + } + clear_client_ids() { + this._conversations.forEach(e => { + e.client_id = 0; + e.set_state(PrivateConversationState.DISCONNECTED_SELF); + }); + } + html_tag() { return this._html_tag; } + destroy() { + this._chat_box && this._chat_box.destroy(); + this._chat_box = undefined; + for (const conversation of this._conversations) + conversation.destroy(); + this._conversations = []; + this._current_conversation = undefined; + clearTimeout(this._select_read_timer); + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + } + current_conversation() { return this._current_conversation; } + conversations() { return this._conversations; } + create_conversation(client_uid, client_name, client_id) { + const conv = new PrivateConveration(this, client_uid, client_name, client_id); + this._conversations.push(conv); + this._html_no_chats.hide(); + this._container_conversation_list.append(conv.entry_tag()); + this.handle.info_frame().update_chat_counter(); + return conv; + } + delete_conversation(conv, update_chat_couner) { + if (!this._conversations.remove(conv)) + return; + //TODO: May animate? + conv.destroy(); + conv.clear_messages(false); + this._html_no_chats.toggle(this._conversations.length == 0); + if (conv === this._current_conversation) + this.set_selected_conversation(undefined); + if (update_chat_couner || typeof (update_chat_couner) !== "boolean") + this.handle.info_frame().update_chat_counter(); + } + find_conversation(partner, mode) { + for (const conversation of this.conversations()) + if (conversation.client_id == partner.client_id && (!partner.unique_id || conversation.client_unique_id == partner.unique_id)) { + if (conversation.state() != PrivateConversationState.OPEN) + conversation.set_state(PrivateConversationState.OPEN); + return conversation; + } + let conv; + if (mode.attach) { + for (const conversation of this.conversations()) + if (conversation.client_unique_id == partner.unique_id && conversation.state() != PrivateConversationState.OPEN) { + conversation.set_state(PrivateConversationState.OPEN); + conversation.client_id = partner.client_id; + conversation.set_client_name(partner.name); + conv = conversation; + break; + } + } + if (mode.create && !conv) { + conv = this.create_conversation(partner.unique_id, partner.name, partner.client_id); + conv.client_id = partner.client_id; + conv.set_client_name(partner.name); + } + if (conv) { + conv.set_text_callback(message => { + log.debug(LogCategory.CLIENT, _translations.P7dP7UKz || (_translations.P7dP7UKz = tr("Sending text message %s to %o")), message, partner); + this.handle.handle.serverConnection.send_command("sendtextmessage", { "targetmode": 1, "target": partner.client_id, "msg": message }).catch(error => { + if (error instanceof CommandResult) { + if (error.id == ErrorID.CLIENT_INVALID_ID) { + conv.set_state(PrivateConversationState.DISCONNECTED); + conv.set_text_callback(undefined); + } + else if (error.id == ErrorID.PERMISSION_ERROR) { + /* may notify for no permissions? */ + } + else { + conv.append_error((_translations.HQmGkDk1 || (_translations.HQmGkDk1 = tr("Failed to send message: "))) + (error.extra_message || error.message)); + } + } + else { + conv.append_error(_translations.XZu8l5EV || (_translations.XZu8l5EV = tr("Failed to send message. Lookup the console for more details"))); + log.error(LogCategory.CHAT, tr("Failed to send conversation message: %o", error)); + } + }); + }); + } + return conv; + } + clear_conversations() { + while (this._conversations.length > 0) + this.delete_conversation(this._conversations[0], false); + this.handle.info_frame().update_chat_counter(); + } + set_selected_conversation(conv) { + if (conv === this._current_conversation) + return; + if (this._select_read_timer) + clearTimeout(this._select_read_timer); + if (this._current_conversation) + this._current_conversation._html_message_container = undefined; + this._container_conversation_list.find(".selected").removeClass("selected"); + this._container_conversation_messages.children().detach(); + this._current_conversation = conv; + if (!this._current_conversation) { + this.update_chatbox_state(); + return; + } + this._current_conversation._html_message_container = this._container_conversation_messages; + const messages = this._current_conversation.messages_tags(); + /* TODO: Check if the messages are empty and display "No messages" */ + this._container_conversation_messages.append(...messages); + if (this._current_conversation.is_unread() && false) { + this._select_read_timer = setTimeout(() => { + this._current_conversation.set_unread_flag(false, true); + }, 20 * 1000); /* Lets guess you've read the new messages within 5 seconds */ + } + this._current_conversation.fix_scroll(false); + this._current_conversation.entry_tag().addClass("selected"); + this.update_chatbox_state(); + } + update_chatbox_state() { + this._chat_box.set_enabled(!!this._current_conversation && this._current_conversation.chat_enabled()); + } + update_typing_state() { + this._container_typing.toggleClass("hidden", !this._current_conversation || !this._current_conversation.typing_active()); + } + _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_private").renderTag({ + chatbox: this._chat_box.html_tag() + }).dividerfy(); + this._container_conversation = this._html_tag.find(".conversation"); + this._container_conversation.on('click', event => { + if (this._current_conversation) + this._current_conversation.set_unread_flag(false, true); /* only updates everything if the state changes */ + }); + this._container_conversation_messages = this._container_conversation.find(".messages"); + this._container_conversation_messages.on('scroll', event => { + if (!this._current_conversation) + return; + const current_view = this._container_conversation_messages[0].scrollTop + this._container_conversation_messages[0].clientHeight + this._container_conversation_messages[0].clientHeight * .125; + if (current_view > this._container_conversation_messages[0].scrollHeight) + this._current_conversation._scroll_position = undefined; + else + this._current_conversation._scroll_position = this._container_conversation_messages[0].scrollTop; + }); + this._container_conversation_list = this._html_tag.find(".conversation-list"); + this._html_no_chats = this._container_conversation_list.find(".no-chats"); + this._container_typing = this._html_tag.find(".container-typing"); + this.update_input_format_helper(); + } + try_input_focus() { + this._chat_box.focus_input(); + } + on_show() { + if (this._current_conversation) + this._current_conversation.fix_scroll(false); + } + update_input_format_helper() { + const tag = this._html_tag.find(".container-format-helper"); + if (settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { + tag.removeClass("hidden").text(_translations.WTCSSX6D || (_translations.WTCSSX6D = tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more..."))); + } + else { + tag.addClass("hidden"); + } + } + } + chat.PrivateConverations = PrivateConverations; +})(chat || (chat = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["cb1a8cd441d1bbbd9b2737e4c26524694bd2bd35f9024e2a99bd9a12d07cf7b0"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["cb1a8cd441d1bbbd9b2737e4c26524694bd2bd35f9024e2a99bd9a12d07cf7b0"] = "cb1a8cd441d1bbbd9b2737e4c26524694bd2bd35f9024e2a99bd9a12d07cf7b0"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "UvC90shS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAbout.ts (27,21)" }, { name: "_OdnrDIg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAbout.ts (50,48)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function format_date(date) { + const d = new Date(date); + return ('00' + d.getDay()).substr(-2) + "." + ('00' + d.getMonth()).substr(-2) + "." + d.getFullYear() + " - " + ('00' + d.getHours()).substr(-2) + ":" + ('00' + d.getMinutes()).substr(-2); + } + function spawnAbout() { + const app_version = (() => { + const version_node = document.getElementById("app_version"); + if (!version_node) + return undefined; + const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; + if (!version) + return undefined; + if (version == "unknown" || version.replace(/0+/, "").length == 0) + return undefined; + return version; + })(); + const connectModal = createModal({ + header: _translations.UvC90shS || (_translations.UvC90shS = tr("About")), + body: () => { + let tag = $("#tmpl_about").renderTag({ + client: !app.is_web(), + version_client: app.is_web() ? app_version || "in-dev" : "loading...", + version_ui: app_version || "in-dev", + version_timestamp: !!app_version ? format_date(Date.now()) : "--" + }); + return tag; + }, + footer: null, + width: "60em" + }); + connectModal.htmlTag.find(".modal-body").addClass("modal-about"); + connectModal.open(); + if (!app.is_web()) { + window.native.client_version().then(version => { + connectModal.htmlTag.find(".version-client").text(version); + }).catch(error => { + log.error(LogCategory.GENERAL, _translations._OdnrDIg || (_translations._OdnrDIg = tr("Failed to load client version: %o")), error); + connectModal.htmlTag.find(".version-client").text("unknown"); + }); + } + } + Modals.spawnAbout = spawnAbout; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["ba60e7511140f62e2e02c5860bea99e474c1f09dfb5d657bb59ea95b5ca607cc"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["ba60e7511140f62e2e02c5860bea99e474c1f09dfb5d657bb59ea95b5ca607cc"] = "ba60e7511140f62e2e02c5860bea99e474c1f09dfb5d657bb59ea95b5ca607cc"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "ClTfrLVQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatar.ts (9,21)" }, { name: "bltBa8CM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatar.ts (59,35)" }, { name: "u7xCv0Fo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatar.ts (60,38)" }, { name: "ABQle2XS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatar.ts (65,29)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + //TODO: Test if we could render this image and not only the browser by knowing the type. + function spawnAvatarUpload(callback_data) { + const modal = createModal({ + header: _translations.ClTfrLVQ || (_translations.ClTfrLVQ = tr("Avatar Upload")), + footer: undefined, + body: () => { + return $("#tmpl_avatar_upload").renderTag({}); + } + }); + let _data_submitted = false; + let _current_avatar; + modal.htmlTag.find(".button-select").on('click', event => { + modal.htmlTag.find(".file-inputs").trigger('click'); + }); + modal.htmlTag.find(".button-delete").on('click', () => { + if (_data_submitted) + return; + _data_submitted = true; + modal.close(); + callback_data(null); + }); + modal.htmlTag.find(".button-cancel").on('click', () => modal.close()); + const button_upload = modal.htmlTag.find(".button-upload"); + button_upload.on('click', event => (!_data_submitted) && (_data_submitted = true, modal.close(), true) && callback_data(_current_avatar)); + const set_avatar = (data, type) => { + _current_avatar = data ? arrayBufferBase64(data) : undefined; + button_upload.prop("disabled", !_current_avatar); + modal.htmlTag.find(".preview img").attr("src", data ? ("data:image/" + type + ";base64," + data) : "img/style/avatar.png"); + }; + const input_node = modal.htmlTag.find(".file-inputs")[0]; + input_node.multiple = false; + modal.htmlTag.find(".file-inputs").on('change', event => { + console.log("Files: %o", input_node.files); + const read_file = (file) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = error => reject(error); + reader.readAsDataURL(file); + }); + (() => __awaiter(this, void 0, void 0, function* () { + const data = yield read_file(input_node.files[0]); + if (!data.startsWith("data:image/")) { + console.error(_translations.bltBa8CM || (_translations.bltBa8CM = tr("Failed to load file %s: Invalid data media type (%o)")), input_node.files[0].name, data); + createErrorModal(_translations.u7xCv0Fo || (_translations.u7xCv0Fo = tr("Icon upload failed")), tra("Failed to select avatar {}.
File is not an image", input_node.files[0].name)).open(); + return; + } + const semi = data.indexOf(';'); + const type = data.substring(11, semi); + console.log(_translations.ABQle2XS || (_translations.ABQle2XS = tr("Given image has type %s")), type); + set_avatar(data.substr(semi + 8 /* 8 bytes := base64, */), type); + }))(); + }); + set_avatar(undefined); + modal.close_listener.push(() => !_data_submitted && callback_data(undefined)); + modal.open(); + } + Modals.spawnAvatarUpload = spawnAvatarUpload; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["b6161b8459fe46763e17df58843394182a9583e985503f2e71811b45b30b4ffd"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["b6161b8459fe46763e17df58843394182a9583e985503f2e71811b45b30b4ffd"] = "b6161b8459fe46763e17df58843394182a9583e985503f2e71811b45b30b4ffd"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "WyO_9diE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (25,21)" }, { name: "dDeDPqV3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (57,86)" }, { name: "rxNEagLS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (58,88)" }, { name: "YjHvCku4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (59,84)" }, { name: "JxSfQpqn", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (89,28)" }, { name: "xPENqFxw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (89,49)" }, { name: "WtQT3rEW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (91,42)" }, { name: "YQR30jMM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (91,65)" }, { name: "EAlpgDaJ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (147,52)" }, { name: "_9ZhLutC", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (148,38)" }, { name: "V5Na3lr_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (148,71)" }, { name: "yN5_Hp_F", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (152,48)" }, { name: "jrf39vdv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (153,34)" }, { name: "q3nuUEOC", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalAvatarList.ts (153,64)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + const avatar_to_uid = (id) => { + const buffer = new Uint8Array(id.length / 2); + for (let index = 0; index < id.length; index += 2) { + const upper_nibble = id.charCodeAt(index) - 97; + const lower_nibble = id.charCodeAt(index + 1) - 97; + buffer[index / 2] = (upper_nibble << 4) | lower_nibble; + } + return base64_encode_ab(buffer); + }; + Modals.human_file_size = (size) => { + if (size < 1000) + return size + "B"; + const exp = Math.floor(Math.log2(size) / 10); + return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB"; + }; + function spawnAvatarList(client) { + const modal = createModal({ + header: _translations.WyO_9diE || (_translations.WyO_9diE = tr("Avatars")), + footer: undefined, + body: () => { + const template = $("#tmpl_avatar_list").renderTag({}); + return template; + } + }); + let callback_download; + let callback_delete; + const button_download = modal.htmlTag.find(".button-download"); + const button_delete = modal.htmlTag.find(".button-delete"); + const container_list = modal.htmlTag.find(".container-list .list-entries-container"); + const list_entries = container_list.find(".list-entries"); + const container_info = modal.htmlTag.find(".container-info"); + const overlay_no_user = container_info.find(".disabled-overlay").show(); + const set_selected_avatar = (unique_id, avatar_id, size) => { + button_download.prop("disabled", true); + callback_download = undefined; + if (!unique_id) { + overlay_no_user.show(); + return; + } + const tag_username = container_info.find(".property-username"); + const tag_unique_id = container_info.find(".property-unique-id"); + const tag_avatar_id = container_info.find(".property-avatar-id"); + const container_avatar = container_info.find(".container-image"); + const tag_image_bytes = container_info.find(".property-image-size"); + const tag_image_width = container_info.find(".property-image-width").val(_translations.dDeDPqV3 || (_translations.dDeDPqV3 = tr("loading..."))); + const tag_image_height = container_info.find(".property-image-height").val(_translations.rxNEagLS || (_translations.rxNEagLS = tr("loading..."))); + const tag_image_type = container_info.find(".property-image-type").val(_translations.YjHvCku4 || (_translations.YjHvCku4 = tr("loading..."))); + tag_username.val("unknown"); + tag_unique_id.val(unique_id); + tag_avatar_id.val(avatar_id); + tag_image_bytes.val(size); + container_avatar.empty().append(client.fileManager.avatars.generate_tag(avatar_id, undefined, { + callback_image: image => { + tag_image_width.val(image[0].naturalWidth + 'px'); + tag_image_height.val(image[0].naturalHeight + 'px'); + }, + callback_avatar: avatar => { + tag_image_type.val(media_image_type(avatar.type)); + button_download.prop("disabled", false); + callback_download = () => { + const element = $.spawn("a") + .text("download") + .attr("href", avatar.url) + .attr("download", "avatar-" + unique_id + "." + media_image_type(avatar.type, true)) + .css("display", "none") + .appendTo($("body")); + element[0].click(); + element.remove(); + }; + } + })); + callback_delete = () => { + Modals.spawnYesNo(_translations.JxSfQpqn || (_translations.JxSfQpqn = tr("Are you sure?")), _translations.xPENqFxw || (_translations.xPENqFxw = tr("Do you really want to delete this avatar?")), result => { + if (result) { + createErrorModal(_translations.WtQT3rEW || (_translations.WtQT3rEW = tr("Not implemented")), _translations.YQR30jMM || (_translations.YQR30jMM = tr("Avatar delete hasn't implemented yet"))).open(); + //TODO Implement avatar delete + } + }); + }; + overlay_no_user.hide(); + }; + set_selected_avatar(undefined, undefined, 0); + const update_avatar_list = () => { + const template_entry = $("#tmpl_avatar_list-list_entry"); + list_entries.empty(); + client.fileManager.requestFileList("/").then(files => { + const username_resolve = {}; + for (const entry of files) { + const avatar_id = entry.name.substr('avatar_'.length); + const unique_id = avatar_to_uid(avatar_id); + const tag = template_entry.renderTag({ + username: 'loading', + unique_id: unique_id, + size: Modals.human_file_size(entry.size), + timestamp: moment(entry.datetime * 1000).format('YY-MM-DD HH:mm') + }); + (username_resolve[unique_id] || (username_resolve[unique_id] = [])).push(name => { + const tag_username = tag.find(".column-username").empty(); + if (name) { + tag_username.append(ClientEntry.chatTag(0, name, unique_id, false)); + } + else { + tag_username.text("unknown"); + } + }); + list_entries.append(tag); + tag.on('click', () => { + list_entries.find('.selected').removeClass('selected'); + tag.addClass('selected'); + set_selected_avatar(unique_id, avatar_id, entry.size); + }); + } + if (container_list.hasScrollBar()) + container_list.addClass("scrollbar"); + client.serverConnection.command_helper.info_from_uid(...Object.keys(username_resolve)).then(result => { + for (const info of result) { + username_resolve[info.client_unique_id].forEach(e => e(info.client_nickname)); + delete username_resolve[info.client_unique_id]; + } + for (const uid of Object.keys(username_resolve)) { + (username_resolve[uid] || []).forEach(e => e(undefined)); + } + }).catch(error => { + log.error(LogCategory.GENERAL, _translations.EAlpgDaJ || (_translations.EAlpgDaJ = tr("Failed to fetch usernames from avatar names. Error: %o")), error); + createErrorModal(_translations._9ZhLutC || (_translations._9ZhLutC = tr("Failed to fetch usernames")), _translations.V5Na3lr_ || (_translations.V5Na3lr_ = tr("Failed to fetch usernames related to their avatar names")), undefined).open(); + }); + }).catch(error => { + //TODO: Display no perms error + log.error(LogCategory.GENERAL, _translations.yN5_Hp_F || (_translations.yN5_Hp_F = tr("Failed to receive avatar list. Error: %o")), error); + createErrorModal(_translations.jrf39vdv || (_translations.jrf39vdv = tr("Failed to list avatars")), _translations.q3nuUEOC || (_translations.q3nuUEOC = tr("Failed to receive avatar list.")), undefined).open(); + }); + }; + button_download.on('click', () => (callback_download || (() => { }))()); + button_delete.on('click', () => (callback_delete || (() => { }))()); + setTimeout(() => update_avatar_list(), 250); + modal.open(); + } + Modals.spawnAvatarList = spawnAvatarList; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["d08a8911c127b3f383f7c4a52021137598cace00cb6d24c3fcce51d3b4047aa0"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["d08a8911c127b3f383f7c4a52021137598cace00cb6d24c3fcce51d3b4047aa0"] = "d08a8911c127b3f383f7c4a52021137598cace00cb6d24c3fcce51d3b4047aa0"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "TsgDlJVp", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (9,21)" }, { name: "vheWLtP2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (51,88)" }, { name: "mBoCWnAP", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (55,64)" }, { name: "Q3EMl6hY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (58,52)" }, { name: "hwuaCgCO", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (59,62)" }, { name: "m6Nljdlg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (60,53)" }, { name: "x9AY7Kln", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (62,64)" }, { name: "nuFGAPOV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (65,46)" }, { name: "Wlsrp0ec", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (198,47)" }, { name: "fPLYDIee", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (198,67)" }, { name: "FXdq1lZN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (213,42)" }, { name: "QcWetQw3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (213,69)" }, { name: "kRMVC8Kv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (232,42)" }, { name: "rUYIwFuH", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (232,71)" }, { name: "x53hiZEu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalBookmarks.ts (305,59)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function spawnBookmarkModal() { + let modal; + modal = createModal({ + header: _translations.TsgDlJVp || (_translations.TsgDlJVp = tr("Manage bookmarks")), + body: () => { + let template = $("#tmpl_manage_bookmarks").renderTag({}); + let selected_bookmark; + const button_delete = template.find(".button-delete"); + const button_add_folder = template.find(".button-add-folder"); + const button_add_bookmark = template.find(".button-add-bookmark"); + const button_connect = template.find(".button-connect"); + const button_connect_tab = template.find(".button-connect-tab"); + const label_bookmark_name = template.find(".header .container-name"); + const label_server_address = template.find(".header .container-address"); + const input_bookmark_name = template.find(".input-bookmark-name"); + const input_connect_profile = template.find(".input-connect-profile"); + const input_server_address = template.find(".input-server-address"); + const input_server_password = template.find(".input-server-password"); + const label_server_name = template.find(".server-name"); + const label_server_region = template.find(".server-region"); + const label_last_ping = template.find(".server-ping"); + const label_client_count = template.find(".server-client-count"); + const label_connection_count = template.find(".server-connection-count"); + const update_buttons = () => { + button_delete.prop("disabled", !selected_bookmark); + button_connect.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); + button_connect_tab.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); + }; + const update_connect_info = () => { + if (selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) { + const entry = selected_bookmark; + const history = connection_log.history().find(e => e.address.hostname === entry.server_properties.server_address && e.address.port === entry.server_properties.server_port); + if (history) { + label_server_name.text(history.name); + label_server_region.empty().append($.spawn("div").addClass("country flag-" + history.country.toLowerCase()), $.spawn("div").text(i18n.country_name(history.country, _translations.vheWLtP2 || (_translations.vheWLtP2 = tr("Global"))))); + label_client_count.text(history.clients_online + "/" + history.clients_total); + label_connection_count.empty().append(...MessageHelper.formatMessage(_translations.mBoCWnAP || (_translations.mBoCWnAP = tr("You've connected {} times")), $.spawn("div").addClass("connect-count").text(history.total_connection))); + } + else { + label_server_name.text(_translations.Q3EMl6hY || (_translations.Q3EMl6hY = tr("Unknown"))); + label_server_region.empty().text(_translations.hwuaCgCO || (_translations.hwuaCgCO = tr("Unknown"))); + label_client_count.text(_translations.m6Nljdlg || (_translations.m6Nljdlg = tr("Unknown"))); + label_connection_count.empty().append(...MessageHelper.formatMessage(_translations.x9AY7Kln || (_translations.x9AY7Kln = tr("You {} connected to that server address")), $.spawn("div").addClass("connect-never").text("never"))); + } + label_last_ping.text(_translations.nuFGAPOV || (_translations.nuFGAPOV = tr("Average ping isn't yet supported"))); + } + else { + label_server_name.text("--"); + label_server_region.text("--"); + label_last_ping.text("--"); + label_client_count.text("--"); + label_connection_count.text("--"); + } + }; + const update_selected = () => { + input_bookmark_name.prop("disabled", !selected_bookmark); + input_connect_profile.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); + input_server_address.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); + input_server_password.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); + if (selected_bookmark) { + input_bookmark_name.val(selected_bookmark.display_name); + label_bookmark_name.text(selected_bookmark.display_name); + } + if (selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) { + const entry = selected_bookmark; + const address = entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port)); + label_server_address.text(address); + input_server_address.val(address); + let profile = input_connect_profile.find("option[value='" + entry.connect_profile + "']"); + if (profile.length == 0) + profile = input_connect_profile.find("option[value=default]"); + profile.prop("selected", true); + input_server_password.val(entry.server_properties.server_password_hash || entry.server_properties.server_password ? "WolverinDEV" : ""); + } + else { + input_server_password.val(""); + input_server_address.val(""); + input_connect_profile.find("option[value='no-value']").prop('selected', true); + label_server_address.text(" "); + } + update_connect_info(); + }; + const container_bookmarks = template.find(".container-bookmarks"); + const update_bookmark_list = (_current_selected) => { + container_bookmarks.empty(); + selected_bookmark = undefined; + update_selected(); + const hide_links = []; + const build_entry = (entry, sibling_data, index) => { + let container = $.spawn("div") + .addClass(entry.type === bookmarks.BookmarkType.ENTRY ? "bookmark" : "directory") + .addClass(index > 0 ? "linked" : "") + .addClass(sibling_data.first ? "link-start" : ""); + for (let i = 0; i < index; i++) { + container.append($.spawn("div") + .addClass("link") + .addClass(i + 1 === index ? " connected" : "") + .addClass(hide_links[i + 1] ? "hidden" : "")); + } + if (entry.type === bookmarks.BookmarkType.ENTRY) { + const bookmark = entry; + container.append(bookmark.last_icon_id ? + IconManager.generate_tag(IconManager.load_cached_icon(bookmark.last_icon_id || 0), { animate: false }) : + $.spawn("div").addClass("icon-container icon_em")); + } + else { + container.append($.spawn("div").addClass("icon-container icon_em client-folder")); + } + container.append($.spawn("div").addClass("name").attr("title", entry.display_name).text(entry.display_name)); + container.appendTo(container_bookmarks); + container.on('click', event => { + if (selected_bookmark === entry) + return; + selected_bookmark = entry; + container_bookmarks.find(".selected").removeClass("selected"); + container.addClass("selected"); + update_buttons(); + update_selected(); + }); + if (entry.unique_id === _current_selected) + container.trigger('click'); + hide_links.push(sibling_data.last); + let cindex = 0; + const children = entry.content || []; + for (const child of children) + build_entry(child, { first: cindex++ == 0, last: cindex == children.length }, index + 1); + hide_links.pop(); + }; + let cindex = 0; + const children = bookmarks.bookmarks().content; + for (const bookmark of children) + build_entry(bookmark, { first: cindex++ == 0, last: cindex == children.length }, 0); + }; + /* generate profile list */ + { + input_connect_profile.append($.spawn("option") + .attr("value", "no-value") + .text("") + .css("display", "none")); + for (const profile of profiles.profiles()) { + input_connect_profile.append($.spawn("option") + .attr("value", profile.id) + .text(profile.profile_name)); + } + } + /* buttons */ + { + button_delete.on('click', event => { + if (!selected_bookmark) + return; + if (selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY && selected_bookmark.content.length > 0) { + Modals.spawnYesNo(_translations.Wlsrp0ec || (_translations.Wlsrp0ec = tr("Are you sure")), _translations.fPLYDIee || (_translations.fPLYDIee = tr("Do you really want to delete this non empty directory?")), answer => { + if (answer) { + bookmarks.delete_bookmark(selected_bookmark); + bookmarks.save_bookmark(selected_bookmark); + update_bookmark_list(undefined); + } + }); + } + else { + bookmarks.delete_bookmark(selected_bookmark); + bookmarks.save_bookmark(selected_bookmark); + update_bookmark_list(undefined); + } + }); + button_add_folder.on('click', event => { + createInputModal(_translations.FXdq1lZN || (_translations.FXdq1lZN = tr("Enter a folder name")), _translations.QcWetQw3 || (_translations.QcWetQw3 = tr("Enter the folder name")), text => { + return true; + }, result => { + if (result) { + const mark = bookmarks.create_bookmark_directory(selected_bookmark ? + selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ? + selected_bookmark : + selected_bookmark.parent : + bookmarks.bookmarks(), result); + bookmarks.save_bookmark(mark); + update_bookmark_list(mark.unique_id); + } + }).open(); + }); + button_add_bookmark.on('click', event => { + createInputModal(_translations.kRMVC8Kv || (_translations.kRMVC8Kv = tr("Enter a bookmark name")), _translations.rUYIwFuH || (_translations.rUYIwFuH = tr("Enter the bookmark name")), text => { + return true; + }, result => { + if (result) { + const mark = bookmarks.create_bookmark(result, selected_bookmark ? + selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ? + selected_bookmark : + selected_bookmark.parent : + bookmarks.bookmarks(), { + server_password: "", + server_port: 9987, + server_address: "", + server_password_hash: "" + }, ""); + bookmarks.save_bookmark(mark); + update_bookmark_list(mark.unique_id); + } + }).open(); + }); + button_connect_tab.on('click', event => { + bookmarks.boorkmak_connect(selected_bookmark, true); + modal.close(); + }).toggle(!settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION)); + button_connect.on('click', event => { + bookmarks.boorkmak_connect(selected_bookmark, false); + modal.close(); + }); + } + /* inputs */ + { + input_bookmark_name.on('change keydown', event => { + const name = input_bookmark_name.val(); + const valid = name.length > 3; + input_bookmark_name.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + if (event.type === "change" && valid) { + selected_bookmark.display_name = name; + label_bookmark_name.text(name); + } + }); + input_server_address.on('change keydown', event => { + const address = input_server_address.val(); + const valid = !!address.match(Modals.Regex.IP_V4) || !!address.match(Modals.Regex.IP_V6) || !!address.match(Modals.Regex.DOMAIN); + input_server_address.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + if (valid) { + const entry = selected_bookmark; + let _v6_end = address.indexOf(']'); + let idx = address.lastIndexOf(':'); + if (idx != -1 && idx > _v6_end) { + entry.server_properties.server_port = parseInt(address.substr(idx + 1)); + entry.server_properties.server_address = address.substr(0, idx); + } + else { + entry.server_properties.server_address = address; + entry.server_properties.server_port = 9987; + } + label_server_address.text(entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port))); + update_connect_info(); + } + }); + input_connect_profile.on('change', event => { + const id = input_connect_profile.val(); + const profile = profiles.profiles().find(e => e.id === id); + if (profile) { + selected_bookmark.connect_profile = id; + } + else { + log.warn(LogCategory.GENERAL, _translations.x53hiZEu || (_translations.x53hiZEu = tr("Failed to change connect profile for profile %s to %s")), selected_bookmark.unique_id, id); + } + }); + } + /* Arrow key navigation for the bookmark list */ + { + let _focused = false; + let _focus_listener; + let _key_listener; + _focus_listener = event => { + _focused = false; + let element = event.target; + while (element) { + if (element === container_bookmarks[0]) { + _focused = true; + break; + } + element = element.parentNode; + } + }; + _key_listener = event => { + if (!_focused) + return; + if (event.key.toLowerCase() === "arrowdown") { + container_bookmarks.find(".selected").next().trigger('click'); + } + else if (event.key.toLowerCase() === "arrowup") { + container_bookmarks.find(".selected").prev().trigger('click'); + } + }; + document.addEventListener('click', _focus_listener); + document.addEventListener('keydown', _key_listener); + modal.close_listener.push(() => { + document.removeEventListener('click', _focus_listener); + document.removeEventListener('keydown', _key_listener); + }); + } + update_bookmark_list(undefined); + update_buttons(); + template.find(".container-bookmarks").on('keydown', event => { + console.error(event.key); + }); + template.find(".button-close").on('click', event => modal.close()); + return template.children(); + }, + footer: undefined, + width: "40em" + }); + modal.htmlTag.dividerfy().find(".modal-body").addClass("modal-bookmarks"); + modal.close_listener.push(() => { + control_bar.update_bookmarks(); + top_menu.rebuild_bookmarks(); + }); + modal.open(); + } + Modals.spawnBookmarkModal = spawnBookmarkModal; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["0e23f86b74d3179ef83d3e49bd849b2e36271d501e8e6ae0dafe6ad5bf4fc1a1"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["0e23f86b74d3179ef83d3e49bd849b2e36271d501e8e6ae0dafe6ad5bf4fc1a1"] = "0e23f86b74d3179ef83d3e49bd849b2e36271d501e8e6ae0dafe6ad5bf4fc1a1"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["9956def2660d1b3f450e99957454a2512b88c5456b04ba6ea6622103ae26fe34"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["9956def2660d1b3f450e99957454a2512b88c5456b04ba6ea6622103ae26fe34"] = "9956def2660d1b3f450e99957454a2512b88c5456b04ba6ea6622103ae26fe34"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "rnpixUHv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChangeLatency.ts (14,21)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + let modal; + function spawnChangeLatency(client, current, reset, apply, callback_flush) { + if (modal) + modal.close(); + const begin = Object.assign({}, current); + current = Object.assign({}, current); + modal = createModal({ + header: _translations.rnpixUHv || (_translations.rnpixUHv = tr("Change playback latency")), + body: function () { + let tag = $("#tmpl_change_latency").renderTag({ + client: htmltags.generate_client_object({ + add_braces: false, + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier, + client_id: client.clientId() + }), + have_flush: (typeof (callback_flush) === "function") + }); + const update_value = () => { + const valid = current.min_buffer < current.max_buffer; + modal.htmlTag.find(".modal-body").toggleClass("modal-red", !valid); + modal.htmlTag.find(".modal-body").toggleClass("modal-green", valid); + if (!valid) + return; + apply(current); + }; + let slider_min, slider_max; + { + const container = tag.find(".container-min"); + const tag_value = container.find(".value"); + const slider_tag = container.find(".container-slider"); + slider_min = sliderfy(slider_tag, { + initial_value: current.min_buffer, + step: 20, + max_value: 1000, + min_value: 0, + unit: 'ms' + }); + slider_tag.on('change', event => { + current.min_buffer = parseInt(slider_tag.attr("value")); + tag_value.text(current.min_buffer + "ms"); + update_value(); + }); + tag_value.text(current.min_buffer + "ms"); + } + { + const container = tag.find(".container-max"); + const tag_value = container.find(".value"); + const slider_tag = container.find(".container-slider"); + slider_max = sliderfy(slider_tag, { + initial_value: current.max_buffer, + step: 20, + max_value: 1020, + min_value: 20, + unit: 'ms' + }); + slider_tag.on('change', event => { + current.max_buffer = parseInt(slider_tag.attr("value")); + tag_value.text(current.max_buffer + "ms"); + update_value(); + }); + tag_value.text(current.max_buffer + "ms"); + } + setTimeout(update_value, 0); + tag.find(".button-close").on('click', event => { + modal.close(); + }); + tag.find(".button-cancel").on('click', event => { + apply(begin); + modal.close(); + }); + tag.find(".button-reset").on('click', event => { + current = Object.assign({}, reset()); + slider_max.value(current.max_buffer); + slider_min.value(current.min_buffer); + }); + tag.find(".button-flush").on('click', event => callback_flush()); + return tag.children(); + }, + footer: null, + width: 600 + }); + modal.close_listener.push(() => modal = undefined); + modal.open(); + modal.htmlTag.find(".modal-body").addClass("modal-latency"); + } + Modals.spawnChangeLatency = spawnChangeLatency; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["11929b66a8fc3047cb47278d16ce2fb341d4e68bf2428ef134e561ea19cce86a"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["11929b66a8fc3047cb47278d16ce2fb341d4e68bf2428ef134e561ea19cce86a"] = "11929b66a8fc3047cb47278d16ce2fb341d4e68bf2428ef134e561ea19cce86a"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "I5sxCWb1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (6,21)" }, { name: "MWhXMLY3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (18,37)" }, { name: "RE2vZk9z", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (18,63)" }, { name: "QaIxrSq4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (47,41)" }, { name: "EnZqLY5O", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (52,33)" }, { name: "LSPBpazO", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (56,9)" }, { name: "b5HSmHYG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (57,9)" }, { name: "sEjNJjwa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (58,9)" }, { name: "mpF_YaoQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (59,9)" }, { name: "rKgUkVIE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (60,9)" }, { name: "UyvxGUrS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (61,9)" }, { name: "YHbfKEbb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (69,26)" }, { name: "BR_g0fBa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (71,26)" }, { name: "MhdcoTGM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (74,26)" }, { name: "mGNRZmBq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (81,26)" }, { name: "ndprpEHA", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (84,30)" }, { name: "m3R76_Ct", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (86,30)" }, { name: "oFX6TI_6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (88,60)" }, { name: "SOznTjw5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (98,37)" }, { name: "MoxbL41x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (108,26)" }, { name: "jXz3b_jU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (115,72)" }, { name: "BM72xXA7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (124,28)" }, { name: "sAPSOS4t", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (126,28)" }, { name: "l53YQJDt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (128,73)" }, { name: "f7A9CNx_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (128,93)" }, { name: "NJeT_LZU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (135,26)" }, { name: "O6LcaNSq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalChannelInfo.ts (137,26)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function openChannelInfo(channel) { + let modal; + modal = createModal({ + header: (_translations.I5sxCWb1 || (_translations.I5sxCWb1 = tr("Channel information: "))) + channel.channelName(), + body: () => { + const template = $("#tmpl_channel_info").renderTag(); + const update_values = (container) => { + apply_channel_description(container.find(".container-description"), channel); + apply_general(container, channel); + }; + template.find(".button-copy").on('click', event => { + copy_to_clipboard(channel.properties.channel_description); + createInfoModal(_translations.MWhXMLY3 || (_translations.MWhXMLY3 = tr("Description copied")), _translations.RE2vZk9z || (_translations.RE2vZk9z = tr("The channel description has been copied to your clipboard!"))).open(); + }); + const button_update = template.find(".button-update"); + button_update.on('click', event => update_values(modal.htmlTag)); + update_values(template); + tooltip(template); + return template.children(); + }, + footer: null, + width: "65em" + }); + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".modal-body").addClass("modal-channel-info"); + modal.open(); + } + Modals.openChannelInfo = openChannelInfo; + function apply_channel_description(container, channel) { + const container_value = container.find(".value"); + const container_no_value = container.find(".no-value"); + channel.getChannelDescription().then(description => { + if (description) { + const result = xbbcode.parse(description, {}); + container_value[0].innerHTML = result.build_html(); + container_no_value.hide(); + container_value.show(); + } + else { + container_no_value.text(_translations.QaIxrSq4 || (_translations.QaIxrSq4 = tr("Channel has no description"))); + } + }); + container_value.hide(); + container_no_value.text(_translations.EnZqLY5O || (_translations.EnZqLY5O = tr("loading..."))).show(); + } + const codec_names = [ + _translations.LSPBpazO || (_translations.LSPBpazO = tr("Speex Narrowband")), + _translations.b5HSmHYG || (_translations.b5HSmHYG = tr("Speex Wideband")), + _translations.sEjNJjwa || (_translations.sEjNJjwa = tr("Speex Ultra-Wideband")), + _translations.mpF_YaoQ || (_translations.mpF_YaoQ = tr("CELT Mono")), + _translations.rKgUkVIE || (_translations.rKgUkVIE = tr("Opus Voice")), + _translations.UyvxGUrS || (_translations.UyvxGUrS = tr("Opus Music")) + ]; + function apply_general(container, channel) { + /* channel type */ + { + const tag = container.find(".channel-type .value").empty(); + if (channel.properties.channel_flag_permanent) + tag.text(_translations.YHbfKEbb || (_translations.YHbfKEbb = tr("Permanent"))); + else if (channel.properties.channel_flag_semi_permanent) + tag.text(_translations.BR_g0fBa || (_translations.BR_g0fBa = tr("Semi permanent"))); + else + //TODO: Channel delete delay! + tag.text(_translations.MhdcoTGM || (_translations.MhdcoTGM = tr("Temporary"))); + } + /* chat mode */ + { + const tag = container.find(".chat-mode .value").empty(); + if (channel.properties.channel_flag_conversation_private || channel.properties.channel_flag_password) { + tag.text(_translations.mGNRZmBq || (_translations.mGNRZmBq = tr("Private"))); + } + else { + if (channel.properties.channel_conversation_history_length == -1) + tag.text(_translations.ndprpEHA || (_translations.ndprpEHA = tr("Public; Semi permanent message saving"))); + else if (channel.properties.channel_conversation_history_length == 0) + tag.text(_translations.m3R76_Ct || (_translations.m3R76_Ct = tr("Public; Permanent message saving"))); + else + tag.append(MessageHelper.formatMessage(_translations.oFX6TI_6 || (_translations.oFX6TI_6 = tr("Public; Saving last {} messages")), channel.properties.channel_conversation_history_length)); + } + } + /* current clients */ + { + const tag = container.find(".current-clients .value").empty(); + if (channel.flag_subscribed) { + const current = channel.clients().length; + let channel_limit = _translations.SOznTjw5 || (_translations.SOznTjw5 = tr("Unlimited")); + if (!channel.properties.channel_flag_maxclients_unlimited) + channel_limit = "" + channel.properties.channel_maxclients; + else if (!channel.properties.channel_flag_maxfamilyclients_unlimited) { + if (channel.properties.channel_maxfamilyclients >= 0) + channel_limit = "" + channel.properties.channel_maxfamilyclients; + } + tag.text(current + " / " + channel_limit); + } + else { + tag.text(_translations.MoxbL41x || (_translations.MoxbL41x = tr("Channel not subscribed"))); + } + } + /* audio codec */ + { + const tag = container.find(".audio-codec .value").empty(); + tag.text((codec_names[channel.properties.channel_codec] || (_translations.jXz3b_jU || (_translations.jXz3b_jU = tr("Unknown")))) + " (" + channel.properties.channel_codec_quality + ")"); + } + /* audio encrypted */ + { + const tag = container.find(".audio-encrypted .value").empty(); + const mode = channel.channelTree.server.properties.virtualserver_codec_encryption_mode; + let appendix; + if (mode == 1) + appendix = _translations.BM72xXA7 || (_translations.BM72xXA7 = tr("Overridden by the server with Unencrypted!")); + else if (mode == 2) + appendix = _translations.sAPSOS4t || (_translations.sAPSOS4t = tr("Overridden by the server with Encrypted!")); + tag.html((channel.properties.channel_codec_is_unencrypted ? _translations.l53YQJDt || (_translations.l53YQJDt = tr("Unencrypted")) : _translations.f7A9CNx_ || (_translations.f7A9CNx_ = tr("Encrypted"))) + (appendix ? "
" + appendix : "")); + } + /* flag password */ + { + const tag = container.find(".flag-password .value").empty(); + if (channel.properties.channel_flag_password) + tag.text(_translations.NJeT_LZU || (_translations.NJeT_LZU = tr("Yes"))); + else + tag.text(_translations.O6LcaNSq || (_translations.O6LcaNSq = tr("No"))); + } + /* topic */ + { + const container_tag = container.find(".topic"); + const tag = container_tag.find(".value").empty(); + if (channel.properties.channel_topic) { + container_tag.show(); + tag.text(channel.properties.channel_topic); + } + else { + container_tag.hide(); + } + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3e0d934846e6678af688be0aae682a444a3e95e9842710b8911212ab336fc824"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3e0d934846e6678af688be0aae682a444a3e95e9842710b8911212ab336fc824"] = "3e0d934846e6678af688be0aae682a444a3e95e9842710b8911212ab336fc824"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "q4nJUy60", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (8,21)" }, { name: "yHGa0IfR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (61,58)" }, { name: "fmWIsvZh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (61,72)" }, { name: "wBmsWQ1W", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (67,58)" }, { name: "xHHtl43Q", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (67,71)" }, { name: "JUA4eXwZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (73,58)" }, { name: "wXLHZg4c", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (73,72)" }, { name: "vutnPHSB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (79,58)" }, { name: "ZdTd3wyS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (79,74)" }, { name: "au_VFD3u", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (85,58)" }, { name: "gJBoJ4Ma", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (85,74)" }, { name: "GUq7Pfeo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (132,33)" }, { name: "PFTGjDJA", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (132,57)" }, { name: "oLMwSJYc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (145,36)" }, { name: "AxikKxHc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (147,36)" }, { name: "zskgIt4S", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (149,36)" }, { name: "gSwEalIF", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (157,52)" }, { name: "DvkrY7gD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (194,87)" }, { name: "Jn3_cGLt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (202,24)" }, { name: "jmL5o4nI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (206,33)" }, { name: "Tz0k6vyV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (206,57)" }, { name: "O1OHNB8l", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (210,122)" }, { name: "RTQwXVHs", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (218,45)" }, { name: "NXeVdyQD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (222,49)" }, { name: "Rcficr3s", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (230,45)" }, { name: "DRUtOKKo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (234,49)" }, { name: "fAGxWnB9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (245,87)" }, { name: "S2krnqJS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (259,77)" }, { name: "bzbgZkFM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (261,34)" }, { name: "SkjAXN3e", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (275,42)" }, { name: "FjOC00RC", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (277,42)" }, { name: "kIMQ6Lut", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (279,34)" }, { name: "SjS4iMTN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (306,92)" }, { name: "vozQRN8R", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (336,104)" }, { name: "vdSpi1jT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (338,45)" }, { name: "syz7QWKN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (343,100)" }, { name: "icJiGa8t", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (345,43)" }, { name: "a7UYf6jl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (362,53)" }, { name: "Rghh2BiN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (366,45)" }, { name: "YhgjzYlu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (376,51)" }, { name: "XAulSBWk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (380,43)" }, { name: "jstU8KwW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (397,53)" }, { name: "iizJRjxp", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (401,45)" }, { name: "hsKLHain", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (411,51)" }, { name: "NAdmQZoI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (415,43)" }, { name: "dP3HUqzO", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (432,53)" }, { name: "i2XrSfWG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (436,45)" }, { name: "KB1Gb7t_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (446,51)" }, { name: "O0s5zgie", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (450,43)" }, { name: "AkeqGziU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (467,53)" }, { name: "HWcY62Qh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (471,45)" }, { name: "s0iM_F0m", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (481,51)" }, { name: "ks4dg80v", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (485,43)" }, { name: "j8hYfOEQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (500,45)" }, { name: "Zf4MdfLy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalClientInfo.ts (508,43)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function openClientInfo(client) { + let modal; + let update_callbacks = []; + modal = createModal({ + header: (_translations.q4nJUy60 || (_translations.q4nJUy60 = tr("Profile Information: "))) + client.clientNickName(), + body: () => { + const template = $("#tmpl_client_info").renderTag(); + /* the tab functionality */ + { + const container_tabs = template.find(".container-categories"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); + container_tabs.find(".entry").first().trigger('click'); + } + apply_static_info(client, template, modal, update_callbacks); + apply_client_status(client, template, modal, update_callbacks); + apply_basic_info(client, template.find(".container-basic"), modal, update_callbacks); + apply_groups(client, template.find(".container-groups"), modal, update_callbacks); + apply_packets(client, template.find(".container-packets"), modal, update_callbacks); + tooltip(template); + return template.children(); + }, + footer: null, + width: '60em' + }); + const updater = setInterval(() => { + client.request_connection_info().then(info => update_callbacks.forEach(e => e(info))); + }, 1000); + modal.htmlTag.find(".modal-body").addClass("modal-client-info"); + modal.open(); + modal.close_listener.push(() => clearInterval(updater)); + } + Modals.openClientInfo = openClientInfo; + const TIME_SECOND = 1000; + const TIME_MINUTE = 60 * TIME_SECOND; + const TIME_HOUR = 60 * TIME_MINUTE; + const TIME_DAY = 24 * TIME_HOUR; + const TIME_WEEK = 7 * TIME_DAY; + function format_time(time, default_value) { + let result = ""; + if (time > TIME_WEEK) { + const amount = Math.floor(time / TIME_WEEK); + result += " " + amount + " " + (amount > 1 ? _translations.yHGa0IfR || (_translations.yHGa0IfR = tr("Weeks")) : _translations.fmWIsvZh || (_translations.fmWIsvZh = tr("Week"))); + time -= amount * TIME_WEEK; + } + if (time > TIME_DAY) { + const amount = Math.floor(time / TIME_DAY); + result += " " + amount + " " + (amount > 1 ? _translations.wBmsWQ1W || (_translations.wBmsWQ1W = tr("Days")) : _translations.xHHtl43Q || (_translations.xHHtl43Q = tr("Day"))); + time -= amount * TIME_DAY; + } + if (time > TIME_HOUR) { + const amount = Math.floor(time / TIME_HOUR); + result += " " + amount + " " + (amount > 1 ? _translations.JUA4eXwZ || (_translations.JUA4eXwZ = tr("Hours")) : _translations.wXLHZg4c || (_translations.wXLHZg4c = tr("Hour"))); + time -= amount * TIME_HOUR; + } + if (time > TIME_MINUTE) { + const amount = Math.floor(time / TIME_MINUTE); + result += " " + amount + " " + (amount > 1 ? _translations.vutnPHSB || (_translations.vutnPHSB = tr("Minutes")) : _translations.ZdTd3wyS || (_translations.ZdTd3wyS = tr("Minute"))); + time -= amount * TIME_MINUTE; + } + if (time > TIME_SECOND) { + const amount = Math.floor(time / TIME_SECOND); + result += " " + amount + " " + (amount > 1 ? _translations.au_VFD3u || (_translations.au_VFD3u = tr("Seconds")) : _translations.gJBoJ4Ma || (_translations.gJBoJ4Ma = tr("Second"))); + time -= amount * TIME_SECOND; + } + return result.length > 0 ? result.substring(1) : default_value; + } + function apply_static_info(client, tag, modal, callbacks) { + tag.find(".container-avatar").append(client.channelTree.client.fileManager.avatars.generate_chat_tag({ database_id: client.properties.client_database_id, id: client.clientId() }, client.properties.client_unique_identifier)); + tag.find(".container-name").append(client.createChatTag()); + tag.find(".client-description").text(client.properties.client_description); + } + function apply_client_status(client, tag, modal, callbacks) { + tag.find(".status-output-disabled").toggle(!client.properties.client_output_hardware); + tag.find(".status-input-disabled").toggle(!client.properties.client_input_hardware); + tag.find(".status-output-muted").toggle(client.properties.client_output_muted); + tag.find(".status-input-muted").toggle(client.properties.client_input_muted); + tag.find(".status-away").toggle(client.properties.client_away); + if (client.properties.client_away_message) { + tag.find(".container-away-message").show().find("a").text(client.properties.client_away_message); + } + else { + tag.find(".container-away-message").hide(); + } + } + function apply_basic_info(client, tag, modal, callbacks) { + /* Unique ID */ + { + const container = tag.find(".property-unique-id"); + container.find(".value a").text(client.clientUid()); + container.find(".value-dbid").text(client.properties.client_database_id); + container.find(".button-copy").on('click', event => { + copy_to_clipboard(client.clientUid()); + createInfoModal(_translations.GUq7Pfeo || (_translations.GUq7Pfeo = tr("Unique ID copied")), _translations.PFTGjDJA || (_translations.PFTGjDJA = tr("The unique id has been copied to your clipboard!"))).open(); + }); + } + /* TeaForo */ + { + const container = tag.find(".property-teaforo .value").empty(); + if (client.properties.client_teaforo_id) { + container.children().remove(); + let text = client.properties.client_teaforo_name; + if ((client.properties.client_teaforo_flags & 0x01) > 0) + text += " (" + (_translations.oLMwSJYc || (_translations.oLMwSJYc = tr("Banned"))) + ")"; + if ((client.properties.client_teaforo_flags & 0x02) > 0) + text += " (" + (_translations.AxikKxHc || (_translations.AxikKxHc = tr("Stuff"))) + ")"; + if ((client.properties.client_teaforo_flags & 0x04) > 0) + text += " (" + (_translations.zskgIt4S || (_translations.zskgIt4S = tr("Premium"))) + ")"; + $.spawn("a") + .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) + .attr("target", "_blank") + .text(text) + .appendTo(container); + } + else { + container.append($.spawn("a").text(_translations.gSwEalIF || (_translations.gSwEalIF = tr("Not connected")))); + } + } + /* Version */ + { + const container = tag.find(".property-version"); + let version_full = client.properties.client_version; + let version = version_full.substring(0, version_full.indexOf(" ")); + container.find(".value").empty().append($.spawn("a").attr("title", version_full).text(version), $.spawn("a").addClass("a-on").text("on"), $.spawn("a").text(client.properties.client_platform)); + const container_timestamp = container.find(".container-tooltip"); + let timestamp = -1; + version_full.replace(/\[build: ?([0-9]+)]/gmi, (group, ts) => { + timestamp = parseInt(ts); + return ""; + }); + if (timestamp > 0) { + container_timestamp.find(".value-timestamp").text(moment(timestamp * 1000).format('MMMM Do YYYY, h:mm:ss a')); + container_timestamp.show(); + } + else { + container_timestamp.hide(); + } + } + /* Country */ + { + const container = tag.find(".property-country"); + container.find(".value").empty().append($.spawn("div").addClass("country flag-" + client.properties.client_country.toLowerCase()), $.spawn("a").text(i18n.country_name(client.properties.client_country, _translations.DvkrY7gD || (_translations.DvkrY7gD = tr("Unknown"))))); + } + /* IP Address */ + { + const container = tag.find(".property-ip"); + const value = container.find(".value a"); + value.text(_translations.Jn3_cGLt || (_translations.Jn3_cGLt = tr("loading..."))); + container.find(".button-copy").on('click', event => { + copy_to_clipboard(value.text()); + createInfoModal(_translations.jmL5o4nI || (_translations.jmL5o4nI = tr("Client IP copied")), _translations.Tz0k6vyV || (_translations.Tz0k6vyV = tr("The client IP has been copied to your clipboard!"))).open(); + }); + callbacks.push(info => { + value.text(info.connection_client_ip ? (info.connection_client_ip + ":" + info.connection_client_port) : _translations.O1OHNB8l || (_translations.O1OHNB8l = tr("Hidden"))); + }); + } + /* first connected */ + { + const container = tag.find(".property-first-connected"); + container.find(".value a").text(_translations.RTQwXVHs || (_translations.RTQwXVHs = tr("loading..."))); + client.updateClientVariables().then(() => { + container.find(".value a").text(moment(client.properties.client_created * 1000).format('MMMM Do YYYY, h:mm:ss a')); + }).catch(error => { + container.find(".value a").text(_translations.NXeVdyQD || (_translations.NXeVdyQD = tr("error"))); + }); + } + /* connect count */ + { + const container = tag.find(".property-connect-count"); + container.find(".value a").text(_translations.Rcficr3s || (_translations.Rcficr3s = tr("loading..."))); + client.updateClientVariables().then(() => { + container.find(".value a").text(client.properties.client_totalconnections); + }).catch(error => { + container.find(".value a").text(_translations.DRUtOKKo || (_translations.DRUtOKKo = tr("error"))); + }); + } + /* Online since */ + { + const container = tag.find(".property-online-since"); + const node = container.find(".value a")[0]; + if (node) { + const update = () => { + node.innerText = format_time(client.calculateOnlineTime() * 1000, _translations.fAGxWnB9 || (_translations.fAGxWnB9 = tr("0 Seconds"))); + }; + callbacks.push(update); /* keep it in sync with all other updates. Else it looks wired */ + update(); + } + } + /* Idle time */ + { + const container = tag.find(".property-idle-time"); + const node = container.find(".value a")[0]; + if (node) { + callbacks.push(info => { + node.innerText = format_time(info.connection_idle_time, _translations.S2krnqJS || (_translations.S2krnqJS = tr("Currently active"))); + }); + node.innerText = _translations.bzbgZkFM || (_translations.bzbgZkFM = tr("loading...")); + } + } + /* ping */ + { + const container = tag.find(".property-ping"); + const node = container.find(".value a")[0]; + if (node) { + callbacks.push(info => { + if (info.connection_ping >= 0) + node.innerText = info.connection_ping.toFixed(0) + "ms ± " + info.connection_ping_deviation.toFixed(2) + "ms"; + else if (info.connection_ping == -1 && info.connection_ping_deviation == -1) + node.innerText = _translations.SkjAXN3e || (_translations.SkjAXN3e = tr("Not calculated")); + else + node.innerText = _translations.FjOC00RC || (_translations.FjOC00RC = tr("loading...")); + }); + node.innerText = _translations.kIMQ6Lut || (_translations.kIMQ6Lut = tr("loading...")); + } + } + } + function apply_groups(client, tag, modal, callbacks) { + /* server groups */ + { + const container_entries = tag.find(".entries"); + const container_empty = tag.find(".container-default-groups"); + const update_groups = () => { + container_entries.empty(); + container_empty.show(); + for (const group_id of client.assignedServerGroupIds()) { + if (group_id == client.channelTree.server.properties.virtualserver_default_server_group) + continue; + const group = client.channelTree.client.groups.serverGroup(group_id); + if (!group) + continue; //This shall never happen! + container_empty.hide(); + container_entries.append($.spawn("div").addClass("entry").append(client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid), $.spawn("a").addClass("name").text(group.name + " (" + group.id + ")"), $.spawn("div").addClass("button-delete").append($.spawn("div").addClass("icon_em client-delete").attr("title", _translations.SjS4iMTN || (_translations.SjS4iMTN = tr("Delete group"))).on('click', event => { + client.channelTree.client.serverConnection.send_command("servergroupdelclient", { + sgid: group.id, + cldbid: client.properties.client_database_id + }).then(result => update_groups()); + })).toggleClass("visible", client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberRemovePower) || + client.clientId() == client.channelTree.client.getClientId() && client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_SELF_REMOVE_POWER).granted(group.requiredMemberRemovePower)))); + } + }; + tag.find(".button-group-add").on('click', () => client.open_assignment_modal()); + update_groups(); + } + } + function apply_packets(client, tag, modal, callbacks) { + /* Packet Loss */ + { + const container = tag.find(".statistic-packet-loss"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + callbacks.push(info => { + node_downstream.innerText = info.connection_server2client_packetloss_control < 0 ? _translations.vozQRN8R || (_translations.vozQRN8R = tr("Not calculated")) : (info.connection_server2client_packetloss_control || 0).toFixed(); + }); + node_downstream.innerText = _translations.vdSpi1jT || (_translations.vdSpi1jT = tr("loading...")); + } + if (node_upstream) { + callbacks.push(info => { + node_upstream.innerText = info.connection_client2server_packetloss_total < 0 ? _translations.syz7QWKN || (_translations.syz7QWKN = tr("Not calculated")) : (info.connection_client2server_packetloss_total || 0).toFixed(); + }); + node_upstream.innerText = _translations.icJiGa8t || (_translations.icJiGa8t = tr("loading...")); + } + } + /* Packets transmitted */ + { + const container = tag.find(".statistic-transmitted-packets"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + callbacks.push(info => { + let packets = 0; + packets += info.connection_packets_received_speech > 0 ? info.connection_packets_received_speech : 0; + packets += info.connection_packets_received_control > 0 ? info.connection_packets_received_control : 0; + packets += info.connection_packets_received_keepalive > 0 ? info.connection_packets_received_keepalive : 0; + if (packets == 0 && info.connection_packets_received_keepalive == -1) + node_downstream.innerText = _translations.a7UYf6jl || (_translations.a7UYf6jl = tr("Not calculated")); + else + node_downstream.innerText = MessageHelper.format_number(packets, { unit: "Packets" }); + }); + node_downstream.innerText = _translations.Rghh2BiN || (_translations.Rghh2BiN = tr("loading...")); + } + if (node_upstream) { + callbacks.push(info => { + let packets = 0; + packets += info.connection_packets_sent_speech > 0 ? info.connection_packets_sent_speech : 0; + packets += info.connection_packets_sent_control > 0 ? info.connection_packets_sent_control : 0; + packets += info.connection_packets_sent_keepalive > 0 ? info.connection_packets_sent_keepalive : 0; + if (packets == 0 && info.connection_packets_sent_keepalive == -1) + node_upstream.innerText = _translations.YhgjzYlu || (_translations.YhgjzYlu = tr("Not calculated")); + else + node_upstream.innerText = MessageHelper.format_number(packets, { unit: "Packets" }); + }); + node_upstream.innerText = _translations.XAulSBWk || (_translations.XAulSBWk = tr("loading...")); + } + } + /* Bytes transmitted */ + { + const container = tag.find(".statistic-transmitted-bytes"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bytes_received_speech > 0 ? info.connection_bytes_received_speech : 0; + bytes += info.connection_bytes_received_control > 0 ? info.connection_bytes_received_control : 0; + bytes += info.connection_bytes_received_keepalive > 0 ? info.connection_bytes_received_keepalive : 0; + if (bytes == 0 && info.connection_bytes_received_keepalive == -1) + node_downstream.innerText = _translations.jstU8KwW || (_translations.jstU8KwW = tr("Not calculated")); + else + node_downstream.innerText = MessageHelper.network.format_bytes(bytes); + }); + node_downstream.innerText = _translations.iizJRjxp || (_translations.iizJRjxp = tr("loading...")); + } + if (node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bytes_sent_speech > 0 ? info.connection_bytes_sent_speech : 0; + bytes += info.connection_bytes_sent_control > 0 ? info.connection_bytes_sent_control : 0; + bytes += info.connection_bytes_sent_keepalive > 0 ? info.connection_bytes_sent_keepalive : 0; + if (bytes == 0 && info.connection_bytes_sent_keepalive == -1) + node_upstream.innerText = _translations.hsKLHain || (_translations.hsKLHain = tr("Not calculated")); + else + node_upstream.innerText = MessageHelper.network.format_bytes(bytes); + }); + node_upstream.innerText = _translations.NAdmQZoI || (_translations.NAdmQZoI = tr("loading...")); + } + } + /* Bandwidth second */ + { + const container = tag.find(".statistic-bandwidth-second"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_received_last_second_speech > 0 ? info.connection_bandwidth_received_last_second_speech : 0; + bytes += info.connection_bandwidth_received_last_second_control > 0 ? info.connection_bandwidth_received_last_second_control : 0; + bytes += info.connection_bandwidth_received_last_second_keepalive > 0 ? info.connection_bandwidth_received_last_second_keepalive : 0; + if (bytes == 0 && info.connection_bandwidth_received_last_second_keepalive == -1) + node_downstream.innerText = _translations.dP3HUqzO || (_translations.dP3HUqzO = tr("Not calculated")); + else + node_downstream.innerText = MessageHelper.network.format_bytes(bytes, { time: "s" }); + }); + node_downstream.innerText = _translations.i2XrSfWG || (_translations.i2XrSfWG = tr("loading...")); + } + if (node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_sent_last_second_speech > 0 ? info.connection_bandwidth_sent_last_second_speech : 0; + bytes += info.connection_bandwidth_sent_last_second_control > 0 ? info.connection_bandwidth_sent_last_second_control : 0; + bytes += info.connection_bandwidth_sent_last_second_keepalive > 0 ? info.connection_bandwidth_sent_last_second_keepalive : 0; + if (bytes == 0 && info.connection_bandwidth_sent_last_second_keepalive == -1) + node_upstream.innerText = _translations.KB1Gb7t_ || (_translations.KB1Gb7t_ = tr("Not calculated")); + else + node_upstream.innerText = MessageHelper.network.format_bytes(bytes, { time: "s" }); + }); + node_upstream.innerText = _translations.O0s5zgie || (_translations.O0s5zgie = tr("loading...")); + } + } + /* Bandwidth minute */ + { + const container = tag.find(".statistic-bandwidth-minute"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_received_last_minute_speech > 0 ? info.connection_bandwidth_received_last_minute_speech : 0; + bytes += info.connection_bandwidth_received_last_minute_control > 0 ? info.connection_bandwidth_received_last_minute_control : 0; + bytes += info.connection_bandwidth_received_last_minute_keepalive > 0 ? info.connection_bandwidth_received_last_minute_keepalive : 0; + if (bytes == 0 && info.connection_bandwidth_received_last_minute_keepalive == -1) + node_downstream.innerText = _translations.AkeqGziU || (_translations.AkeqGziU = tr("Not calculated")); + else + node_downstream.innerText = MessageHelper.network.format_bytes(bytes, { time: "s" }); + }); + node_downstream.innerText = _translations.HWcY62Qh || (_translations.HWcY62Qh = tr("loading...")); + } + if (node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_sent_last_minute_speech > 0 ? info.connection_bandwidth_sent_last_minute_speech : 0; + bytes += info.connection_bandwidth_sent_last_minute_control > 0 ? info.connection_bandwidth_sent_last_minute_control : 0; + bytes += info.connection_bandwidth_sent_last_minute_keepalive > 0 ? info.connection_bandwidth_sent_last_minute_keepalive : 0; + if (bytes == 0 && info.connection_bandwidth_sent_last_minute_keepalive == -1) + node_upstream.innerText = _translations.s0iM_F0m || (_translations.s0iM_F0m = tr("Not calculated")); + else + node_upstream.innerText = MessageHelper.network.format_bytes(bytes, { time: "s" }); + }); + node_upstream.innerText = _translations.ks4dg80v || (_translations.ks4dg80v = tr("loading...")); + } + } + /* quota */ + { + const container = tag.find(".statistic-quota"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + if (node_downstream) { + client.updateClientVariables().then(info => { + //TODO: Test for own client info and if so then show the max quota (needed permission) + node_downstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_downloaded, { exact: false }); + }); + node_downstream.innerText = _translations.j8hYfOEQ || (_translations.j8hYfOEQ = tr("loading...")); + } + if (node_upstream) { + client.updateClientVariables().then(info => { + //TODO: Test for own client info and if so then show the max quota (needed permission) + node_upstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_uploaded, { exact: false }); + }); + node_upstream.innerText = _translations.Zf4MdfLy || (_translations.Zf4MdfLy = tr("loading...")); + } + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5bc7b026ccd4fdff697caf41b4c42b847d0401f9506454a667d610d3f97f0462"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5bc7b026ccd4fdff697caf41b4c42b847d0401f9506454a667d610d3f97f0462"] = "5bc7b026ccd4fdff697caf41b4c42b847d0401f9506454a667d610d3f97f0462"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "IhOda4YW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalGroupAssignment.ts (8,21)" }, { name: "VqL4CmtZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalGroupAssignment.ts (43,42)" }, { name: "TvHg9OzB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalGroupAssignment.ts (48,97)" }, { name: "IAFhqbSA", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalGroupAssignment.ts (64,91)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + let current_modal; + function createServerGroupAssignmentModal(client, callback) { + if (current_modal) + current_modal.close(); + current_modal = createModal({ + header: _translations.IhOda4YW || (_translations.IhOda4YW = tr("Server Groups")), + body: () => { + let tag = {}; + let groups = tag["groups"] = []; + tag["client"] = client.createChatTag(); + const _groups = client.channelTree.client.groups.serverGroups.sort(GroupManager.sorter()); + for (let group of _groups) { + if (group.type != GroupType.NORMAL) + continue; + let entry = {}; + entry["id"] = group.id; + entry["name"] = group.name; + entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); + entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id; + tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid); + groups.push(entry); + } + let template = $("#tmpl_server_group_assignment").renderTag(tag); + const update_groups = () => { + for (let group of _groups) { + template.find("input[group-id='" + group.id + "']").prop("checked", client.groupAssigned(group)); + } + }; + template.find(".group-entry input").each((_idx, _entry) => { + let entry = $(_entry); + entry.on('change', event => { + let group_id = parseInt(entry.attr("group-id")); + let group = client.channelTree.client.groups.serverGroup(group_id); + if (!group) { + console.warn(_translations.VqL4CmtZ || (_translations.VqL4CmtZ = tr("Could not resolve target group!"))); + return false; + } + let target = entry.prop("checked"); + callback([group.id], target).catch(e => { log.warn(LogCategory.GENERAL, _translations.TvHg9OzB || (_translations.TvHg9OzB = tr("Failed to change group assignment: %o")), e); }).then(update_groups); + }); + }); + template.find(".button-close").on('click', () => current_modal.close()); + template.find(".button-remove-all").on('click', () => { + const group_ids = []; + template.find(".group-entry input").each((_idx, _entry) => { + let entry = $(_entry); + if (entry.attr("default") !== undefined || !entry.prop("checked")) + return; + group_ids.push(parseInt(entry.attr("group-id"))); + }); + callback(group_ids, false).catch(e => { log.warn(LogCategory.GENERAL, _translations.IAFhqbSA || (_translations.IAFhqbSA = tr("Failed to remove all group assignments: %o")), e); }).then(update_groups); + }); + update_groups(); + return template; + }, + footer: null, + min_width: "10em" + }); + current_modal.htmlTag.find(".modal-body").addClass("modal-server-group-assignments"); + current_modal.close_listener.push(() => current_modal = undefined); + current_modal.open(); + } + Modals.createServerGroupAssignmentModal = createServerGroupAssignmentModal; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["6ddbc806d1e545bfbe7cd6edfda70c9a909461095cf3c503ccf9a1e9c6e456f2"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["6ddbc806d1e545bfbe7cd6edfda70c9a909461095cf3c503ccf9a1e9c6e456f2"] = "6ddbc806d1e545bfbe7cd6edfda70c9a909461095cf3c503ccf9a1e9c6e456f2"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "CQoIZUkg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (11,21)" }, { name: "zoVj4N6w", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (98,59)" }, { name: "aGhDORx1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (136,52)" }, { name: "Ygeok_Hp", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (137,42)" }, { name: "OKoqOjb_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (149,30)" }, { name: "V9FDbgFE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (158,30)" }, { name: "_KpIpBvt", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (162,34)" }, { name: "WTCs0FZc", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (204,27)" }, { name: "OBdh35e5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (205,30)" }, { name: "weFhyRXi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (211,27)" }, { name: "slnJLUnr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (212,30)" }, { name: "ZQUb3mnr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (228,35)" }, { name: "PM2yox2o", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (229,38)" }, { name: "Hdzgrw14", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (236,35)" }, { name: "haFBfmL2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (237,38)" }, { name: "fWTWB0rC", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (246,39)" }, { name: "rXQ9XpMl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (247,42)" }, { name: "qwKMVZCa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (253,33)" }, { name: "FKg8ziMk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (255,39)" }, { name: "jbJiQdmE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (256,42)" }, { name: "Cnm0yzhD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (276,35)" }, { name: "nP_b16tY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (277,38)" }, { name: "VWE0wbL5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (282,35)" }, { name: "p7VzcfRi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (283,38)" }, { name: "LRMQF4bW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (340,37)" }, { name: "iPgGKIpi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (358,31)" }, { name: "TveVOKL0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (364,41)" }, { name: "Sdb1D0Df", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (368,39)" }, { name: "hPVhYCvy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (373,35)" }, { name: "Xqb7msAQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (381,37)" }, { name: "evYSGDAg", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (397,45)" }, { name: "gJb1dy46", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (402,39)" }, { name: "fYxv3EsZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (403,39)" }, { name: "kU_0puQM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (408,37)" }, { name: "EPY4nTY6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (414,39)" }, { name: "CqQcLAq4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (416,43)" }, { name: "NyIZkcuR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (418,43)" }, { name: "bt6ItaiX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (420,43)" }, { name: "x9YWUXAH", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (429,37)" }, { name: "wn8NJP_6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (440,21)" }, { name: "F2IChL3l", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (582,39)" }, { name: "mO_jmbzr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIconSelect.ts (586,39)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function spawnIconSelect(client, callback_icon, selected_icon) { + selected_icon = selected_icon || 0; + let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1); + const modal = createModal({ + header: _translations.CQoIZUkg || (_translations.CQoIZUkg = tr("Icons")), + footer: undefined, + body: () => { + return $("#tmpl_icon_select").renderTag({ + enable_select: !!callback_icon, + enable_upload: allow_manage, + enable_delete: allow_manage + }); + }, + min_width: "20em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-icon-select"); + const button_select = modal.htmlTag.find(".button-select"); + const button_delete = modal.htmlTag.find(".button-delete").prop("disabled", true); + const button_upload = modal.htmlTag.find(".button-upload").prop("disabled", !allow_manage); + const container_loading = modal.htmlTag.find(".container-loading").hide(); + const container_no_permissions = modal.htmlTag.find(".container-no-permissions").hide(); + const container_error = modal.htmlTag.find(".container-error").hide(); + const selected_container = modal.htmlTag.find(".selected-item-container"); + const container_icons = modal.htmlTag.find(".container-icons"); + const container_icons_remote = container_icons.find(".container-icons-remote"); + const container_icons_local = container_icons.find(".container-icons-local"); + const update_local_icons = (icons) => { + container_icons_local.empty(); + for (const icon_id of icons) { + const tag = client.fileManager.icons.generateTag(icon_id, { animate: false }).attr('title', "Icon " + icon_id); + if (callback_icon) { + tag.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + tag.addClass("selected"); + selected_container.empty().append(tag.clone()); + selected_icon = icon_id; + button_select.prop("disabled", false); + }); + tag.on('dblclick', event => { + callback_icon(icon_id); + modal.close(); + }); + if (icon_id == selected_icon) + tag.trigger('click'); + } + tag.appendTo(container_icons_local); + } + }; + const update_remote_icons = () => { + container_no_permissions.hide(); + container_error.hide(); + container_loading.show(); + const display_remote_error = (error) => { + if (typeof (error) === "string") { + container_error.find(".error-message").text(error); + container_error.show(); + } + else { + container_error.hide(); + } + }; + client.fileManager.requestFileList("/icons").then(icons => { + const container_icons_remote_parent = container_icons_remote.parent(); + container_icons_remote.detach().empty(); + const chunk_size = 50; + const icon_chunks = []; + let index = 0; + while (icons.length > index) { + icon_chunks.push(icons.slice(index, index + chunk_size)); + index += chunk_size; + } + const process_next_chunk = () => { + const chunk = icon_chunks.pop_front(); + if (!chunk) + return; + for (const icon of chunk) { + const icon_id = parseInt(icon.name.substr("icon_".length)); + if (Number.isNaN(icon_id)) { + log.warn(LogCategory.GENERAL, _translations.zoVj4N6w || (_translations.zoVj4N6w = tr("Received an unparsable icon within icon list (%o)")), icon); + continue; + } + const tag = client.fileManager.icons.generateTag(icon_id, { animate: false }).attr('title', "Icon " + icon_id); + if (callback_icon || allow_manage) { + tag.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + tag.addClass("selected"); + selected_container.empty().append(tag.clone()); + selected_icon = icon_id; + button_select.prop("disabled", false); + button_delete.prop("disabled", !allow_manage); + }); + tag.on('dblclick', event => { + if (!callback_icon) + return; + callback_icon(icon_id); + modal.close(); + }); + if (icon_id == selected_icon) + tag.trigger('click'); + } + tag.appendTo(container_icons_remote); + } + setTimeout(process_next_chunk, 100); + }; + process_next_chunk(); + container_icons_remote_parent.append(container_icons_remote); + container_error.hide(); + container_loading.hide(); + container_no_permissions.hide(); + }).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + container_no_permissions.show(); + } + else { + log.error(LogCategory.GENERAL, _translations.aGhDORx1 || (_translations.aGhDORx1 = tr("Failed to fetch icon list. Error: %o")), error); + display_remote_error(_translations.Ygeok_Hp || (_translations.Ygeok_Hp = tr("Failed to fetch icon list"))); + } + container_loading.hide(); + }); + }; + button_delete.on('click', event => { + if (!selected_icon) + return; + const selected = modal.htmlTag.find(".selected"); + if (selected.length != 1) + console.warn(_translations.OKoqOjb_ || (_translations.OKoqOjb_ = tr("UI selected icon length does not equal with 1! (%o)")), selected.length); + if (selected_icon < 1000) + return; /* we cant delete local icons */ + client.fileManager.icons.delete_icon(selected_icon).then(() => { + selected.detach(); + }).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) + return; + console.warn(_translations.V9FDbgFE || (_translations.V9FDbgFE = tr("Failed to delete icon %d: %o")), selected_icon, error); + error = error instanceof CommandResult ? error.extra_message || error.message : error; + createErrorModal(_translations._KpIpBvt || (_translations._KpIpBvt = tr("Failed to delete icon")), tra("Failed to delete icon.
Error: ", error)).open(); + }); + }); + button_upload.on('click', event => spawnIconUpload(client)); + update_local_icons([100, 200, 300, 500, 600]); + update_remote_icons(); + modal.htmlTag.find('.button-reload').on('click', () => update_remote_icons()); + button_select.prop("disabled", true).on('click', () => { + if (callback_icon) + callback_icon(selected_icon); + modal.close(); + }); + modal.htmlTag.find(".button-select-no-icon").on('click', () => { + if (callback_icon) + callback_icon(0); + modal.close(); + }); + modal.open(); + } + Modals.spawnIconSelect = spawnIconSelect; + function handle_icon_upload(file, client) { + const icon = {}; + icon.file = file; + icon.upload_state = "unset"; + const file_too_big = () => { + console.error(_translations.WTCs0FZc || (_translations.WTCs0FZc = tr("Failed to load file %s: File is too big!")), file.name); + createErrorModal(_translations.OBdh35e5 || (_translations.OBdh35e5 = tr("Icon upload failed")), tra("Failed to upload icon {}.
The given file is too big!", file.name)).open(); + icon.state = "error"; + }; + if (file.size > 1024 * 1024 * 512) { + file_too_big(); + } + else if ((file.size | 0) <= 0) { + console.error(_translations.weFhyRXi || (_translations.weFhyRXi = tr("Failed to load file %s: Your browser does not support file sizes!")), file.name); + createErrorModal(_translations.slnJLUnr || (_translations.slnJLUnr = tr("Icon upload failed")), tra("Failed to upload icon {}.
Your browser does not support file sizes!", file.name)).open(); + icon.state = "error"; + return; + } + else { + icon.state = "loading"; + icon.loader = (() => __awaiter(this, void 0, void 0, function* () { + const reader = new FileReader(); + try { + yield new Promise((resolve, reject) => { + reader.onload = resolve; + reader.onerror = reject; + reader.readAsDataURL(file); + }); + } + catch (error) { + console.log("Image failed to load (%o)", error); + console.error(_translations.ZQUb3mnr || (_translations.ZQUb3mnr = tr("Failed to load file %s: Image failed to load")), file.name); + createErrorModal(_translations.PM2yox2o || (_translations.PM2yox2o = tr("Icon upload failed")), tra("Failed to upload icon {}.
Failed to load image", file.name)).open(); + icon.state = "error"; + return; + } + const result = reader.result; + if (typeof (result) !== "string") { + console.error(_translations.Hdzgrw14 || (_translations.Hdzgrw14 = tr("Failed to load file %s: Result is not an media string (%o)")), file.name, result); + createErrorModal(_translations.haFBfmL2 || (_translations.haFBfmL2 = tr("Icon upload failed")), tra("Failed to upload icon {}.
Result is not an media string", file.name)).open(); + icon.state = "error"; + return; + } + /* get the CRC32 sum */ + { + if (!result.startsWith("data:image/")) { + console.error(_translations.fWTWB0rC || (_translations.fWTWB0rC = tr("Failed to load file %s: Invalid data media type (%o)")), file.name, result); + createErrorModal(_translations.rXQ9XpMl || (_translations.rXQ9XpMl = tr("Icon upload failed")), tra("Failed to upload icon {}.
File is not an image", file.name)).open(); + icon.state = "error"; + return; + } + const semi = result.indexOf(';'); + const type = result.substring(11, semi); + console.log(_translations.qwKMVZCa || (_translations.qwKMVZCa = tr("Given image has type %s")), type); + if (!result.substr(semi + 1).startsWith("base64,")) { + console.error(_translations.FKg8ziMk || (_translations.FKg8ziMk = tr("Failed to load file %s: Mimetype isnt base64 encoded (%o)")), file.name, result.substr(semi + 1)); + createErrorModal(_translations.jbJiQdmE || (_translations.jbJiQdmE = tr("Icon upload failed")), tra("Failed to upload icon {}.
Decoder returned unknown result", file.name)).open(); + icon.state = "error"; + return; + } + const crc = new Crc32(); + crc.update(arrayBufferBase64(result.substr(semi + 8))); + icon.icon_id = crc.digest(10); + } + const image = document.createElement("img"); + try { + yield new Promise((resolve, reject) => { + image.onload = resolve; + image.onerror = reject; + image.src = result; + }); + } + catch (error) { + console.log("Image failed to load (%o)", error); + console.error(_translations.Cnm0yzhD || (_translations.Cnm0yzhD = tr("Failed to load file %s: Image failed to load")), file.name); + createErrorModal(_translations.nP_b16tY || (_translations.nP_b16tY = tr("Icon upload failed")), tra("Failed to upload icon {}.{:br:}Failed to load image", file.name)).open(); + icon.state = "error"; + } + const width_error = message => { + console.error(_translations.VWE0wbL5 || (_translations.VWE0wbL5 = tr("Failed to load file %s: Invalid bounds: %s")), file.name, message); + createErrorModal(_translations.p7VzcfRi || (_translations.p7VzcfRi = tr("Icon upload failed")), tra("Failed to upload icon {}.{:br:}Image is too large ({})", file.name, message)).open(); + icon.state = "error"; + }; + if (!result.startsWith("data:image/svg+xml")) { + if (image.naturalWidth > 32 && image.naturalHeight > 32) { + width_error("width and height (max 32px). Given: " + image.naturalWidth + "x" + image.naturalHeight); + return; + } + if (image.naturalWidth > 32) { + width_error("width (max 32px)"); + return; + } + if (image.naturalHeight > 32) { + width_error("height (max 32px)"); + return; + } + } + console.log("Image loaded (%dx%d) %s (%s)", image.naturalWidth, image.naturalHeight, image.name, icon.icon_id); + icon.image_element = () => { + const image = document.createElement("img"); + image.src = result; + return image; + }; + icon.state = "valid"; + }))(); + icon.upload_icon = () => { + const create_progress_bar = () => { + const html = $.spawn("div").addClass("progress"); + const indicator = $.spawn("div").addClass("progress-bar bg-success progress-bar-striped progress-bar-animated"); + const message = $.spawn("div").addClass("progress-message"); + const set_value = value => { + indicator.stop(true, false).animate({ width: value + "%" }, 250); + if (value === 100) + setTimeout(() => indicator.removeClass("progress-bar-striped progress-bar-animated"), 900); + }; + return { + html_tag: html.append(indicator).append(message), + set_value: set_value, + set_message: msg => message.text(msg), + set_error: (msg) => { + let index = msg.lastIndexOf(':'); + message.text(index == -1 ? msg : msg.substring(index + 1)); + message.attr('title', msg); + set_value(100); + indicator.removeClass("bg-success").addClass("bg-danger"); + } + }; + }; + const container_image = $.spawn("div").addClass("container-icon"); + const bar = create_progress_bar(); + const set_error = message => { + bar.set_value(100); + bar.set_message((_translations.LRMQF4bW || (_translations.LRMQF4bW = tr("error: "))) + message); + }; + const html_tag = $.spawn("div") + .addClass("upload-entry") + .append(container_image) + .append(bar.html_tag); + icon.upload_html_tag = html_tag; + let icon_added = false; + if (icon.image_element) { + container_image.append(icon.image_element()); + icon_added = true; + } + bar.set_value(0); + bar.set_value(_translations.iPgGKIpi || (_translations.iPgGKIpi = tr("waiting"))); + return () => __awaiter(this, void 0, void 0, function* () { + const time_begin = Date.now(); + if (icon.state === "loading") { + bar.set_message(_translations.TveVOKL0 || (_translations.TveVOKL0 = tr("Awaiting local processing"))); + yield icon.loader; + // @ts-ignore Could happen because the loader function updates the state + if (icon.state !== "valid") { + set_error(_translations.Sdb1D0Df || (_translations.Sdb1D0Df = tr("local processing failed"))); + icon.upload_state = "error"; + return; + } + } + else if (icon.state === "error") { + set_error(_translations.hPVhYCvy || (_translations.hPVhYCvy = tr("local processing error"))); + icon.upload_state = "error"; + return; + } + if (!icon_added) + container_image.append(icon.image_element()); + bar.set_value(25); + bar.set_message(_translations.Xqb7msAQ || (_translations.Xqb7msAQ = tr("initializing"))); + let upload_key; + try { + upload_key = yield client.fileManager.upload_file({ + channel: undefined, + channel_password: undefined, + name: '/icon_' + icon.icon_id, + overwrite: false, + path: '', + size: icon.file.size + }); + } + catch (error) { + if (error instanceof CommandResult && error.id == ErrorID.FILE_ALREADY_EXISTS) { + if (!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) + yield new Promise(resolve => setTimeout(resolve, 500 + Math.floor(Math.random() * 500))); + bar.set_message(_translations.evYSGDAg || (_translations.evYSGDAg = tr("icon already exists"))); + bar.set_value(100); + icon.upload_state = "uploaded"; + return; + } + console.error(_translations.gJb1dy46 || (_translations.gJb1dy46 = tr("Failed to initialize upload: %o")), error); + bar.set_error(_translations.fYxv3EsZ || (_translations.fYxv3EsZ = tr("failed to initialize upload"))); + icon.upload_state = "error"; + return; + } + bar.set_value(50); + bar.set_message(_translations.kU_0puQM || (_translations.kU_0puQM = tr("uploading"))); + const connection = transfer.spawn_upload_transfer(upload_key); + try { + yield connection.put_data(icon.file); + } + catch (error) { + console.error(_translations.EPY4nTY6 || (_translations.EPY4nTY6 = tr("Icon upload failed for icon %s: %o")), icon.file.name, error); + if (typeof (error) === "string") + bar.set_error((_translations.CqQcLAq4 || (_translations.CqQcLAq4 = tr("upload failed: "))) + error); + else if (typeof (error.message) === "string") + bar.set_error((_translations.NyIZkcuR || (_translations.NyIZkcuR = tr("upload failed: "))) + error.message); + else + bar.set_error(_translations.bt6ItaiX || (_translations.bt6ItaiX = tr("upload failed"))); + icon.upload_state = "error"; + return; + } + const time_end = Date.now(); + if (!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) + yield new Promise(resolve => setTimeout(resolve, Math.max(0, 1000 - (time_end - time_begin)))); + bar.set_value(100); + bar.set_message(_translations.x9YWUXAH || (_translations.x9YWUXAH = tr("upload completed"))); + icon.upload_state = "uploaded"; + }); + }; + } + return icon; + } + function spawnIconUpload(client) { + const modal = createModal({ + header: _translations.wn8NJP_6 || (_translations.wn8NJP_6 = tr("Upload Icons")), + footer: undefined, + body: () => $("#tmpl_icon_upload").renderTag(), + closeable: false, + min_width: "20em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-icon-upload"); + const button_upload = modal.htmlTag.find(".button-upload"); + const button_delete = modal.htmlTag.find(".button-remove").prop("disabled", true); + const button_add = modal.htmlTag.find(".button-add"); + const button_upload_abort = modal.htmlTag.find(".button-upload-abort"); + const input_file = modal.htmlTag.find(".input-file-upload"); + const container_icons = modal.htmlTag.find(".container-icons"); + let selected_icon; + let icons = []; + const update_upload_button = () => { + const icon_count = icons.filter(e => e.state === "valid").length; + button_upload.empty(); + tra("Upload icons ({})", icon_count).forEach(e => e.appendTo(button_upload)); + button_upload.prop("disabled", icon_count == 0); + }; + update_upload_button(); + const add_icon = (icon) => { + icons.push(icon); + icon.loader.then(e => { + if (icon.state === "valid") { + const image = icon.image_element(); + const element = $.spawn("div") + .addClass("icon-container") + .append(image); + container_icons.append(icon.html_tag = element); + element.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + element.addClass("selected"); + selected_icon = icon; + button_delete.prop("disabled", false); + }); + update_upload_button(); + } + }); + }; + button_delete.on('click', event => { + if (!selected_icon) + return; + icons = icons.filter(e => e !== selected_icon); + if (selected_icon.html_tag) + selected_icon.html_tag.detach(); + button_delete.prop("disabled", true); + update_upload_button(); + }); + button_add.on('click', event => input_file.click()); + input_file.on('change', event => { + if (input_file[0].files.length > 0) { + for (let index = 0; index < input_file[0].files.length; index++) { + const file = input_file[0].files.item(index); + { + let duplicate = false; + for (const icon of icons) + if (icon.file.name === file.name && icon.file.lastModified === file.lastModified && icon.state !== "error") { + duplicate = true; + break; + } + if (duplicate) + continue; + } + add_icon(handle_icon_upload(file, client)); + } + } + }); + container_icons.on('dragover', ((event) => { + event.stopPropagation(); + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + })); + container_icons.on('drop', ((event) => { + event.stopPropagation(); + event.preventDefault(); + for (let index = 0; index < event.dataTransfer.files.length; index++) { + const file = event.dataTransfer.files.item(index); + { + let duplicate = false; + for (const icon of icons) + if (icon.file === file && icon.state !== "error") { + duplicate = true; + break; + } + if (duplicate) + continue; + } + add_icon(handle_icon_upload(file, client)); + } + })); + /* upload process */ + { + const container_upload = modal.htmlTag.find(".container-upload"); + const container_error = container_upload.find(".container-error"); + const container_success = container_upload.find(".container-success"); + const container_process = container_upload.find(".container-process"); + const container_info = container_upload.find(".container-info"); + const container_statistics = container_upload.find(".uploaded-statistics"); + const show_critical_error = message => { + container_error.find(".error-message").text(message); + container_error.removeClass("hidden"); + }; + const finish_upload = () => { + icons = icons.filter(e => { + if (e.upload_state === "uploaded") { + e.html_tag.detach(); + return false; + } + return true; + }); + update_upload_button(); + button_upload.prop("disabled", false); + button_upload.prop("disabled", false); + container_upload.hide(); + container_error.addClass("hidden"); + container_error.addClass("hidden"); + modal.set_closeable(true); + }; + const execute_upload = () => __awaiter(this, void 0, void 0, function* () { + if (!client || !client.fileManager) { + show_critical_error(_translations.F2IChL3l || (_translations.F2IChL3l = tr("Invalid client handle"))); + return; + } + if (!client.connected) { + show_critical_error(_translations.mO_jmbzr || (_translations.mO_jmbzr = tr("Not connected"))); + return; + } + let invoke_count = 0; + let succeed_count = 0; + let failed_count = 0; + const uploads = icons.filter(e => e.state !== "error"); + const executes = []; + for (const icon of uploads) { + executes.push({ + icon: icon, + task: icon.upload_icon() + }); + if (!icon.upload_html_tag) + continue; /* TODO: error? */ + icon.upload_html_tag.appendTo(container_process); + } + const update_state = () => container_statistics.text(invoke_count + " | " + succeed_count + " | " + failed_count); + for (const execute of executes) { + invoke_count++; + update_state(); + try { + yield execute.task(); + if (execute.icon.upload_state !== "uploaded") + throw "failed"; + succeed_count++; + } + catch (error) { + failed_count++; + } + update_state(); + } + container_info.css({ opacity: 1 }).animate({ opacity: 0 }, 250, () => container_info.css({ opacity: undefined }).hide()); + container_success.find(".message").html("Total icons: " + invoke_count + "
" + + "Succeeded icons: " + succeed_count + "
" + + "Failed icons: " + failed_count); + container_success.removeClass("hidden"); + }); + button_upload.on('click', event => { + modal.set_closeable(false); + button_upload.prop("disabled", true); + button_delete.prop("disabled", true); + button_add.prop("disabled", true); + container_process.empty(); + container_upload.show(); + execute_upload(); + }); + button_upload_abort.on('click', event => finish_upload()); + container_error.addClass("hidden"); + container_success.addClass("hidden"); + container_upload.hide(); + } + modal.open(); + modal.set_closeable(true); + } + Modals.spawnIconUpload = spawnIconUpload; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c565e1eabe89f658f183c84f4ff888d5c072e3806c50d19008c565d931f36873"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c565e1eabe89f658f183c84f4ff888d5c072e3806c50d19008c565d931f36873"] = "c565e1eabe89f658f183c84f4ff888d5c072e3806c50d19008c565d931f36873"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Q26wE6OY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (7,21)" }, { name: "HvyxPjCN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (35,40)" }, { name: "KdBchkgj", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (35,54)" }, { name: "JadIc69x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (57,46)" }, { name: "F3hRLwdH", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (57,80)" }, { name: "lvUbN5p7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (70,53)" }, { name: "Qkr0VmdR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (70,119)" }, { name: "OumC_Krz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (79,46)" }, { name: "tRwKNZyK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (79,80)" }, { name: "oZn_XbMx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (127,21)" }, { name: "XAQhUOKN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (161,32)" }, { name: "s81RoyQO", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (168,36)" }, { name: "WEuJt8JD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (177,32)" }, { name: "_ZCxkHLa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (183,39)" }, { name: "CEPpkX2y", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (184,36)" }, { name: "VA2ysnwv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalIdentity.ts (200,36)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function spawnTeamSpeakIdentityImprove(identity, name) { + let modal; + let elapsed_timer; + modal = createModal({ + header: _translations.Q26wE6OY || (_translations.Q26wE6OY = tr("Improve identity")), + body: () => { + let template = $("#tmpl_settings-teamspeak_improve").renderTag({ + identity_name: name + }); + template = $.spawn("div").append(template); + let active; + const button_start_stop = template.find(".button-start-stop"); + const button_close = template.find(".button-close"); + const input_current_level = template.find(".identity-level input"); + const input_target_level = template.find(".identity-target-level input"); + const input_threads = template.find(".threads input"); + const input_hash_rate = template.find(".hash-rate input"); + const input_elapsed = template.find(".time-elapsed input"); + button_close.on('click', event => { + if (active) + button_start_stop.trigger('click'); + if (modal.shown) + modal.close(); + }); + button_start_stop.on('click', event => { + button_start_stop + .toggleClass('btn-success', active) + .toggleClass('btn-danger', !active) + .text(active ? _translations.HvyxPjCN || (_translations.HvyxPjCN = tr("Start")) : _translations.KdBchkgj || (_translations.KdBchkgj = tr("Stop"))); + input_threads.prop("disabled", !active); + input_target_level.prop("disabled", !active); + if (active) { + input_hash_rate.val(0); + clearInterval(elapsed_timer); + active = false; + return; + } + active = true; + input_hash_rate.val("nan"); + const threads = parseInt(input_threads.val()); + const target_level = parseInt(input_target_level.val()); + if (target_level == 0) { + identity.improve_level(-1, threads, () => active, current_level => { + input_current_level.val(current_level); + }, hash_rate => { + input_hash_rate.val(hash_rate); + }).catch(error => { + console.error(error); + createErrorModal(_translations.JadIc69x || (_translations.JadIc69x = tr("Failed to improve identity")), (_translations.F3hRLwdH || (_translations.F3hRLwdH = tr("Failed to improve identity.
Error:"))) + error).open(); + if (active) + button_start_stop.trigger('click'); + }); + } + else { + identity.improve_level(target_level, threads, () => active, current_level => { + input_current_level.val(current_level); + }, hash_rate => { + input_hash_rate.val(hash_rate); + }).then(success => { + if (success) { + identity.level().then(level => { + input_current_level.val(level); + createInfoModal(_translations.lvUbN5p7 || (_translations.lvUbN5p7 = tr("Identity successfully improved")), MessageHelper.formatMessage(_translations.Qkr0VmdR || (_translations.Qkr0VmdR = tr("Identity successfully improved to level {}")), level)).open(); + }).catch(error => { + input_current_level.val("error: " + error); + }); + } + if (active) + button_start_stop.trigger('click'); + }).catch(error => { + console.error(error); + createErrorModal(_translations.OumC_Krz || (_translations.OumC_Krz = tr("Failed to improve identity")), (_translations.tRwKNZyK || (_translations.tRwKNZyK = tr("Failed to improve identity.
Error:"))) + error).open(); + if (active) + button_start_stop.trigger('click'); + }); + } + const begin = Date.now(); + elapsed_timer = setInterval(() => { + const time = (Date.now() - begin) / 1000; + let seconds = Math.floor(time % 60).toString(); + let minutes = Math.floor(time / 60).toString(); + if (seconds.length < 2) + seconds = "0" + seconds; + if (minutes.length < 2) + minutes = "0" + minutes; + input_elapsed.val(minutes + ":" + seconds); + }, 1000); + }); + template.find(".identity-unique-id input").val(identity.uid()); + identity.level().then(level => { + input_current_level.val(level); + }).catch(error => { + input_current_level.val("error: " + error); + }); + tooltip(template); + return template.children(); + }, + footer: undefined, + width: 750 + }); + modal.htmlTag.find(".modal-body").addClass("modal-identity-improve modal-green"); + modal.close_listener.push(() => modal.htmlTag.find(".button-close").trigger('click')); + modal.open(); + return modal; + } + Modals.spawnTeamSpeakIdentityImprove = spawnTeamSpeakIdentityImprove; + function spawnTeamSpeakIdentityImport(callback) { + let modal; + let selected_type; + let identities = {}; + modal = createModal({ + header: _translations.oZn_XbMx || (_translations.oZn_XbMx = tr("Import identity")), + body: () => { + let template = $("#tmpl_settings-teamspeak_import").renderTag(); + const button_import = template.find(".button-import"); + const button_file_select = template.find(".button-load-file"); + const container_status = template.find(".container-status"); + const input_text = template.find(".input-identity-text"); + const input_file = template.find(".file-selector"); + const set_status = (message, type) => { + container_status.toggleClass("hidden", !message); + if (message) { + container_status.toggleClass("error", type === "error"); + container_status.toggleClass("loading", type === "loading"); + container_status.find("a").text(message); + } + }; + button_file_select.on('click', event => input_file.trigger('click')); + template.find("input[name='type']").on('change', event => { + const type = event.target.value; + button_file_select.prop("disabled", type !== "file"); + input_text.prop("disabled", type !== "text"); + selected_type = type; + button_import.prop("disabled", !identities[type]); + }); + template.find("input[name='type'][value='file']").prop("checked", true).trigger("change"); + const import_identity = (data, ini) => { + set_status(_translations.XAQhUOKN || (_translations.XAQhUOKN = tr("Parsing identity")), "loading"); + profiles.identities.TeaSpeakIdentity.import_ts(data, ini).then(identity => { + identities[selected_type] = identity; + set_status("Identity parsed successfully.", "success"); + button_import.prop("disabled", false); + template.find(".success").show(); + }).catch(error => { + set_status((_translations.s81RoyQO || (_translations.s81RoyQO = tr("Failed to parse identity: "))) + error, "error"); + }); + }; + /* file select button */ + input_file.on('change', event => { + const element = event.target; + const file_reader = new FileReader(); + set_status(_translations.WEuJt8JD || (_translations.WEuJt8JD = tr("Loading file")), "loading"); + file_reader.onload = function () { + import_identity(file_reader.result, true); + }; + file_reader.onerror = ev => { + console.error(_translations._ZCxkHLa || (_translations._ZCxkHLa = tr("Failed to read give identity file: %o")), ev); + set_status(_translations.CEPpkX2y || (_translations.CEPpkX2y = tr("Failed to read the identity file.")), "error"); + return; + }; + if (element.files && element.files.length > 0) + file_reader.readAsText(element.files[0]); + }); + input_text.on('change keyup', event => { + const text = input_text.val(); + if (!text) { + set_status("", "success"); + return; + } + if (text.indexOf('V') == -1) { + set_status(_translations.VA2ysnwv || (_translations.VA2ysnwv = tr("Invalid identity string")), "error"); + return; + } + import_identity(text, false); + }); + button_import.on('click', event => { + modal.close(); + callback(identities[selected_type]); + }); + set_status("", "success"); + button_import.prop("disabled", true); + return template.children(); + }, + footer: undefined, + width: 750 + }); + modal.htmlTag.find(".modal-body").addClass("modal-identity-import modal-green"); + modal.open(); + return modal; + } + Modals.spawnTeamSpeakIdentityImport = spawnTeamSpeakIdentityImport; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c5513d1c4dddf0d8de54b0178a14b1f259bb4b79f9befed66bd8679198a54bf0"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c5513d1c4dddf0d8de54b0178a14b1f259bb4b79f9befed66bd8679198a54bf0"] = "c5513d1c4dddf0d8de54b0178a14b1f259bb4b79f9befed66bd8679198a54bf0"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "kBxmDo9P", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalInvite.ts (116,21)" }, { name: "ePBMg_5b", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalInvite.ts (170,35)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + const DefaultGeneratorSettings = { + flag_direct: true, + flag_resolved: false + }; + const build_url = (base, params) => { + if (Object.keys(params).length == 0) + return base; + return base + "?" + Object.keys(params) + .map(e => e + "=" + encodeURIComponent(params[e])) + .join("&"); + }; + //TODO: Server password + const url_generators = { + "tea-web": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + const address_str = address.host + (address.port === 9987 ? "" : address.port); + const parameter = "connect_default=" + (properties.flag_direct ? 1 : 0) + "&connect_address=" + encodeURIComponent(address_str); + let pathbase = ""; + if (document.location.protocol !== 'https:') { + /* + * Seems to be a test environment or the TeaClient for localhost where we dont have to use https. + */ + pathbase = "https://web.teaspeak.de/"; + } + else if (document.location.hostname === "localhost" || document.location.host.startsWith("127.")) { + pathbase = "https://web.teaspeak.de/"; + } + else { + pathbase = document.location.origin + document.location.pathname; + } + return pathbase + "?" + parameter; + }, + setting_available: setting => { + return { + flag_direct: true, + flag_resolved: true + }[setting] || false; + } + }, + "tea-client": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + let parameters = { + connect_default: properties.flag_direct ? 1 : 0 + }; + if (address.port != 9987) + parameters["port"] = address.port; + return build_url("teaclient://" + address.host + "/", parameters); + }, + setting_available: setting => { + return { + flag_direct: true, + flag_resolved: true + }[setting] || false; + } + }, + "teamspeak": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + let parameters = {}; + if (address.port != 9987) + parameters["port"] = address.port; + /* + ts3server://? + port=9987 + nickname=UserNickname + password=serverPassword + channel=MyDefaultChannel + cid=channelID + channelpassword=defaultChannelPassword + token=TokenKey + addbookmark=MyBookMarkLabel + */ + return build_url("ts3server://" + address.host + "/", parameters); + }, + setting_available: setting => { + return { + flag_direct: false, + flag_resolved: true + }[setting] || false; + } + } + }; + function spawnInviteEditor(connection) { + let modal; + modal = createModal({ + header: _translations.kBxmDo9P || (_translations.kBxmDo9P = tr("Invite URL creator")), + body: () => { + let template = $("#tmpl_invite").renderTag(); + template.find(".button-close").on('click', event => modal.close()); + return template; + }, + footer: undefined, + min_width: "20em", + width: "50em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-invite"); + const button_copy = modal.htmlTag.find(".button-copy"); + const input_type = modal.htmlTag.find(".property-type select"); + const label_output = modal.htmlTag.find(".text-output"); + const invite_settings = [ + { + key: "flag_direct", + node: modal.htmlTag.find(".flag-direct-connect input"), + value: node => node.prop('checked'), + set_value: (node, value) => node.prop('checked', value == "1"), + disable: (node, flag) => node.prop('disabled', flag) + .firstParent('.checkbox').toggleClass('disabled', flag) + }, + { + key: "flag_resolved", + node: modal.htmlTag.find(".flag-resolved-address input"), + value: node => node.prop('checked'), + set_value: (node, value) => node.prop('checked', value == "1"), + disable: (node, flag) => node.prop('disabled', flag) + .firstParent('.checkbox').toggleClass('disabled', flag) + } + ]; + const update_buttons = () => { + const generator = url_generators[input_type.val()]; + if (!generator) { + for (const s of invite_settings) + s.disable(s.node, true); + return; + } + for (const s of invite_settings) + s.disable(s.node, !generator.setting_available(s.key)); + }; + const update_link = () => { + const generator = url_generators[input_type.val()]; + if (!generator) { + button_copy.prop('disabled', true); + label_output.text(_translations.ePBMg_5b || (_translations.ePBMg_5b = tr("Missing link generator"))); + return; + } + button_copy.prop('disabled', false); + const properties = { + address: connection.channelTree.server.remote_address, + resolved_address: connection.channelTree.client.serverConnection.remote_address() + }; + for (const s of invite_settings) + properties[s.key] = s.value(s.node); + label_output.text(generator.generate(properties)); + }; + for (const s of invite_settings) { + s.node.on('change keyup', () => { + settings.changeGlobal(Settings.FN_INVITE_LINK_SETTING(s.key), s.value(s.node)); + update_link(); + }); + s.set_value(s.node, settings.global(Settings.FN_INVITE_LINK_SETTING(s.key), DefaultGeneratorSettings[s.key])); + } + input_type.on('change', () => { + settings.changeGlobal(Settings.KEY_LAST_INVITE_LINK_TYPE, input_type.val()); + update_buttons(); + update_link(); + }).val(settings.global(Settings.KEY_LAST_INVITE_LINK_TYPE)); + button_copy.on('click', event => { + label_output.select(); + document.execCommand('copy'); + }); + update_buttons(); + update_link(); + modal.open(); + } + Modals.spawnInviteEditor = spawnInviteEditor; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["8551b0f0a521938dc720e3dfc08a60a6490a3e63ba4351dc91390a4142ec3872"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["8551b0f0a521938dc720e3dfc08a60a6490a3e63ba4351dc91390a4142ec3872"] = "8551b0f0a521938dc720e3dfc08a60a6490a3e63ba4351dc91390a4142ec3872"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "MLOU8bZn", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalKeySelect.ts (4,21)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function spawnKeySelect(callback) { + let modal = createModal({ + header: _translations.MLOU8bZn || (_translations.MLOU8bZn = tr("Select a key")), + body: () => $("#tmpl_key_select").renderTag().children(), + footer: null, + width: "", + closeable: false + }); + const container_key = modal.htmlTag.find(".container-key a"); + const button_save = modal.htmlTag.find(".button-save"); + const button_cancel = modal.htmlTag.find(".button-cancel"); + let current_key_age; + let last_key; + let current_key; + const listener = (event) => { + if (event.type === ppt.EventType.KEY_PRESS) { + //console.log(tr("Key select got key press for %o"), event); + last_key = current_key; + current_key = event; + current_key_age = Date.now(); + container_key.text(ppt.key_description(event)); + button_save.prop("disabled", false); + } + }; + button_save.on('click', () => { + if (!app.is_web()) { + /* Because pressing the close button is also a mouse action */ + if (current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE2") + current_key = last_key; + } + callback(current_key); + modal.close(); + }).prop("disabled", true); + button_cancel.on('click', () => modal.close()); + ppt.register_key_listener(listener); + modal.close_listener.push(() => ppt.unregister_key_listener(listener)); + modal.htmlTag.find(".modal-body").addClass("modal-keyselect modal-green"); + modal.open(); + } + Modals.spawnKeySelect = spawnKeySelect; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["7565364a2370e4653cf564d50e9bf0c10d91c7b074bec401591fab85bca45080"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["7565364a2370e4653cf564d50e9bf0c10d91c7b074bec401591fab85bca45080"] = "7565364a2370e4653cf564d50e9bf0c10d91c7b074bec401591fab85bca45080"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "Znbysrt7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (13,24)" }, { name: "kb1s3EbM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (35,28)" }, { name: "sJFl4SFw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (35,90)" }, { name: "UX9qUFB7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (41,24)" }, { name: "FR91na9p", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (63,51)" }, { name: "cm5CVSTu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (84,61)" }, { name: "VAfsInts", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (104,51)" }, { name: "xYJ3MH9A", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (150,57)" }, { name: "oWfb8Ffx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (169,51)" }, { name: "oxz3fXdx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (192,51)" }, { name: "YpKG3TzD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (217,51)" }, { name: "CpejOouC", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (240,51)" }, { name: "xoWTeact", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (270,51)" }, { name: "ypBEEqy0", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (294,51)" }, { name: "KMq5sCKT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (336,51)" }, { name: "P7lD9A4F", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (367,51)" }, { name: "xlYmaITB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (558,30)" }, { name: "T0lvDV9H", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (575,46)" }, { name: "c8dSeLZS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (583,69)" }, { name: "ck4GDnos", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (595,43)" }, { name: "ds03qJjo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (595,92)" }, { name: "whmoRxKI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (617,46)" }, { name: "TDfuhMFl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (640,65)" }, { name: "Wba6WIJI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (672,43)" }, { name: "KegIP_Oa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (672,96)" }, { name: "TJs6MrB8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (743,43)" }, { name: "V8NKF3zP", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (743,110)" }, { name: "NUwpilKf", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (797,43)" }, { name: "WRDVagVX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (797,109)" }, { name: "q_kBtt47", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (824,40)" }, { name: "gPVZM1r8", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (872,38)" }, { name: "lczFny7X", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (878,34)" }, { name: "Mk7tMQ1m", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (883,57)" }, { name: "uQ1yO6Yi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (894,43)" }, { name: "DJ1pfloN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (894,98)" }, { name: "pRpmtFjG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (906,34)" }, { name: "_jdzYHNZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (924,46)" }, { name: "q5PwTk4Y", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (933,69)" }, { name: "qokDUQla", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (947,43)" }, { name: "yuB8OE8e", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (947,104)" }, { name: "h2kycEoy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (975,46)" }, { name: "sTxLZaJY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1019,43)" }, { name: "WQE2CZa2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1019,99)" }, { name: "VYtn59Ga", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1073,43)" }, { name: "YUZUVDR4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1073,99)" }, { name: "xp_ufUbh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1100,40)" }, { name: "chknDAXh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1177,46)" }, { name: "n75WL7_X", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1182,46)" }, { name: "ac3ohRnJ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1217,54)" }, { name: "rRXCJPGM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1222,54)" }, { name: "H7Wzuyk5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1245,42)" }, { name: "Jl44qY0M", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1245,70)" }, { name: "dtLjMQff", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1274,48)" }, { name: "xeMEtAeJ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1300,82)" }, { name: "Q3EsnBCT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1319,44)" }, { name: "oN5z34tB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1433,51)" }, { name: "rriL0uvS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1439,55)" }, { name: "NMxty5e5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1447,74)" }, { name: "v50TMxti", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1461,46)" }, { name: "bjvee5Ts", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1472,51)" }, { name: "uaoGscii", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1535,87)" }, { name: "qZHZIWWT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1539,39)" }, { name: "WA9xLJOj", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1598,40)" }, { name: "QrA6IXLw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1733,51)" }, { name: "vXhgxTE9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1764,47)" }, { name: "qTOaE8kn", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1774,51)" }, { name: "W6NSppXR", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1782,70)" }, { name: "NRqLeDXD", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1796,47)" }, { name: "IezxIp9G", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1819,42)" }, { name: "TcN1P2qm", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalMusicManage.ts (1858,40)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function openMusicManage(client, bot) { + const ev_registry = new events.Registry(); + ev_registry.enable_debug("music-manage"); + //dummy_controller(ev_registry); + permission_controller(ev_registry, bot, client); + let modal = createModal({ + header: tr(_translations.Znbysrt7 || (_translations.Znbysrt7 = tr("Playlist Manage"))), + body: () => build_modal(ev_registry), + footer: null, + min_width: "35em", + closeable: true + }); + modal.htmlTag.find(".modal-body").addClass("modal-music-manage"); + /* "controller" */ + { + } + modal.open(); + } + Modals.openMusicManage = openMusicManage; + function permission_controller(event_registry, bot, client) { + const error_msg = error => { + if (error instanceof CommandResult) { + if (error.id === ErrorID.PERMISSION_ERROR) { + const permission = client.permissions.resolveInfo(error.json["failed_permid"]); + return (_translations.kb1s3EbM || (_translations.kb1s3EbM = tr("failed on permission "))) + (permission ? permission.name : _translations.sJFl4SFw || (_translations.sJFl4SFw = tr("unknown"))); + } + return error.extra_message || error.message; + } + else if (typeof error === "string") + return error; + else + return _translations.UX9qUFB7 || (_translations.UX9qUFB7 = tr("command error")); + }; + { + event_registry.on("query_playlist_status", event => { + const playlist_id = bot.properties.client_playlist_id; + client.serverConnection.command_helper.request_playlist_info(playlist_id).then(result => { + event_registry.fire("playlist_status", { + status: "success", + data: { + replay_mode: result.playlist_replay_mode, + finished: result.playlist_flag_finished, + delete_played: result.playlist_flag_delete_played, + notify_song_change: bot.properties.client_flag_notify_song_change, + max_size: result.playlist_max_songs + } + }); + }).catch(error => { + event_registry.fire("playlist_status", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.FR91na9p || (_translations.FR91na9p = tr("Failed to query playlist info for playlist %d: %o")), playlist_id, error); + }); + }); + event_registry.on("set_playlist_status", event => { + const playlist_id = bot.properties.client_playlist_id; + const property_map = { + "replay_mode": "playlist_replay_mode", + "finished": "playlist_flag_finished", + "delete_played": "playlist_flag_delete_played", + "max_size": "playlist_max_songs" + }; + Promise.resolve().then(() => { + if (event.key === "notify_song_change") { + return client.serverConnection.send_command("clientedit", { + clid: bot.clientId(), + client_flag_notify_song_change: event.value + }); + } + else { + const property = property_map[event.key]; + if (!property) + return Promise.reject(_translations.cm5CVSTu || (_translations.cm5CVSTu = tr("unknown property"))); + const data = { + playlist_id: playlist_id + }; + data[property] = event.value; + return client.serverConnection.send_command("playlistedit", data); + } + }).then(() => { + event_registry.fire("set_playlist_status_result", { + status: "success", + key: event.key, + value: event.value + }); + }).catch(error => { + event_registry.fire("set_playlist_status_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.VAfsInts || (_translations.VAfsInts = tr("Failed to change playlist status %s for playlist %d: %o")), event.key, playlist_id, error); + }); + }); + event_registry.on("query_bot_status", event => { + setTimeout(() => { + event_registry.fire("bot_status", { + status: "success", + data: { + channel_commander: bot.properties.client_is_channel_commander, + volume: bot.properties.player_volume, + description: bot.properties.client_description, + default_country_code: (!bot.channelTree ? undefined : + !bot.channelTree.server ? undefined : bot.channelTree.server.properties.virtualserver_country_code) || "DE", + country_code: bot.properties.client_country, + name: bot.properties.client_nickname, + priority_speaker: bot.properties.client_is_priority_speaker, + bot_type: bot.properties.client_bot_type, + client_platform: bot.properties.client_platform, + client_version: bot.properties.client_version, + uptime_mode: bot.properties.client_uptime_mode + } + }); + }, 0); + }); + event_registry.on("set_bot_status", event => { + const property_map = { + "channel_commander": "client_is_channel_commander", + "volume": "player_volume", + "description": "client_description", + "country_code": "client_country", + "name": "client_nickname", + "priority_speaker": "client_is_priority_speaker", + "bot_type": "client_bot_type", + "client_platform": "client_platform", + "client_version": "client_version", + "uptime_mode": "client_uptime_mode" + }; + Promise.resolve().then(() => { + const property = property_map[event.key]; + if (!property) + return Promise.reject(_translations.xYJ3MH9A || (_translations.xYJ3MH9A = tr("unknown property"))); + const data = { + clid: bot.clientId() + }; + data[property] = event.value; + return client.serverConnection.send_command("clientedit", data); + }).then(() => { + event_registry.fire("set_bot_status_result", { + status: "success", + key: event.key, + value: event.value + }); + }).catch(error => { + event_registry.fire("set_bot_status_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.oWfb8Ffx || (_translations.oWfb8Ffx = tr("Failed to change bot setting %s: %o")), event.key, error); + }); + }); + } + /* permissions */ + { + event_registry.on("query_general_permissions", event => { + const playlist_id = bot.properties.client_playlist_id; + client.permissions.requestPlaylistPermissions(playlist_id).then(result => { + const permissions = {}; + for (const permission of result) + if (permission.hasValue()) + permissions[permission.type.name] = permission.value; + event_registry.fire("general_permissions", { + status: "success", + permissions: permissions + }); + }).catch(error => { + event_registry.fire("general_permissions", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.oxz3fXdx || (_translations.oxz3fXdx = tr("Failed to query playlist general permissions for playlist %d: %o")), playlist_id, error); + }); + }); + event_registry.on("set_general_permission", event => { + const playlist_id = bot.properties.client_playlist_id; + client.serverConnection.send_command("playlistaddperm", { + playlist_id: playlist_id, + permsid: event.key, + permvalue: event.value, + permskip: false, + permnegated: false + }).then(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + status: "success", + value: event.value + }); + }).catch(error => { + event_registry.fire("set_general_permission_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.YpKG3TzD || (_translations.YpKG3TzD = tr("Failed to set playlist general permissions for playlist %d and permission %d: %o")), playlist_id, event.key, error); + }); + }); + event_registry.on("query_client_permissions", event => { + const playlist_id = bot.properties.client_playlist_id; + const client_id = event.client_database_id; + client.permissions.requestPlaylistClientPermissions(playlist_id, client_id).then(result => { + const permissions = {}; + for (const permission of result) + if (permission.hasValue()) + permissions[permission.type.name] = permission.value; + event_registry.fire("client_permissions", { + status: "success", + client_database_id: event.client_database_id, + permissions: permissions + }); + }).catch(error => { + event_registry.fire("client_permissions", { + status: "error", + client_database_id: event.client_database_id, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.CpejOouC || (_translations.CpejOouC = tr("Failed to query playlist client permissions for playlist %d and client %d: %o")), playlist_id, client_id, error); + }); + }); + event_registry.on("set_client_permission", event => { + const playlist_id = bot.properties.client_playlist_id; + const client_id = event.client_database_id; + client.serverConnection.send_command("playlistclientaddperm", { + playlist_id: playlist_id, + cldbid: client_id, + permsid: event.key, + permvalue: event.value, + permskip: false, + permnegated: false + }).then(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + status: "success", + client_database_id: client_id, + value: event.value + }); + }).catch(error => { + event_registry.fire("set_client_permission_result", { + status: "error", + key: event.key, + client_database_id: client_id, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.xoWTeact || (_translations.xoWTeact = tr("Failed to set playlist client permissions for playlist %d, permission %d and client id %d: %o")), playlist_id, event.key, client_id, error); + }); + }); + event_registry.on("query_special_clients", event => { + const playlist_id = bot.properties.client_playlist_id; + client.serverConnection.command_helper.request_playlist_client_list(playlist_id).then(clients => { + return client.serverConnection.command_helper.info_from_cldbid(...clients); + }).then(clients => { + event_registry.fire("special_client_list", { + status: "success", + clients: clients.map(e => { + return { + name: e.client_nickname, + unique_id: e.client_unique_id, + database_id: e.client_database_id + }; + }) + }); + }).catch(error => { + event_registry.fire("special_client_list", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.ypBEEqy0 || (_translations.ypBEEqy0 = tr("Failed to query special client list for playlist %d: %o")), playlist_id, error); + }); + }); + event_registry.on("search_client", event => { + if (!event.text) + return; + const text = event.text; + Promise.resolve().then(() => { + let is_uuid = false; + try { + is_uuid = atob(text).length === 32; + } + catch (e) { } + if (is_uuid) { + return client.serverConnection.command_helper.info_from_uid(text); + } + else if (text.match(/^[0-9]{1,7}$/) && !isNaN(parseInt(text))) { + return client.serverConnection.command_helper.info_from_cldbid(parseInt(text)); + } + else { + //TODO: Database name lookup? + return Promise.reject("no results"); + } + }).then(result => { + if (result.length) { + const client = result[0]; + event_registry.fire("search_client_result", { + status: "success", + client: { + name: client.client_nickname, + unique_id: client.client_unique_id, + database_id: client.client_database_id + } + }); + } + else { + event_registry.fire("search_client_result", { + status: "empty" + }); + } + }).catch(error => { + event_registry.fire("search_client_result", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, _translations.KMq5sCKT || (_translations.KMq5sCKT = tr("Failed to lookup search text \"%s\": %o")), text, error); + }); + }); + event_registry.on("query_group_permissions", event => { + client.permissions.find_permission(event.permission_name).then(result => { + let groups = []; + for (const e of result) { + if (e.type !== "server_group") + continue; + const group = client.groups.serverGroup(e.group_id); + if (!group) + continue; + groups.push({ + name: group.name, + value: e.value, + id: group.id + }); + } + event_registry.fire("group_permissions", { + status: "success", + groups: groups, + permission_name: event.permission_name + }); + }).catch(error => { + event_registry.fire("group_permissions", { + status: "error", + error_msg: error_msg(error), + permission_name: event.permission_name + }); + log.error(LogCategory.CLIENT, _translations.P7lD9A4F || (_translations.P7lD9A4F = tr("Failed to execute permfind for permission %s: %o")), event.permission_name, error); + }); + }); + } + } + function dummy_controller(event_registry) { + /* settings */ + { + event_registry.on("query_bot_status", event => { + setTimeout(() => { + event_registry.fire("bot_status", { + status: "success", + data: { + name: "Another TeaSpeak bot", + country_code: "DE", + default_country_code: "GB", + channel_commander: false, + description: "Hello World", + priority_speaker: true, + volume: 66, + uptime_mode: 0, + client_version: "Version", + client_platform: "Platform", + bot_type: 0 + } + }); + }); + }); + event_registry.on("query_playlist_status", event => { + setTimeout(() => { + event_registry.fire("playlist_status", { + status: "success", + data: { + max_size: 55, + notify_song_change: true, + delete_played: false, + finished: false, + replay_mode: 2 + } + }); + }); + }); + } + /* permissions */ + { + event_registry.on("query_special_clients", event => { + setTimeout(() => { + event_registry.fire("special_client_list", { + status: "success", + clients: [{ + name: "WolverinDEV", + database_id: 1, + unique_id: "abd" + }, { + name: "WolverinDEV 2", + database_id: 2, + unique_id: "abd1" + }, { + name: "WolverinDEV 3", + database_id: 3, + unique_id: "abd1" + }] + }); + }, 0); + }); + event_registry.on("query_group_permissions", event => { + setTimeout(() => { + event_registry.fire("group_permissions", { + status: "success", + groups: [{ + value: 20, + name: "Server Admin p:20", + id: 0 + }, { + value: 10, + name: "Server Mod p:10", + id: 0 + }], + permission_name: event.permission_name + }); + }, 0); + }); + event_registry.on("query_general_permissions", event => { + setTimeout(() => { + event_registry.fire("general_permissions", { + status: "success", + permissions: { + i_playlist_song_needed_add_power: 77 + } + }); + }, 0); + }); + event_registry.on("set_general_permission", event => { + setTimeout(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + value: event.value, + status: "success" + }); + }); + }); + event_registry.on("query_client_permissions", event => { + setTimeout(() => { + event_registry.fire("client_permissions", { + client_database_id: event.client_database_id, + status: "success", + permissions: { + i_playlist_song_needed_add_power: 77 + } + }); + }, 500); + }); + event_registry.on("set_client_permission", event => { + setTimeout(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + client_database_id: event.client_database_id, + status: "success", + value: event.value + }); + }, 500); + }); + } + } + function build_modal(event_registry) { + const tag = $("#tmpl_music_manage").renderTag(); + const container_settings = tag.find(".body > .category-settings"); + build_settings_container(event_registry, container_settings); + const container_permissions = tag.find(".body > .category-permissions"); + build_permission_container(event_registry, container_permissions); + /* general switch */ + { + let shown_container; + const header = tag.find(".header"); + const category_permissions = header.find(".category-permissions"); + event_registry.on("show_container", data => { + category_permissions.toggleClass("selected", data.container === "permissions"); + container_permissions.toggleClass("hidden", data.container !== "permissions"); + }); + category_permissions.on('click', event => { + if (shown_container === "permissions") + return; + event_registry.fire("show_container", { container: "permissions" }); + }); + const category_settings = header.find(".category-settings"); + event_registry.on("show_container", data => { + category_settings.toggleClass("selected", data.container === "settings"); + container_settings.toggleClass("hidden", data.container !== "settings"); + }); + category_settings.on('click', event => { + if (shown_container === "settings") + return; + event_registry.fire("show_container", { container: "settings" }); + }); + event_registry.on("show_container", data => shown_container = data.container); + } + /* input length fix */ + tag.find("input[maxlength]").on("input", event => { + const input = event.target; + const max = parseInt(input.getAttribute("maxlength")); + const text = input.value; + if (!isNaN(max) && text && text.length > max) + //input.value = text.substr(text.length - max); + input.value = text.substr(0, max); + }); + /* initialize */ + event_registry.fire("show_container", { container: "settings" }); + return tag.children(); + } + function build_settings_container(event_registry, tag) { + const show_change_error = (header, message) => { + createErrorModal(_translations.xlYmaITB || (_translations.xlYmaITB = tr("Failed to change value")), header + "
" + message).open(); + }; + /* music bot settings */ + { + const container = tag.find(".settings-bot"); + /* bot name */ + { + const input = container.find(".option-bot-name"); + let last_value = undefined; + event_registry.on("query_bot_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", _translations.T0lvDV9H || (_translations.T0lvDV9H = tr("loading..."))); + }); + event_registry.on("bot_status", event => { + if (event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", event.error_msg || (_translations.c8dSeLZS || (_translations.c8dSeLZS = tr("error while loading")))); + else + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value = event.data.name); + }); + event_registry.on("set_bot_status_result", event => { + if (event.key !== "name") + return; + if (event.status !== "success") + show_change_error(_translations.ck4GDnos || (_translations.ck4GDnos = tr("Failed to set bot name")), event.error_msg || (_translations.ds03qJjo || (_translations.ds03qJjo = tr("timeout")))); + else + last_value = event.value; + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value); + }); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val(); + if (value === last_value) + return; + if (!value) { + input.val(last_value); + return; + } + input + .prop("disabled", true) + .val(null) + .attr("placeholder", _translations.whmoRxKI || (_translations.whmoRxKI = tr("applying..."))); + event_registry.fire("set_bot_status", { + key: "name", + value: value + }); + }); + } + /* country flag */ + { + const input = container.find(".option-bot-country"); + const flag = container.find(".container-country .country"); + let last_value = undefined, fallback_country = undefined; + const update_country_code = input => { + input = input || fallback_country || "ts"; + flag.each((_, e) => { + for (const [index, klass] of e.classList.entries()) + if (klass.startsWith("flag-")) + e.classList.remove(klass); + }); + flag.addClass("flag-" + input.toLowerCase()); + flag.attr("title", i18n.country_name(input, _translations.TDfuhMFl || (_translations.TDfuhMFl = tr("Unknown country")))); + }; + event_registry.on("query_bot_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "..."); + update_country_code("ts"); + }); + event_registry.on("bot_status", event => { + if (event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "err"); + else { + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value = event.data.country_code); + fallback_country = event.data.default_country_code; + } + update_country_code(last_value); + }); + event_registry.on("set_bot_status_result", event => { + if (event.key !== "country_code") + return; + if (event.status !== "success") + show_change_error(_translations.Wba6WIJI || (_translations.Wba6WIJI = tr("Failed to set bots country")), event.error_msg || (_translations.KegIP_Oa || (_translations.KegIP_Oa = tr("timeout")))); + else + last_value = event.value; + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value); + update_country_code(last_value); + }); + input.on("input", () => { + update_country_code(input.val()); + input.firstParent(".input-boxed").removeClass("is-invalid"); + }); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val(); + if (value === last_value) + return; + if (value && value.length != 2) { + input.firstParent(".input-boxed").addClass("is-invalid"); + return; + } + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "..."); + event_registry.fire("set_bot_status", { + key: "country_code", + value: value + }); + }); + } + /* flag channel commander */ + { + const input = container.find(".option-channel-commander"); + const label = input.parents("label"); + let last_value = undefined; + event_registry.on("query_bot_status", event => { + last_value = undefined; + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + event_registry.on("bot_status", event => { + if (event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } + else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.channel_commander) + .prop("disabled", false); + } + }); + event_registry.on("set_bot_status_result", event => { + if (event.key !== "channel_commander") + return; + if (event.status !== "success") + show_change_error(_translations.TJs6MrB8 || (_translations.TJs6MrB8 = tr("Failed to change channel commander state")), event.error_msg || (_translations.V8NKF3zP || (_translations.V8NKF3zP = tr("timeout")))); + else + last_value = event.value; + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_bot_status", { + key: "channel_commander", + value: input.prop("checked") + }); + }); + } + /* flag priority speaker */ + { + const input = container.find(".option-priority-speaker"); + const label = input.parents("label"); + let last_value = undefined; + event_registry.on("query_bot_status", event => { + last_value = undefined; + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + event_registry.on("bot_status", event => { + if (event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } + else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.priority_speaker) + .prop("disabled", false); + } + }); + event_registry.on("set_bot_status_result", event => { + if (event.key !== "priority_speaker") + return; + if (event.status !== "success") + show_change_error(_translations.NUwpilKf || (_translations.NUwpilKf = tr("Failed to change priority speaker state")), event.error_msg || (_translations.WRDVagVX || (_translations.WRDVagVX = tr("timeout")))); + else + last_value = event.value; + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_bot_status", { + key: "priority_speaker", + value: input.prop("checked") + }); + }); + } + /* status load timeout */ + { + let timeout; + event_registry.on("query_bot_status", event => { + timeout = setTimeout(() => { + event_registry.fire("bot_status", { + status: "error", + error_msg: _translations.q_kBtt47 || (_translations.q_kBtt47 = tr("load timeout")) + }); + }, 5000); + }); + event_registry.on("bot_status", event => clearTimeout(timeout)); + } + /* set status timeout */ + { + let timeouts = {}; + event_registry.on("set_bot_status", event => { + clearTimeout(timeouts[event.key]); + timeouts[event.key] = setTimeout(() => { + event_registry.fire("set_bot_status_result", { + status: "timeout", + key: event.key, + }); + }, 5000); + }); + event_registry.on("set_bot_status_result", event => { + clearTimeout(timeouts[event.key]); + delete timeouts[event.key]; + }); + } + } + /* music bot settings */ + { + const container = tag.find(".settings-playlist"); + /* playlist replay mode */ + { + const input = container.find(".option-replay-mode"); + let last_value = undefined; + const update_value = text => { + if (text) { + input.prop("disabled", true).addClass("disabled"); + input.val("-1"); + input.find("option[value=-1]").text(text); + } + else if (last_value >= 0 && last_value <= 3) { + input + .prop("disabled", false) + .removeClass("disabled"); + input.val(last_value); + } + else { + update_value(_translations.gPVZM1r8 || (_translations.gPVZM1r8 = tr("invalid value"))); + } + }; + event_registry.on("query_playlist_status", event => { + last_value = undefined; + update_value(_translations.lczFny7X || (_translations.lczFny7X = tr("loading..."))); + }); + event_registry.on("playlist_status", event => { + if (event.status === "error") { + update_value(event.error_msg || (_translations.Mk7tMQ1m || (_translations.Mk7tMQ1m = tr("error while loading")))); + } + else { + last_value = event.data.replay_mode; + update_value(undefined); + } + }); + event_registry.on("set_playlist_status_result", event => { + if (event.key !== "replay_mode") + return; + if (event.status !== "success") + show_change_error(_translations.uQ1yO6Yi || (_translations.uQ1yO6Yi = tr("Failed to change replay mode")), event.error_msg || (_translations.DJ1pfloN || (_translations.DJ1pfloN = tr("timeout")))); + else + last_value = event.value; + update_value(undefined); + }); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("change", event => { + const value = parseInt(input.val()); + console.log(value); + if (isNaN(value)) + return; + update_value(_translations.pRpmtFjG || (_translations.pRpmtFjG = tr("applying..."))); + event_registry.fire("set_playlist_status", { + key: "replay_mode", + value: value + }); + }); + } + /* playlist max size */ + { + const input = container.find(".container-max-playlist-size input"); + let last_value = undefined; + event_registry.on("query_playlist_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", _translations._jdzYHNZ || (_translations._jdzYHNZ = tr("loading..."))) + .firstParent(".input-boxed").addClass("disabled"); + }); + event_registry.on("playlist_status", event => { + if (event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", event.error_msg || (_translations.q5PwTk4Y || (_translations.q5PwTk4Y = tr("error while loading")))) + .firstParent(".input-boxed").addClass("disabled"); + else + input + .prop("disabled", false) + .attr("placeholder", null) + .val((last_value = event.data.max_size).toString()) + .firstParent(".input-boxed").removeClass("disabled"); + }); + event_registry.on("set_playlist_status_result", event => { + if (event.key !== "max_size") + return; + if (event.status !== "success") + show_change_error(_translations.qokDUQla || (_translations.qokDUQla = tr("Failed to change max playlist size")), event.error_msg || (_translations.yuB8OE8e || (_translations.yuB8OE8e = tr("timeout")))); + else + last_value = event.value; + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value) + .firstParent(".input-boxed").removeClass("disabled"); + }); + input.on("input", event => input.parentsUntil(".input-boxed").removeClass("is-invalid")); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val(); + if (value === last_value) + return; + if (value === "") { + input.val(last_value); + return; + } + if (isNaN(parseInt(value))) { + input.parentsUntil(".input-boxed").addClass("is-invalid"); + return; + } + input + .prop("disabled", true) + .val(null) + .attr("placeholder", _translations.h2kycEoy || (_translations.h2kycEoy = tr("applying..."))) + .firstParent(".input-boxed").addClass("disabled"); + event_registry.fire("set_playlist_status", { + key: "max_size", + value: parseInt(value) + }); + }); + } + /* flag delete played */ + { + const input = container.find(".option-delete-played-songs"); + const label = input.parents("label"); + let last_value = undefined; + event_registry.on("query_playlist_status", event => { + last_value = undefined; + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + event_registry.on("playlist_status", event => { + if (event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } + else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.delete_played) + .prop("disabled", false); + } + }); + event_registry.on("set_playlist_status_result", event => { + if (event.key !== "delete_played") + return; + if (event.status !== "success") + show_change_error(_translations.sTxLZaJY || (_translations.sTxLZaJY = tr("Failed to change delete state")), event.error_msg || (_translations.WQE2CZa2 || (_translations.WQE2CZa2 = tr("timeout")))); + else + last_value = event.value; + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_playlist_status", { + key: "delete_played", + value: input.prop("checked") + }); + }); + } + /* flag notify song change */ + { + const input = container.find(".option-notify-songs-change"); + const label = input.parents("label"); + let last_value = undefined; + event_registry.on("query_playlist_status", event => { + last_value = undefined; + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + event_registry.on("playlist_status", event => { + if (event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } + else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.notify_song_change) + .prop("disabled", false); + } + }); + event_registry.on("set_playlist_status_result", event => { + if (event.key !== "notify_song_change") + return; + if (event.status !== "success") + show_change_error(_translations.VYtn59Ga || (_translations.VYtn59Ga = tr("Failed to change notify state")), event.error_msg || (_translations.YUZUVDR4 || (_translations.YUZUVDR4 = tr("timeout")))); + else + last_value = event.value; + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_playlist_status", { + key: "notify_song_change", + value: input.prop("checked") + }); + }); + } + /* status load timeout */ + { + let timeout; + event_registry.on("query_playlist_status", event => { + timeout = setTimeout(() => { + event_registry.fire("playlist_status", { + status: "error", + error_msg: _translations.xp_ufUbh || (_translations.xp_ufUbh = tr("load timeout")) + }); + }, 5000); + }); + event_registry.on("playlist_status", event => clearTimeout(timeout)); + } + /* set status timeout */ + { + let timeouts = {}; + event_registry.on("set_playlist_status", event => { + clearTimeout(timeouts[event.key]); + timeouts[event.key] = setTimeout(() => { + event_registry.fire("set_playlist_status_result", { + status: "timeout", + key: event.key, + }); + }, 5000); + }); + event_registry.on("set_playlist_status_result", event => { + clearTimeout(timeouts[event.key]); + delete timeouts[event.key]; + }); + } + } + /* reload button */ + { + const button = tag.find(".button-reload"); + let timeout; + event_registry.on(["query_bot_status", "query_playlist_status"], event => { + button.prop("disabled", true); + clearTimeout(timeout); + timeout = setTimeout(() => { + button.prop("disabled", false); + }, 1000); + }); + button.on("click", event => { + event_registry.fire("query_bot_status"); + event_registry.fire("query_playlist_status"); + }); + } + tooltip(tag); + /* initialize on show */ + { + let initialized = false; + event_registry.on("show_container", event => { + if (event.container !== "settings" || initialized) + return; + initialized = true; + event_registry.fire("query_bot_status"); + event_registry.fire("query_playlist_status"); + }); + } + } + function build_permission_container(event_registry, tag) { + /* client search mechanism */ + { + const container = tag.find(".table-head .column-client-specific .client-select"); + let list_shown = false; + /* search list show/hide */ + { + const button_list_clients = container.find(".button-list-clients"); + button_list_clients.on('click', event => event_registry.fire(list_shown ? "hide_client_list" : "show_client_list")); + event_registry.on("show_client_list", () => { + list_shown = true; + button_list_clients.text(_translations.chknDAXh || (_translations.chknDAXh = tr("Hide clients"))); + }); + event_registry.on("hide_client_list", () => { + list_shown = false; + button_list_clients.text(_translations.n75WL7_X || (_translations.n75WL7_X = tr("List clients"))); + }); + } + /* the search box */ + { + const input_search = container.find(".input-search"); + const button_search = container.find(".button-search"); + let search_timeout; + let last_query; + input_search.on('keyup', event => { + const text = input_search.val(); + if (text === last_query) + return; + if (text) + event_registry.fire("filter_client_list", { filter: text }); + else + event_registry.fire("filter_client_list", { filter: undefined }); + input_search.toggleClass("is-invalid", !list_shown && text === last_query); + if (!list_shown) { + button_search.prop("disabled", !text || !!search_timeout); + } + else { + last_query = text; + } + }); + input_search.on('keydown', event => { + if (event.key === "Enter" && !list_shown && !button_search.prop("disabled")) + button_search.trigger("click"); + }); + event_registry.on("show_client_list", () => { + button_search.prop("disabled", true); + input_search.attr("placeholder", _translations.ac3ohRnJ || (_translations.ac3ohRnJ = tr("Search client list"))); + }); + event_registry.on("hide_client_list", () => { + button_search.prop("disabled", !input_search.val() || !!search_timeout); + input_search.attr("placeholder", _translations.rRXCJPGM || (_translations.rRXCJPGM = tr("Client uid or database id"))); + }); + button_search.on("click", event => { + button_search.prop("disabled", true); + input_search.blur(); + const text = input_search.val(); + last_query = text; + event_registry.fire("search_client", { + text: text + }); + search_timeout = setTimeout(() => event_registry.fire("search_client_result", { + status: "timeout" + }), 5000); + }); + event_registry.on("search_client_result", event => { + clearTimeout(search_timeout); + search_timeout = 0; + button_search.prop("disabled", !input_search.val()); + if (event.status === "timeout") { + createErrorModal(_translations.H7Wzuyk5 || (_translations.H7Wzuyk5 = tr("Client search failed")), _translations.Jl44qY0M || (_translations.Jl44qY0M = tr("Failed to perform client search.
Search resulted in a timeout."))).open(); + return; + } + else if (event.status === "error" || event.status === "empty") { + //TODO: Display the error somehow? + input_search.addClass("is-invalid"); + return; + } + else { + event_registry.fire("special_client_set", { + client: event.client + }); + } + }); + } + /* the client list */ + { + const container = tag.find(".overlay-client-list"); + event_registry.on("show_client_list", () => container.removeClass("hidden")); + event_registry.on("hide_client_list", () => container.addClass("hidden")); + const button_refresh = container.find(".button-clientlist-refresh"); + const container_entries = container.find(".container-client-list"); + event_registry.on("special_client_list", data => { + button_refresh.prop("disabled", false); + container.find(".overlay").addClass("hidden"); + if (data.status === "error-permission") { + const overlay = container.find(".overlay-query-error-permissions"); + overlay.find("a").text(_translations.dtLjMQff || (_translations.dtLjMQff = tr("Insufficient permissions"))); + overlay.removeClass("hidden"); + } + else if (data.status === "success") { + container_entries.find(".client").remove(); /* clear */ + if (!data.clients.length) { + const overlay = container.find(".overlay-empty-list"); + overlay.removeClass("hidden"); + } + else { + for (const client of data.clients) { + const tag = $.spawn("div").addClass("client").append(htmltags.generate_client_object({ + add_braces: false, + client_id: 0, + client_database_id: client.database_id, + client_name: client.name, + client_unique_id: client.unique_id + })); + tag.on('dblclick', event => event_registry.fire("special_client_set", { client: client })); + tag.attr("x-filter", client.database_id + "_" + client.name + "_" + client.unique_id); + container_entries.append(tag); + } + } + } + else { + const overlay = container.find(".overlay-query-error"); + overlay.find("a").text(data.error_msg ? data.error_msg : _translations.xeMEtAeJ || (_translations.xeMEtAeJ = tr("query failed"))); + overlay.removeClass("hidden"); + } + }); + /* refresh button */ + button_refresh.on('click', event => { + button_refresh.prop("disabled", true); + event_registry.fire("query_special_clients"); + }); + /* special client list query timeout handler */ + { + let query_timeout; + event_registry.on("query_special_clients", event => { + query_timeout = setTimeout(() => { + event_registry.fire("special_client_list", { + status: "error", + error_msg: _translations.Q3EsnBCT || (_translations.Q3EsnBCT = tr("Query timeout")) + }); + }, 5000); + }); + event_registry.on("special_client_list", event => clearTimeout(query_timeout)); + } + /* first time client list show */ + { + let shown; + event_registry.on('show_client_list', event => { + if (shown) + return; + shown = true; + event_registry.fire("query_special_clients"); + }); + } + /* the client list filter */ + { + let filter; + const overlay = container.find(".overlay-filter-no-result"); + const update_filter = () => { + let shown = 0, hidden = 0; + container_entries.find(".client").each(function () { + const text = this.getAttribute("x-filter"); + if (!filter || text.toLowerCase().indexOf(filter) != -1) { + this.classList.remove("hidden"); + shown++; + } + else { + this.classList.add("hidden"); + hidden++; + } + }); + if (shown == 0 && hidden == 0) + return; + overlay.toggleClass("hidden", shown != 0); + }; + event_registry.on("special_client_list", event => update_filter()); + event_registry.on("filter_client_list", event => { + filter = (event.filter || "").toLowerCase(); + update_filter(); + }); + } + } + event_registry.on("special_client_set", event => { + container.toggleClass("hidden", !!event.client); + event_registry.fire("hide_client_list"); + }); + } + /* the client info */ + { + const container = tag.find(".table-head .column-client-specific .client-info"); + container.find(".button-client-deselect").on("click", event => { + event_registry.fire("special_client_set", { client: undefined }); + }); + event_registry.on("special_client_set", event => { + container.toggleClass("hidden", !event.client); + const client_container = container.find(".container-selected-client"); + client_container.find(".htmltag-client").remove(); + if (event.client) { + client_container.append(htmltags.generate_client_object({ + client_unique_id: event.client.unique_id, + client_name: event.client.name, + client_id: 0, + client_database_id: event.client.database_id, + add_braces: false + })); + } + }); + } + const power_needed_map = { + i_client_music_rename_power: "i_client_music_needed_rename_power", + i_client_music_modify_power: "i_client_music_needed_modify_power", + i_client_music_delete_power: "i_client_music_needed_delete_power", + i_playlist_view_power: "i_playlist_needed_view_power", + i_playlist_modify_power: "i_playlist_needed_modify_power", + i_playlist_permission_modify_power: "i_playlist_needed_permission_modify_power", + i_playlist_song_add_power: "i_playlist_song_needed_add_power", + i_playlist_song_move_power: "i_playlist_song_needed_move_power", + i_playlist_song_remove_power: "i_playlist_song_needed_remove_power", + b_virtualserver_playlist_permission_list: "b_virtualserver_playlist_permission_list" + }; + const needed_power_map = Object.entries(power_needed_map).reduce((ret, entry) => { + const [key, value] = entry; + ret[value] = key; + return ret; + }, {}); + /* general permissions */ + { + /* permission input functionality */ + { + tag.find(".general-permission").each((_, _e) => { + const elem = $(_e); + const permission_name = elem.attr("x-permission"); + if (!permission_name) + return; + const input = elem.find("input"); + input.attr("maxlength", 6); + let last_sync_value = undefined; + event_registry.on("query_general_permissions", event => { + input.prop("disabled", true).val(null); + input.attr("placeholder", _translations.oN5z34tB || (_translations.oN5z34tB = tr("loading..."))); + }); + event_registry.on("general_permissions", event => { + input.prop("disabled", true).val(null); + if (event.status === "timeout") { + input.attr("placeholder", _translations.rriL0uvS || (_translations.rriL0uvS = tr("load timeout"))); + } + else if (event.status === "success") { + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + const value = event.permissions ? event.permissions[permission_name] || 0 : 0; + last_sync_value = value; + input.val(value); + } + else { + input.attr("placeholder", event.error_msg || (_translations.NMxty5e5 || (_translations.NMxty5e5 = tr("load error")))); + } + }); + event_registry.on("set_general_permission_result", event => { + if (event.key !== permission_name) + return; + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + if (event.status === "success") { + input.val(event.value); + last_sync_value = event.value; + } + else if (event.status === "error") { + if (typeof last_sync_value === "number") + input.val(last_sync_value); + createErrorModal(_translations.v50TMxti || (_translations.v50TMxti = tr("Failed to change permission")), tra("Failed to change permission:{:br:}{}", event.error_msg)).open(); + } + }); + input.on("focusout", event => { + if (input.prop("disabled")) + return; + const value = parseInt(input.val()); + if (value === last_sync_value) + return; + input.prop("disabled", true).val(null); + input.attr("placeholder", _translations.bjvee5Ts || (_translations.bjvee5Ts = tr("applying..."))); + event_registry.fire("set_general_permission", { + key: permission_name, + value: value || 0 + }); + }); + input.on("keyup", event => event.key === "Enter" && input.blur()); + }); + } + /* the tooltip functionality */ + { + tag.find(".general-permission").each((_, _e) => { + const elem = $(_e); + const permission_name = elem.attr("x-permission"); + if (!permission_name) + return; + const required_power = needed_power_map[permission_name]; + if (!required_power) + return; + let last_sync_value = undefined; + let current_tag; + let loading = false; + let query_result; + event_registry.on("general_permissions", event => { + if (event.status === "success") + last_sync_value = event.permissions ? event.permissions[permission_name] || 0 : 0; + }); + event_registry.on("set_general_permission_result", event => { + if (event.key !== permission_name) + return; + if (event.status === "success") + last_sync_value = event.value; + }); + event_registry.on("refresh_permissions", event => { + query_result = undefined; /* require for the next time */ + }); + const show_query_result = () => { + if (!current_tag) + return; + const container_groups = current_tag.find(".container-groups"); + container_groups.children().remove(); + current_tag.find(".container-status").addClass("hidden"); + if (loading) { + current_tag.find(".status-loading").removeClass("hidden"); + } + else if (!query_result || query_result.status === "error") { + current_tag + .find(".status-error").removeClass("hidden") + .text((query_result ? query_result.error_msg : "") || (_translations.uaoGscii || (_translations.uaoGscii = tr("failed to query data")))); + } + else if (query_result.status === "timeout") { + current_tag + .find(".status-error").removeClass("hidden") + .text(_translations.qZHZIWWT || (_translations.qZHZIWWT = tr("timeout while loading"))); + } + else { + let count = 0; + for (const group of (query_result.groups || [])) { + if (group.value !== -1 && group.value < last_sync_value) + continue; + count++; + container_groups.append($.spawn("div").addClass("group").text(" - " + group.name + " (" + group.id + ")")); + } + if (count === 0) + current_tag.find(".status-no-groups").removeClass("hidden"); + } + }; + tooltip.initialize(elem, { + on_show(tag) { + current_tag = tag; + if (!query_result && !loading) { + event_registry.fire("query_group_permissions", { + permission_name: required_power + }); + loading = true; + } + show_query_result(); + }, + on_hide(tag) { + current_tag = undefined; + } + }); + event_registry.on("group_permissions", event => { + if (event.permission_name !== required_power) + return; + loading = false; + query_result = event; + show_query_result(); + }); + }); + /* refresh mechanism */ + { + event_registry.on("refresh_permissions", event => event_registry.fire("query_general_permissions")); + } + } + /* permission set timeout */ + { + let permission_timers = {}; + event_registry.on("set_general_permission", event => { + if (permission_timers[event.key]) + clearTimeout(permission_timers[event.key]); + permission_timers[event.key] = setTimeout(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + status: "error", + error_msg: _translations.WA9xLJOj || (_translations.WA9xLJOj = tr("controller timeout")) + }); + }, 5000); + }); + event_registry.on("set_general_permission_result", event => { + clearTimeout(permission_timers[event.key]); + delete permission_timers[event.key]; + }); + } + /* group query timeout */ + { + let timers = {}; + event_registry.on("query_group_permissions", event => { + if (timers[event.permission_name]) + clearTimeout(timers[event.permission_name]); + timers[event.permission_name] = setTimeout(() => { + event_registry.fire("group_permissions", { + permission_name: event.permission_name, + status: "timeout" + }); + }, 5000); + }); + event_registry.on("group_permissions", event => { + clearTimeout(timers[event.permission_name]); + delete timers[event.permission_name]; + }); + } + /* query timeout */ + { + let query_timeout; + event_registry.on("query_general_permissions", event => { + clearTimeout(query_timeout); + query_timeout = setTimeout(() => { + event_registry.fire("general_permissions", { + status: "timeout" + }); + }, 5000); + }); + event_registry.on("general_permissions", event => clearTimeout(query_timeout)); + } + /* refresh button */ + { + const button = tag.find(".button-permission-refresh"); + let refresh_timer; + let loading_client_permissions = false; + let loading_general_permissions = false; + const update_button = () => button.prop("disabled", refresh_timer || loading_client_permissions || loading_general_permissions); + event_registry.on("query_general_permissions", event => { + loading_general_permissions = true; + update_button(); + }); + event_registry.on("general_permissions", event => { + loading_general_permissions = false; + update_button(); + }); + event_registry.on("query_client_permissions", event => { + loading_client_permissions = true; + update_button(); + }); + event_registry.on("client_permissions", event => { + loading_client_permissions = false; + update_button(); + }); + button.on('click', event => { + event_registry.fire("refresh_permissions"); + /* allow refreshes only every second */ + refresh_timer = setTimeout(() => { + refresh_timer = undefined; + update_button(); + }, 1000); + }); + } + } + /* client specific permissions */ + { + const container = tag.find(".column-client-specific"); + let client_database_id = 0; + let needed_permissions = {}; + /* needed permissions updater */ + { + event_registry.on("general_permissions", event => { + if (event.status !== "success") + return; + needed_permissions = event.permissions; + }); + event_registry.on("set_general_permission_result", event => { + if (event.status !== "success") + return; + needed_permissions[event.key] = event.value; + }); + } + event_registry.on("special_client_set", event => { + client_database_id = event.client ? event.client.database_id : 0; + container.find(".client-permission").toggleClass("hidden", !event.client); + if (client_database_id) + event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); + }); + const enabled_class = "client-apply"; + const disabled_class = "client-delete"; + container.find(".client-permission").each((_, _e) => { + const elem = $(_e); + const input = elem.find("input"); + const status_indicator = elem.find(".icon_em"); + const permission_name = elem.attr("x-permission"); + const permission_needed_name = power_needed_map[permission_name]; + let last_sync_value = undefined; + let hide_indicator = false; + if (typeof permission_needed_name !== "string") { + log.warn(LogCategory.GENERAL, _translations.QrA6IXLw || (_translations.QrA6IXLw = tr("Missing permission needed mapping for %s")), permission_name); + return; + } + const update_indicator = () => { + const value = parseInt(input.val()); + const needed = typeof needed_permissions[permission_needed_name] === "number" ? needed_permissions[permission_needed_name] : 0; + const flag = value == -1 ? true : isNaN(value) || value == 0 ? false : value >= needed; + status_indicator.toggle(!hide_indicator); + status_indicator.toggleClass(enabled_class, flag).toggleClass(disabled_class, !flag); + }; + event_registry.on("special_client_set", event => { + last_sync_value = undefined; + }); + event_registry.on("general_permissions", event => update_indicator()); + event_registry.on("set_general_permission_result", event => { + if (event.key !== permission_needed_name) + return; + if (event.status !== "success") + return; + update_indicator(); + }); + /* loading the permission */ + event_registry.on("query_client_permissions", event => { + if (event.client_database_id !== client_database_id) + return; + last_sync_value = undefined; + hide_indicator = true; + input.prop("disabled", true).val(null); + input.attr("placeholder", _translations.vXhgxTE9 || (_translations.vXhgxTE9 = tr("loading..."))); + update_indicator(); + }); + event_registry.on('client_permissions', event => { + if (event.client_database_id !== client_database_id) + return; + hide_indicator = false; + input.prop("disabled", true).val(null); + if (event.status === "timeout") { + input.attr("placeholder", _translations.qTOaE8kn || (_translations.qTOaE8kn = tr("load timeout"))); + } + else if (event.status === "success") { + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + const value = event.permissions ? event.permissions[permission_name] || 0 : 0; + last_sync_value = value; + input.val(value); + } + else { + input.attr("placeholder", event.error_msg || (_translations.W6NSppXR || (_translations.W6NSppXR = tr("load error")))); + } + update_indicator(); + }); + /* permission editing */ + input.attr("maxlength", 6); + input.on("focusout", event => { + if (!client_database_id) + return; + const value = parseInt(input.val()); + if (value === last_sync_value) + return; + input.prop("disabled", true).val(null); + input.attr("placeholder", _translations.NRqLeDXD || (_translations.NRqLeDXD = tr("applying..."))); + event_registry.fire("set_client_permission", { + client_database_id: client_database_id, + key: permission_name, + value: value || 0 + }); + hide_indicator = true; + update_indicator(); + }); + input.on("change", () => update_indicator()); + input.on("keyup", event => event.key === "Enter" && input.blur()); + event_registry.on("set_client_permission_result", event => { + if (event.key !== permission_name) + return; + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + if (event.status === "success") { + input.val(event.value); + last_sync_value = event.value; + } + else if (event.status === "error") { + if (typeof last_sync_value === "number") + input.val(last_sync_value); + createErrorModal(_translations.IezxIp9G || (_translations.IezxIp9G = tr("Failed to change permission")), tra("Failed to change permission:{:br:}{}", event.error_msg)).open(); + } + hide_indicator = false; + update_indicator(); + }); + }); + /* client permission query timeout */ + { + let timeout = {}; + event_registry.on("query_client_permissions", event => { + if (timeout[event.client_database_id]) + clearTimeout(timeout[event.client_database_id]); + timeout[event.client_database_id] = setTimeout(() => { + event_registry.fire("client_permissions", { + status: "timeout", + client_database_id: event.client_database_id + }); + }, 5000); + }); + event_registry.on("client_permissions", event => { + clearTimeout(timeout[event.client_database_id]); + }); + } + /* client permission set timeout */ + { + let timeout = {}; + event_registry.on("set_client_permission", event => { + const key = event.client_database_id + "_" + event.key; + if (timeout[key]) + clearTimeout(timeout[key]); + timeout[key] = setTimeout(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + status: "error", + client_database_id: event.client_database_id, + error_msg: _translations.TcN1P2qm || (_translations.TcN1P2qm = tr("timeout")) + }); + }, 5000); + }); + event_registry.on("set_client_permission_result", event => { + const key = event.client_database_id + "_" + event.key; + if (timeout[key]) { + clearTimeout(timeout[key]); + delete timeout[key]; + } + }); + } + event_registry.on("refresh_permissions", event => { + if (client_database_id) + event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); + }); + tooltip(container); + } + /* a title attribute for permission column */ + tag.find(".table-body .column-permission a").each(function () { + this.setAttribute("title", this.textContent); + }); + /* initialize on show */ + { + let initialized = false; + event_registry.on("show_container", event => { + if (event.container !== "permissions" || initialized) + return; + initialized = true; + event_registry.fire("special_client_set", { client: undefined }); + event_registry.fire("query_general_permissions", {}); + }); + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["bb89811e744e5d317f61f537cddef6033b386003838e9d5b5605e65af1c35b7d"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["bb89811e744e5d317f61f537cddef6033b386003838e9d5b5605e65af1c35b7d"] = "bb89811e744e5d317f61f537cddef6033b386003838e9d5b5605e65af1c35b7d"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "bTUbzE93", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (46,35)" }, { name: "eXHtaUUE", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (46,56)" }, { name: "CbxHqsI9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (96,65)" }, { name: "i_CcdmUJ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (96,83)" }, { name: "MFoo4HUQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (97,65)" }, { name: "EOvfKbzY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (97,83)" }, { name: "lLkLuT23", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (386,39)" }, { name: "K0mUVYLm", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (391,65)" }, { name: "Fqmae3Ke", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (399,65)" }, { name: "TWiMcMlr", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (408,65)" }, { name: "dqF7V4bv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (409,71)" }, { name: "oqlWN8_x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalNewcomer.ts (415,39)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + const next_step = { + "welcome": "microphone", + //"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */ + "microphone": "identity", + "speaker": "identity", + "identity": "finish" + }; + const last_step = (() => { + const result = {}; + for (const key of Object.keys(next_step)) + if (!result[next_step[key]]) + result[next_step[key]] = key; + return result; + })(); + function openModalNewcomer() { + let modal = createModal({ + header: tra("Welcome to the {}", app.is_web() ? "TeaSpeak - Web client" : "TeaSpeak - Client"), + body: () => $("#tmpl_newcomer").renderTag({ + is_web: app.is_web() + }).children(), + footer: null, + width: "", + closeable: false + }); + const event_registry = new events.Registry(); + event_registry.enable_debug("newcomer"); + modal.htmlTag.find(".modal-body").addClass("modal-newcomer"); + initializeBasicFunctionality(modal.htmlTag, event_registry); + initializeStepWelcome(modal.htmlTag.find(".container-body .step.step-welcome"), event_registry); + initializeStepIdentity(modal.htmlTag.find(".container-body .step.step-identity"), event_registry); + initializeStepMicrophone(modal.htmlTag.find(".container-body .step.step-microphone"), event_registry, modal); + initializeStepFinish(modal.htmlTag.find(".container-body .step.step-finish"), event_registry); + event_registry.on("exit_guide", event => { + if (event.ask_yesno) + Modals.spawnYesNo(_translations.bTUbzE93 || (_translations.bTUbzE93 = tr("Are you sure?")), _translations.eXHtaUUE || (_translations.eXHtaUUE = tr("Do you really want to skip the basic setup guide?")), result => { + if (result) + event_registry.fire("exit_guide", { ask_yesno: false }); + }); + else + modal.close(); + }); + event_registry.fire("show_step", { step: "welcome" }); + modal.open(); + event_registry.fire_async("modal-shown"); + return modal; + } + Modals.openModalNewcomer = openModalNewcomer; + function initializeBasicFunctionality(tag, event_registry) { + const container_header = tag.find(".container-header"); + const tag_body = tag.find(".container-body .body"); + /* step navigation */ + event_registry.on("show_step", event => { + tag_body.find(".step").addClass("hidden"); + container_header.find(".step").addClass("hidden"); + tag_body.find(".step.step-" + event.step).removeClass("hidden"); + container_header.find(".step.step-" + event.step).removeClass("hidden"); + }); + /* button controller */ + { + const buttons = tag.find(".buttons"); + const button_last_step = buttons.find(".button-last-step"); + const button_next_step = buttons.find(".button-next-step"); + button_last_step.on('click', event => { + if (last_step[current_step]) + event_registry.fire("show_step", { step: last_step[current_step] }); + else + event_registry.fire("exit_guide", { ask_yesno: true }); + }); + let current_step; + button_next_step.on('click', event => { + if (next_step[current_step]) + event_registry.fire("show_step", { step: next_step[current_step] }); + else + event_registry.fire("exit_guide", { ask_yesno: false }); + }); + event_registry.on("show_step", event => { + current_step = event.step; + button_next_step.text(next_step[current_step] ? _translations.CbxHqsI9 || (_translations.CbxHqsI9 = tr("Next step")) : _translations.i_CcdmUJ || (_translations.i_CcdmUJ = tr("Finish guide"))); + button_last_step.text(last_step[current_step] ? _translations.MFoo4HUQ || (_translations.MFoo4HUQ = tr("Last step")) : _translations.EOvfKbzY || (_translations.EOvfKbzY = tr("Skip guide"))); + }); + event_registry.on("show_step", event => button_next_step.prop("disabled", true)); + event_registry.on("show_step", event => button_last_step.prop("disabled", true)); + event_registry.on("step-status", event => button_next_step.prop("disabled", !event.next_button)); + event_registry.on("step-status", event => button_last_step.prop("disabled", !event.previous_button)); + } + } + function initializeStepWelcome(tag, event_registry) { + event_registry.on("show_step", e => { + if (e.step !== "welcome") + return; + event_registry.fire_async("step-status", { next_button: true, previous_button: true }); + }); + } + function initializeStepFinish(tag, event_registry) { + event_registry.on("show_step", e => { + if (e.step !== "finish") + return; + event_registry.fire_async("step-status", { next_button: true, previous_button: true }); + }); + } + function initializeStepIdentity(tag, event_registry) { + const profile_events = new events.Registry(); + profile_events.enable_debug("settings-identity"); + Modals.modal_settings.initialize_identity_profiles_controller(profile_events); + Modals.modal_settings.initialize_identity_profiles_view(tag, profile_events, { forum_setuppable: false }); + let step_shown = false; + let help_animation_done = false; + const profiles_valid = () => profiles.profiles().findIndex(e => e.valid()) !== -1; + const update_step_status = () => { + event_registry.fire_async("step-status", { next_button: help_animation_done && profiles_valid(), previous_button: help_animation_done }); + }; + profile_events.on("query-profile-validity-result", event => step_shown && event.status === "success" && event.valid && update_step_status()); + event_registry.on("show_step", e => { + step_shown = e.step === "identity"; + if (!step_shown) + return; + update_step_status(); + }); + /* the help sequence */ + { + const container = tag.find(".container-settings-identity-profile"); + const container_help_text = tag.find(".container-help-text"); + const container_profile_list = tag.find(".highlight-profile-list"); + const container_profile_settings = tag.find(".highlight-profile-settings"); + const container_identity_settings = tag.find(".highlight-identity-settings"); + let is_first_show = true; + event_registry.on("show_step", event => { + if (!is_first_show || event.step !== "identity") + return; + is_first_show = false; + container.addClass("help-shown"); + const text = tr("After you've successfully set upped your microphone,\n" + + "lets setup some profiles and identities!\n" + + "\n" + + "Connect profiles determine, how your're authenticating yourself with the server.\n" + + "So basically they're your identity.\n" + + "In the following I'll guid you thru the options and GUI elements.\n" + + "\n" + + "To continue click anywhere on the screen."); + set_help_text(text); + $("body").one('mousedown', event => show_profile_list_help()); + }); + const set_help_text = text => { + container_help_text.empty(); + text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); + }; + const show_profile_list_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_list.addClass("highlighted"); + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const offset = container_profile_list.offset(); + const abs = container.offset(); + container_help_text.css({ + top: offset.top - abs.top, + left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", + right: "1em", + bottom: "1em" + }); + }; + update_position(); + container_help_text.off('resize').on('resize', update_position); + const text = tr("You could have as many connect profiles as you want.\n" + + "All created profiles will be listed here.\n" + + "\n" + + "To create a new profile just simply click the blue button \"Create profile\" and enter a profile name.\n" + + "If you want to delete a profile you've to select that profile and click the delete button.\n" + + "\n" + + "By default we're using the \"default\" profile\n" + + "to connect to any server. o change the default profile\n" + + "just select the new profile and press the \"select as default\" button.\n" + + "\n" + + "To continue click anywhere on the screen."); + set_help_text(text); + $("body").one('mousedown', event => show_profile_settings_help()); + }; + const show_profile_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_settings.addClass("highlighted"); + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_settings_offset = container_profile_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_settings_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); + }; + set_help_text(tr("In the upper left, you'll find the profile settings for the selected profile.\n" + + "You could give each profile an individual name. You could also specify the default connect nickname here.\n" + + "\n" + + "The last option \"Identity Type\" determines on what your identity is based on.\n" + + "TeaSpeak has two possibilities to identify yourself:\n" + + "1. Identify yourself by your TeaSpeak forum account\n" + + "2. Identify by an own generated cryptographic identity\n" + + "The second methods is also known as a TeamSpeak 3 identity.\n" + + "\n" + + "To continue click anywhere on the screen.")); + update_position(); + container_help_text.off('resize').on('resize', update_position); + $("body").one('mousedown', event => show_identity_settings_help()); + }; + const show_identity_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_identity_settings.addClass("highlighted"); + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_identity_offset = container_identity_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_identity_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); + }; + set_help_text(tr("When selecting an identify type, some corresponding will pop up in the highlighted area.\n" + + "\n" + + "But don't worry, we've already generated\n" + + "a cryptographic identity for you!\n" + + "So you don't have to change anything before you start.")); + update_position(); + container_help_text.off('resize').on('resize', update_position); + $("body").one('mousedown', event => hide_help()); + }; + const hide_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container.addClass("hide-help"); + setTimeout(() => container.removeClass("help-shown"), 1000); + container_help_text.off('resize'); + help_animation_done = true; + update_step_status(); + }; + } + } + function initializeStepMicrophone(tag, event_registry, modal) { + const microphone_events = new events.Registry(); + //microphone_events.enable_debug("settings-microphone"); + Modals.modal_settings.initialize_audio_microphone_controller(microphone_events); + Modals.modal_settings.initialize_audio_microphone_view(tag, microphone_events); + modal.close_listener.push(() => microphone_events.fire_async("deinitialize")); + let help_animation_done = false; + const update_step_status = () => event_registry.fire_async("step-status", { next_button: help_animation_done, previous_button: help_animation_done }); + event_registry.on("show_step", e => { + if (e.step !== "microphone") + return; + update_step_status(); + }); + /* the help sequence */ + { + const container = tag.find(".container-settings-audio-microphone"); + const container_help_text = tag.find(".container-help-text"); + const container_profile_list = tag.find(".highlight-microphone-list"); + const container_profile_settings = tag.find(".highlight-microphone-settings"); + let is_first_show = true; + event_registry.on("show_step", event => { + if (!is_first_show || event.step !== "microphone") + return; + is_first_show = false; + container.addClass("help-shown"); + const text = tr("Firstly we need to setup a microphone.\n" + + "Let me guide you thru the basic UI elements.\n" + + "\n" + + "To continue click anywhere on the screen."); + set_help_text(text); + $("body").one('mousedown', event => show_microphone_list_help()); + }); + const set_help_text = text => { + container_help_text.empty(); + text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); + }; + const show_microphone_list_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_list.addClass("highlighted"); + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const offset = container_profile_list.offset(); + const abs = container.offset(); + container_help_text.css({ + top: offset.top - abs.top, + left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", + right: "1em", + bottom: "1em" + }); + }; + update_position(); + container_help_text.off('resize').on('resize', update_position); + const text = tr("All your available microphones are listed within this box.\n" + + "\n" + + "The currently selected microphone\n" + + "is marked with a green checkmark. To change the selected microphone\n" + + "just click on the new one.\n" + + "\n" + + "To continue click anywhere on the screen."); + set_help_text(text); + $("body").one('mousedown', event => show_microphone_settings_help()); + }; + const show_microphone_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_settings.addClass("highlighted"); + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_settings_offset = container_profile_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_settings_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); + }; + container_help_text.empty(); + container_help_text.append($.spawn("div").addClass("help-microphone-settings").append($.spawn("a").text(_translations.lLkLuT23 || (_translations.lLkLuT23 = tr("On the right side you'll find all microphone settings."))), $.spawn("br"), $.spawn("a").text("TeaSpeak has three voice activity detection types:"), $.spawn("ol").append($.spawn("li").addClass("vad-type").append($.spawn("a").addClass("title").text(_translations.K0mUVYLm || (_translations.K0mUVYLm = tr("Push to Talk"))), $.spawn("a").addClass("description").html(tr("To transmit audio data you'll have to
" + + "press a key. The key could be selected " + + "via the button right to the radio button."))), $.spawn("li").addClass("vad-type").append($.spawn("a").addClass("title").text(_translations.Fqmae3Ke || (_translations.Fqmae3Ke = tr("Voice activity detection"))), $.spawn("a").addClass("description").html(tr("In this mode, TeaSpeak will continuously analyze your microphone input. " + + "If the audio level is grater than a certain threshold, " + + "the audio will be transmitted. " + + "The threshold is changeable via the \"Sensitivity Settings\" slider."))), $.spawn("li").addClass("vad-type").append($.spawn("a").addClass("title").html(_translations.TWiMcMlr || (_translations.TWiMcMlr = tr("Always active"))), $.spawn("a").addClass("description").text(_translations.dqF7V4bv || (_translations.dqF7V4bv = tr("Continuously transmit any audio data.\n"))))), $.spawn("br"), $.spawn("a").text(_translations.oqlWN8_x || (_translations.oqlWN8_x = tr("Now you're ready to configure your microphone. Just click anywhere on the screen."))))); + update_position(); + container_help_text.off('resize').on('resize', update_position); + $("body").one('mousedown', event => hide_help()); + }; + const hide_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container.addClass("hide-help"); + setTimeout(() => container.removeClass("help-shown"), 1000); + container_help_text.off('resize'); + help_animation_done = true; + update_step_status(); + }; + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["37e3848da43122a37a986ffe0d4318d9c4892236cfecba4532909138fc47ba58"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["37e3848da43122a37a986ffe0d4318d9c4892236cfecba4532909138fc47ba58"] = "37e3848da43122a37a986ffe0d4318d9c4892236cfecba4532909138fc47ba58"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "J_SnpbWx", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (10,21)" }, { name: "odLgoCTL", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (43,21)" }, { name: "KGo6XEt2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (73,30)" }, { name: "HDvApYkd", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (73,53)" }, { name: "hLXhQgZZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (88,21)" }, { name: "LVBcNpNW", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (113,46)" }, { name: "vXw52Eni", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (113,82)" }, { name: "h1C4jc6K", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (135,46)" }, { name: "r6Y8ZEYM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (135,82)" }, { name: "O6_EO880", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (142,36)" }, { name: "UwbofYR1", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (142,57)" }, { name: "WbN1Rawl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (183,27)" }, { name: "EccIOEia", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (212,50)" }, { name: "tr0bL6Eo", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (212,80)" }, { name: "DleZumwj", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (235,31)" }, { name: "dQbnEzVb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (253,38)" }, { name: "mpjeWled", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (253,65)" }, { name: "FvJMcfQT", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (357,30)" }, { name: "DIfGV_4h", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistEdit.ts (357,67)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function spawnPlaylistSongInfo(song) { + let modal; + modal = createModal({ + header: _translations.J_SnpbWx || (_translations.J_SnpbWx = tr("Song info")), + body: () => { + try { + song.metadata = JSON.parse(song.song_metadata); + } + catch (e) { } + let template = $("#tmpl_playlist_edit-song_info").renderTag(song); + template = $.spawn("div").append(template); + const text_area = template.find(".property-metadata-raw textarea"); + template.find(".toggle-metadata").on('click', event => { + if (text_area.is(":visible")) { + template.find(".toggle-metadata").text("show"); + } + else { + template.find(".toggle-metadata").text("hide"); + } + text_area.slideToggle({ duration: 250 }); + }); + text_area.hide(); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + Modals.spawnPlaylistSongInfo = spawnPlaylistSongInfo; + function spawnSongAdd(playlist, callback_add) { + let modal; + modal = createModal({ + header: _translations.odLgoCTL || (_translations.odLgoCTL = tr("Add a song")), + body: () => { + let template = $("#tmpl_playlist_edit-song_add").renderTag(); + template = $.spawn("div").append(template); + const url = template.find(".property-url .value"); + const url_loader = template.find(".property-loader .value"); + const button_add = template.find(".container-buttons .button-add"); + const button_cancel = template.find(".container-buttons .button-cancel"); + url.on('change keyup', event => { + button_add.prop("disabled", url.val().toString().length == 0); + }).trigger('change'); + button_cancel.on('click', event => modal.close()); + button_add.on('click', event => { + callback_add(url.val(), url_loader.val()); + modal.close(); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + Modals.spawnSongAdd = spawnSongAdd; + function spawnPlaylistEdit(client, playlist) { + { + createErrorModal(_translations.KGo6XEt2 || (_translations.KGo6XEt2 = tr("Not implemented")), _translations.HDvApYkd || (_translations.HDvApYkd = tr("Playlist editing hasn't yet been implemented"))).open(); + return; + } + let modal; + let changed_properties = {}; + let changed_permissions = {}; + let callback_permission_update; + const update_save = () => { + const save_button = modal.htmlTag.find(".buttons .button-save"); + save_button.prop("disabled", (Object.keys(changed_properties).length + Object.keys(changed_permissions).length) == 0); + }; + modal = createModal({ + header: _translations.hLXhQgZZ || (_translations.hLXhQgZZ = tr("Edit playlist")), + body: () => { + let template = $("#tmpl_playlist_edit").renderTag().tabify(); + callback_permission_update = apply_permissions(template, client, playlist, (key, value) => { + console.log("Change permission %o => %o", key, value); + changed_permissions[key] = value; + update_save(); + }); + const callback_song_id = apply_songs(template, client, playlist); + apply_properties(template, client, playlist, (key, value) => { + console.log("Change property %o => %o", key, value); + changed_properties[key] = value; + update_save(); + }, callback_song_id); + template.find(".buttons .button-save").on('click', event => { + if (Object.keys(changed_properties).length != 0) { + changed_properties["playlist_id"] = playlist.playlist_id; + client.serverConnection.send_command("playlistedit", changed_properties).then(() => { + changed_properties = {}; + update_save(); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.LVBcNpNW || (_translations.LVBcNpNW = tr("Failed to change properties.")), (_translations.vXw52Eni || (_translations.vXw52Eni = tr("Failed to change playlist properties.
Error: "))) + error).open(); + }); + } + if (Object.keys(changed_permissions).length != 0) { + const array = []; + for (const permission_key of Object.keys(changed_permissions)) { + array.push({ + permvalue: changed_permissions[permission_key], + permnegated: false, + permskip: false, + permsid: permission_key + }); + } + array[0]["playlist_id"] = playlist.playlist_id; + client.serverConnection.send_command("playlistaddperm", array).then(() => { + changed_permissions = {}; + update_save(); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.h1C4jc6K || (_translations.h1C4jc6K = tr("Failed to change permission.")), (_translations.r6Y8ZEYM || (_translations.r6Y8ZEYM = tr("Failed to change playlist permissions.
Error: "))) + error).open(); + }); + } + }); + template.find(".buttons .button-close").on('click', event => { + if ((Object.keys(changed_properties).length + Object.keys(changed_permissions).length) != 0) { + Modals.spawnYesNo(_translations.O6_EO880 || (_translations.O6_EO880 = tr("Are you sure?")), _translations.UwbofYR1 || (_translations.UwbofYR1 = tr("Do you really want to discard all your changes?")), result => { + if (result) + modal.close(); + }); + return; + } + modal.close(); + }); + return template; + }, + footer: undefined, + width: 750 + }); + update_save(); + modal.open(); + return modal; + } + Modals.spawnPlaylistEdit = spawnPlaylistEdit; + function apply_songs(tag, client, playlist) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + const song_tag = tag.find(".container-songs"); + let replaying_song_id = 0; + let selected_song; + const set_song_info = (text) => { + const tag = song_tag.find(".info-message"); + if (text && text.length > 0) { + tag.text(text).show(); + } + else + tag.hide(); + }; + const set_current_song = (id) => { + /* this method shall enforce an update */ + replaying_song_id = id; + update_songs(); + }; + const update_songs = () => { + set_song_info(_translations.WbN1Rawl || (_translations.WbN1Rawl = tr("loading song list"))); + client.serverConnection.command_helper.request_playlist_songs(playlist.playlist_id).then(result => { + const entries_tag = song_tag.find(".song-list-entries"); + const entry_template = $("#tmpl_playlist_edit-song_entry"); + entries_tag.empty(); + for (const song of result) { + const rendered = entry_template.renderTag(song); + rendered.find(".button-info").on('click', event => { + spawnPlaylistSongInfo(song); + }); + const button_delete = rendered.find(".button-delete"); + if (!owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_REMOVE_POWER).granted(playlist.needed_power_song_remove)) + button_delete.detach(); + else + button_delete.on('click', event => { + client.serverConnection.send_command("playlistsongremove", { + playlist_id: playlist.playlist_id, + song_id: song.song_id + }).then(() => { + rendered.slideToggle({ duration: 250, done(animation, jumpedToEnd) { + rendered.detach(); + } }); + rendered.hide(250); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.EccIOEia || (_translations.EccIOEia = tr("Failed to remove song.")), (_translations.tr0bL6Eo || (_translations.tr0bL6Eo = tr("Failed to remove song/url from the playlist.
Error: "))) + error).open(); + }); + }); + if (song.song_id == replaying_song_id) + rendered.addClass("playing"); + rendered.on('click', event => { + selected_song = song; + entries_tag.find(".selected").removeClass("selected"); + rendered.addClass("selected"); + }); + entries_tag.append(rendered); + } + const entry_container = song_tag.find(".song-list-entries-container"); + if (entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + set_song_info("displaying " + result.length + " songs"); + }).catch(error => { + console.error(error); + set_song_info(_translations.DleZumwj || (_translations.DleZumwj = tr("failed to load song list"))); + //TODO improve error handling! + }); + }; + song_tag.find(".button-refresh").on('click', event => update_songs()); + song_tag.find(".button-song-add").on('click', event => { + spawnSongAdd(playlist, (url, loader) => { + //playlist_id invoker previous url + client.serverConnection.send_command("playlistsongadd", { + playlist_id: playlist.playlist_id, + invoker: loader, + url: url + }).then(() => { + update_songs(); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.dQbnEzVb || (_translations.dQbnEzVb = tr("Failed to add song.")), (_translations.mpjeWled || (_translations.mpjeWled = tr("Failed to add song/url to the playlist.
Error: "))) + error).open(); + }); + }); + }).prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_ADD_POWER).granted(playlist.needed_power_song_add)); + /* setTimeout(update_songs, 100); */ /* We dont have to call that here because it will get called over set_current_song when we received the current song id */ + return set_current_song; + } + function apply_permissions(tag, client, playlist, change_permission) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + const permission_tag = tag.find(".container-permissions"); + const nopermission_tag = tag.find(".container-no-permissions"); + const update_permissions = () => { + if (!client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST).granted(1)) { + nopermission_tag.show(); + permission_tag.hide(); + } + else { + nopermission_tag.hide(); + permission_tag.show(); + permission_tag.find(".permission input").prop("disabled", true); + client.permissions.requestPlaylistPermissions(playlist.playlist_id).then(permissions => { + permission_tag.find(".permission input") + .val(0) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_PERMISSION_MODIFY_POWER).granted(playlist.needed_power_permission_modify)); + for (const permission of permissions) { + const tag = permission_tag.find(".permission[permission='" + permission.type.name + "']"); + if (permission.value != -2) + tag.find("input").val(permission.value); + } + }); + } + }; + permission_tag.find(".permission").each((index, _element) => { + const element = $(_element); + element.find("input").on('change', event => { + console.log(element.find("input").val()); + change_permission(element.attr("permission"), parseInt(element.find("input").val().toString())); + }); + }); + update_permissions(); + return update_permissions; + } + function apply_properties(tag, client, playlist, change_property, callback_current_song) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => { + tag.find(".property-owner input") + .val(info.playlist_owner_name + " (" + info.playlist_owner_dbid + ")"); + tag.find(".property-title input") + .val(info.playlist_title) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_title", event.target.value); + }); + tag.find(".property-description textarea") + .val(info.playlist_description) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_description", event.target.value); + }); + tag.find(".property-type select") + .val(info.playlist_type.toString()) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_description", event.target.selectedIndex.toString()); + }); + tag.find(".property-replay-mode select") + .val(info.playlist_replay_mode.toString()) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_replay_mode", event.target.selectedIndex.toString()); + }); + tag.find(".property-flag-delete-played input") + .prop("checked", info.playlist_flag_delete_played) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_flag_delete_played", event.target.checked ? "1" : "0"); + }); + tag.find(".property-current-song input") + .val(info.playlist_current_song_id); + callback_current_song(info.playlist_current_song_id); + tag.find(".property-flag-finished input") + .prop("checked", info.playlist_flag_finished) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_flag_finished", event.target.checked ? "1" : "0"); + }); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.FvJMcfQT || (_translations.FvJMcfQT = tr("Failed to query playlist info")), (_translations.DIfGV_4h || (_translations.DIfGV_4h = tr("Failed to query playlist info.
Error:"))) + error).open(); + }); + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["bb5acfe72ba10b0aa2a6519edd2b750e4a456190f2d624fe214316fd928e1f66"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["bb5acfe72ba10b0aa2a6519edd2b750e4a456190f2d624fe214316fd928e1f66"] = "bb5acfe72ba10b0aa2a6519edd2b750e4a456190f2d624fe214316fd928e1f66"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "eFc85qtL", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (9,30)" }, { name: "kG4iwbMz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (9,53)" }, { name: "o5nuh_LS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (88,21)" }, { name: "aN1mAh7m", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (106,44)" }, { name: "WBBTbFHq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (106,79)" }, { name: "xpCch8hN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (127,42)" }, { name: "sTep6Bz5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (127,75)" }, { name: "cq45GpJi", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (139,39)" }, { name: "ZOxonC98", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (139,60)" }, { name: "Bp3F9rOw", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (142,49)" }, { name: "TiqZADIK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (142,84)" }, { name: "_1mprv6P", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (150,50)" }, { name: "jNGK8sZ3", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPlaylistList.ts (150,83)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +/// +var Modals; +(function (Modals) { + function spawnPlaylistManage(client) { + { + createErrorModal(_translations.eFc85qtL || (_translations.eFc85qtL = tr("Not implemented")), _translations.kG4iwbMz || (_translations.kG4iwbMz = tr("Playlist management hasn't yet been implemented"))).open(); + return; + } + let modal; + let selected_playlist; + let available_playlists; + let highlight_own = settings.global("playlist-list-highlight-own", true); + const update_selected = () => { + const buttons = modal.htmlTag.find(".header .buttons"); + buttons.find(".button-playlist-edit").prop("disabled", !selected_playlist); + buttons.find(".button-playlist-delete").prop("disabled", !selected_playlist || !( /* not owner or permission */client.permissions.neededPermission(PermissionType.I_PLAYLIST_DELETE_POWER).granted(selected_playlist.needed_power_delete) || /* client has permissions */ + client.getClient().properties.client_database_id == selected_playlist.playlist_owner_dbid /* client is playlist owner */)); + buttons.find(".button-playlist-create").prop("disabled", !client.permissions.neededPermission(PermissionType.B_PLAYLIST_CREATE).granted(1)); + if (selected_playlist) { + buttons.find(".button-playlist-edit").prop("disabled", false); + } + }; + const update_list = () => __awaiter(this, void 0, void 0, function* () { + const info_tag = modal.htmlTag.find(".footer .info a"); + info_tag.text("loading..."); + selected_playlist = undefined; + update_selected(); + try { + available_playlists = yield client.serverConnection.command_helper.request_playlist_list(); + } + catch (error) { + info_tag.text("failed to query playlist list."); + //FIXME error handling? + return; + } + const entries_tag = modal.htmlTag.find(".playlist-list-entries"); + const entry_template = $("#tmpl_playlist_list-list_entry"); + entries_tag.empty(); + const owndbid = client.getClient().properties.client_database_id; + for (const query of available_playlists) { + const tag = entry_template.renderTag(query).on('click', event => { + entries_tag.find(".entry.selected").removeClass("selected"); + $(event.target).parent(".entry").addClass("selected"); + selected_playlist = query; + update_selected(); + }); + if (highlight_own && query.playlist_owner_dbid == owndbid) + tag.addClass("highlighted"); + entries_tag.append(tag); + } + const entry_container = modal.htmlTag.find(".playlist-list-entries-container"); + if (entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + info_tag.text("Showing " + available_playlists.length + " entries"); + update_selected(); + }); + modal = createModal({ + header: _translations.o5nuh_LS || (_translations.o5nuh_LS = tr("Manage playlists")), + body: () => { + let template = $("#tmpl_playlist_list").renderTag(); + /* first open the modal */ + setTimeout(() => { + const entry_container = template.find(".playlist-list-entries-container"); + if (entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + }, 100); + template.find(".footer .buttons .button-refresh").on('click', update_list); + template.find(".button-playlist-create").on('click', event => { + const single_handler = { + function: command => { + const json = command.arguments; + update_list().then(() => { + Modals.spawnYesNo(_translations.aN1mAh7m || (_translations.aN1mAh7m = tr("Playlist created successful")), _translations.WBBTbFHq || (_translations.WBBTbFHq = tr("The playlist has been successfully created.
Should we open the editor?")), result => { + if (result) { + for (const playlist of available_playlists) { + if (playlist.playlist_id == json[0]["playlist_id"]) { + Modals.spawnPlaylistEdit(client, playlist).close_listener.push(update_list); + return; + } + } + } + }); + }); + return true; + }, + command: "notifyplaylistcreated" + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + client.serverConnection.send_command("playlistcreate").catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.xpCch8hN || (_translations.xpCch8hN = tr("Unable to create playlist")), (_translations.sTep6Bz5 || (_translations.sTep6Bz5 = tr("Failed to create playlist
Message: "))) + error).open(); + }); + }); + template.find(".button-playlist-edit").on('click', event => { + if (!selected_playlist) + return; + Modals.spawnPlaylistEdit(client, selected_playlist).close_listener.push(update_list); + }); + template.find(".button-playlist-delete").on('click', () => { + if (!selected_playlist) + return; + Modals.spawnYesNo(_translations.cq45GpJi || (_translations.cq45GpJi = tr("Are you sure?")), _translations.ZOxonC98 || (_translations.ZOxonC98 = tr("Do you really want to delete this playlist?")), result => { + if (result) { + client.serverConnection.send_command("playlistdelete", { playlist_id: selected_playlist.playlist_id }).then(() => { + createInfoModal(_translations.Bp3F9rOw || (_translations.Bp3F9rOw = tr("Playlist deleted successful")), _translations.TiqZADIK || (_translations.TiqZADIK = tr("This playlist has been deleted successfully."))).open(); + update_list(); + }).catch(error => { + if (error instanceof CommandResult) { + /* TODO extra handling here */ + //if(error.id == ErrorID.PLAYLIST_IS_IN_USE) { } + error = error.extra_message || error.message; + } + createErrorModal(_translations._1mprv6P || (_translations._1mprv6P = tr("Unable to delete playlist")), (_translations.jNGK8sZ3 || (_translations.jNGK8sZ3 = tr("Failed to delete playlist
Message: "))) + error).open(); + }); + } + }); + }); + template.find(".input-search").on('change keyup', () => { + const text = (template.find(".input-search").val() || "").toLowerCase(); + if (text.length == 0) { + template.find(".playlist-list-entries .entry").show(); + } + else { + template.find(".playlist-list-entries .entry").each((_, e) => { + const element = $(e); + if (element.text().toLowerCase().indexOf(text) == -1) + element.hide(); + else + element.show(); + }); + } + }); + template.find(".button-highlight-own").on('change', event => { + const flag = event.target.checked; + settings.changeGlobal("playlist-list-highlight-own", flag); + if (flag) { + const owndbid = client.getClient().properties.client_database_id; + template.find(".playlist-list-entries .entry").each((index, _element) => { + const element = $(_element); + if (parseInt(element.attr("playlist-owner-dbid")) == owndbid) + element.addClass("highlighted"); + }); + } + else { + template.find(".playlist-list-entries .highlighted").removeClass("highlighted"); + } + }).prop("checked", highlight_own); + return template; + }, + footer: undefined, + width: 750 + }); + update_list(); + modal.open(); + } + Modals.spawnPlaylistManage = spawnPlaylistManage; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["e8a999fc7a44ee2d0ddde9773f4d98a7fd17eff7fe5d5988d687dede92d8fb74"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["e8a999fc7a44ee2d0ddde9773f4d98a7fd17eff7fe5d5988d687dede92d8fb74"] = "e8a999fc7a44ee2d0ddde9773f4d98a7fd17eff7fe5d5988d687dede92d8fb74"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "UMNCZlGl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPoke.ts (19,25)" }, { name: "cfRyWTer", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPoke.ts (61,66)" }, { name: "xYlEtjJv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalPoke.ts (64,66)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + let global_modal; + class PokeModal { + constructor() { + this.source_map = []; + this._handle = createModal({ + header: _translations.UMNCZlGl || (_translations.UMNCZlGl = tr("You have been poked!")), + body: () => { + let template = $("#tmpl_poke_popup").renderTag(); + template.find(".button-close").on('click', event => this._handle_close()); + return template; + }, + footer: undefined, + width: 750 + }); + this._handle.close_listener.push(() => this._handle_close()); + } + modal() { return this._handle; } + add_poke(source, invoker, message) { + let handler; + for (const entry of this.source_map) + if (entry.source === source) { + handler = entry; + break; + } + if (!handler) { + const html_tag = $.spawn("div").addClass("server"); + const poke_list = $.spawn("div").addClass("poke-list"); + $.spawn("div") + .addClass("server-name") + .text(source && source.channelTree && source.channelTree.server ? source.channelTree.server.properties.virtualserver_name : "unknown") + .appendTo(html_tag); + poke_list.appendTo(html_tag); + this.source_map.push(handler = { + source: source, + add_message: (invoker, message) => { + const container = $.spawn("div").addClass("entry"); + $.spawn("div").addClass("date").text(moment().format("HH:mm:ss") + " - ").appendTo(container); + $.spawn("div").addClass("user").append($(htmltags.generate_client({ + add_braces: true, + client_id: invoker.id, + client_name: invoker.name, + client_unique_id: invoker.unique_id + }))).appendTo(container); + if (message) { + $.spawn("div").addClass("text").text(_translations.cfRyWTer || (_translations.cfRyWTer = tr("pokes you:"))).appendTo(container); + $.spawn("div").addClass("poke-message").append(...MessageHelper.bbcode_chat(message)).appendTo(container); + } + else { + $.spawn("div").addClass("text").text(_translations.xYlEtjJv || (_translations.xYlEtjJv = tr("pokes you."))).appendTo(container); + } + container.appendTo(poke_list); + } + }); + this._handle.htmlTag.find(".container-servers").append(html_tag); + } + handler.add_message(invoker, message); + } + _handle_close() { + this._handle.close(); + global_modal = undefined; + } + } + function spawnPoke(source, invoker, message) { + if (!global_modal) + global_modal = new PokeModal(); + global_modal.add_poke(source, invoker, message); + global_modal.modal().open(); + } + Modals.spawnPoke = spawnPoke; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["8233474cff76a05a8663c0eaec8fe99c0de8bc9b0f1e55e093cf1b4c5c5253d9"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["8233474cff76a05a8663c0eaec8fe99c0de8bc9b0f1e55e093cf1b4c5c5253d9"] = "8233474cff76a05a8663c0eaec8fe99c0de8bc9b0f1e55e093cf1b4c5c5253d9"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "oEF0dbsG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (9,21)" }, { name: "A709oy8V", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (18,42)" }, { name: "u8gNNCgy", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (18,66)" }, { name: "mgzlpOSP", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (43,42)" }, { name: "gxrVeEls", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (43,74)" }, { name: "YedBLfXN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (62,36)" }, { name: "nkmR5Lds", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQuery.ts (62,69)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + function spawnQueryCreate(connection, callback_created) { + let modal; + modal = createModal({ + header: _translations.oEF0dbsG || (_translations.oEF0dbsG = tr("Create a server query login")), + body: () => { + let template = $("#tmpl_query_create").renderTag(); + template = $.spawn("div").append(template); + template.find(".button-close").on('click', event => modal.close()); + template.find(".button-create").on('click', event => { + const name = template.find(".input-name").val(); + if (name.length < 3 || name.length > 64) { + createErrorModal(_translations.A709oy8V || (_translations.A709oy8V = tr("Invalid username")), _translations.u8gNNCgy || (_translations.u8gNNCgy = tr("Please enter a valid name!"))).open(); + return; + } + const single_handler = { + function: command => { + const json = command.arguments[0]; + spawnQueryCreated({ + username: name, + password: json.client_login_password + }, true); + if (callback_created) + callback_created(name, json.client_login_password); + return true; + }, + command: "notifyquerycreated" + }; + connection.serverConnection.command_handler_boss().register_single_handler(single_handler); + connection.serverConnection.send_command("querycreate", { + client_login_name: name + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.mgzlpOSP || (_translations.mgzlpOSP = tr("Unable to create account")), (_translations.gxrVeEls || (_translations.gxrVeEls = tr("Failed to create account
Message: "))) + error).open(); + }).then(() => connection.serverConnection.command_handler_boss().remove_single_handler(single_handler)); + modal.close(); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + Modals.spawnQueryCreate = spawnQueryCreate; + function spawnQueryCreated(credentials, just_created) { + let modal; + modal = createModal({ + header: just_created ? _translations.YedBLfXN || (_translations.YedBLfXN = tr("Server query credentials")) : _translations.nkmR5Lds || (_translations.nkmR5Lds = tr("New server query credentials")), + body: () => { + let template = $("#tmpl_query_created").renderTag(credentials); + template = $.spawn("div").append(template); + template.find(".button-close").on('click', event => modal.close()); + template.find(".query_name").text(credentials.username); + template.find(".query_password").text(credentials.password); + template.find(".btn_copy_name").on('click', () => { + template.find(".query_name").select(); + document.execCommand("copy"); + }); + template.find(".btn_copy_password").on('click', () => { + template.find(".query_password").select(); + document.execCommand("copy"); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + Modals.spawnQueryCreated = spawnQueryCreated; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["3a91b349c2467016f349cf833e9efd82314ecbcfc2ccc49a1b5acf3e418d361b"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["3a91b349c2467016f349cf833e9efd82314ecbcfc2ccc49a1b5acf3e418d361b"] = "3a91b349c2467016f349cf833e9efd82314ecbcfc2ccc49a1b5acf3e418d361b"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "QuHkt6Yk", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (169,21)" }, { name: "i_hVQpk9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (213,47)" }, { name: "vIqZZZIq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (224,59)" }, { name: "gk7HleVb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (256,43)" }, { name: "GUdKOS4n", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (259,59)" }, { name: "yZzadkPz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (260,39)" }, { name: "clwP2wmM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (264,55)" }, { name: "tSViiNga", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (265,35)" }, { name: "FnZFqOgS", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (285,54)" }, { name: "xp1swftm", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (287,54)" }, { name: "Kp_4nxoB", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (310,51)" }, { name: "iKboGUz6", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (321,41)" }, { name: "cLqUBiXb", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (321,65)" }, { name: "m9MLHYRV", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (331,43)" }, { name: "KIIc2CpG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (331,64)" }, { name: "Hf32wf2q", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (336,53)" }, { name: "qiRW7EnN", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (336,89)" }, { name: "Q_SsRGIG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (341,54)" }, { name: "rUve7dTz", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (341,114)" }, { name: "qwlCVdpq", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (350,42)" }, { name: "lFO6F9nu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (350,69)" }, { name: "NxEf3JtU", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (356,53)" }, { name: "Z2taR5dJ", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (356,89)" }, { name: "g4NSz8jG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (361,54)" }, { name: "ewpKfvcI", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (361,114)" }, { name: "uMip3QhX", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (370,42)" }, { name: "h4H98bLv", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (370,75)" }, { name: "DoHvT4u4", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (392,54)" }, { name: "wishvA6x", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalQueryManage.ts (392,115)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + /* + export function spawnQueryManage(client: ConnectionHandler) { + let modal: Modal; + let selected_query: QueryListEntry; + + const update_selected = () => { + const buttons = modal.htmlTag.find(".header .buttons"); + + //TODO gray out if no permissions (Server needs to send that... :D) + buttons.find(".button-query-delete").prop("disabled", selected_query === undefined); + buttons.find(".button-query-rename").prop("disabled", selected_query === undefined); + buttons.find(".button-query-change-password").prop("disabled", selected_query === undefined); + }; + + const update_list = () => { + const info_tag = modal.htmlTag.find(".footer .info a"); + info_tag.text("loading..."); + client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { + client.serverConnection.command_helper.request_query_list(server_id).then(result => { + selected_query = undefined; + + const entries_tag = modal.htmlTag.find(".query-list-entries"); + const entry_template = $("#tmpl_query_manager-list_entry"); + entries_tag.empty(); + + for(const query of result.queries || []) { + entries_tag.append(entry_template.renderTag(query).on('click', event => { + entries_tag.find(".entry.selected").removeClass("selected"); + $(event.target).parent(".entry").addClass("selected"); + selected_query = query; + update_selected(); + })); + } + + const entry_container = modal.htmlTag.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + + if(!result || result.flag_all) { + info_tag.text("Showing all server queries"); + } else { + info_tag.text("Showing your server queries") + } + update_selected(); + }); + }); + //TODO error handling + }; + + modal = createModal({ + header: tr("Manage query accounts"), + body: () => { + let template = $("#tmpl_query_manager").renderTag(); + template = $.spawn("div").append(template); + + /* first open the modal + setTimeout(() => { + const entry_container = template.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + }, 100); + + template.find(".footer .buttons .button-refresh").on('click', update_list); + template.find(".button-query-create").on('click', () => { + Modals.spawnQueryCreate(client, (user, pass) => update_list()); + }); + template.find(".button-query-rename").on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account name"), tr("Enter the new name for the login:
"), text => text.length >= 3, result => { + if(result) { + client.serverConnection.send_command("queryrename", { + client_login_name: selected_query.username, + client_new_login_name: result + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to rename account"), tr("Failed to rename account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); + update_list(); + }); + } + }).open(); + }); + template.find(".button-query-change-password").on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):
"), text => true, result => { + if(result !== false) { + const single_handler: connection.SingleCommandHandler = { + command: "notifyquerypasswordchanges", + function: command => { + Modals.spawnQueryCreated({ + username: command.arguments[0]["client_login_name"], + password: command.arguments[0]["client_login_password"] + }, false); + + return true; + } + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + + client.serverConnection.send_command("querychangepassword", { + client_login_name: selected_query.username, + client_login_password: result + }).catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to change password"), tr("Failed to change password
Message: ") + error).open(); + }); + } + }).open(); + }); + template.find(".button-query-delete").on('click', () => { + if(!selected_query) return; + + Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { + if(result) { + client.serverConnection.send_command("querydelete", { + client_login_name: selected_query.username + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to delete account"), tr("Failed to delete account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); + update_list(); + }); + } + }); + }); + template.find(".input-search").on('change keyup', () => { + const text = (template.find(".input-search").val() as string || "").toLowerCase(); + if(text.length == 0) { + template.find(".query-list-entries .entry").show(); + } else { + template.find(".query-list-entries .entry").each((_, e) => { + const element = $(e); + if(element.text().toLowerCase().indexOf(text) == -1) + element.hide(); + else + element.show(); + }) + } + }); + return template; + }, + footer: undefined, + width: 750 + }); + + update_list(); + modal.open(); + } + */ + //tmpl_query_manager + function spawnQueryManage(client) { + let modal; + modal = createModal({ + header: _translations.QuHkt6Yk || (_translations.QuHkt6Yk = tr("Manage query accounts")), + body: () => { + let template = $("#tmpl_query_manager").renderTag(); + let current_server; + let selected_query; + let filter_callbacks = []; + const container_list = template.find(".container-list .container-entries"); + const container_list_empty = container_list.find(".container-empty"); + const container_list_error = container_list.find(".container-error"); + const detail_name = template.find(".detail.login-name .value"); + const detail_unique_id = template.find(".detail.unique-id .value"); + const detail_bound_server = template.find(".detail.bound-server .value"); + const detail_unique_id_copy = template.find(".detail.unique-id .button-copy"); + const input_filter = template.find(".filter-input"); + const button_create = template.find(".button-create"); + const button_delete = template.find(".button-delete"); + const button_rename = template.find(".button-rename"); + const button_change_password = template.find(".button-change-password"); + const button_update = template.find(".button-update"); + const permission_create = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1); + const permission_delete = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE).granted(1); + const permission_delete_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE_OWN).granted(1); + const permission_rename = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME).granted(1); + const permission_rename_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME_OWN).granted(1); + const permission_password = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD).granted(1); + const permission_password_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_OWN_PASSWORD).granted(1); + const permission_password_global = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL).granted(1); + button_create.prop('disabled', !permission_create); + const set_error = (error) => { + if (typeof (error) === "string") + container_list_error.text(error).show(); + else + container_list_error.hide(); + }; + const update_list = (selected_entry) => { + button_update.prop('disabled', true); + container_list_empty.text(_translations.i_hVQpk9 || (_translations.i_hVQpk9 = tr("loading..."))).show(); + set_error(undefined); + set_selected(undefined, false); + filter_callbacks = []; + container_list.find(".entry").remove(); + client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { + current_server = server_id; + client.serverConnection.command_helper.request_query_list(server_id).then(result => { + if (!result || !result.queries.length) { + container_list_empty.text(_translations.vIqZZZIq || (_translations.vIqZZZIq = tr("No queries available"))); + return; + } + for (const entry of result.queries) { + const tag = $.spawn("div").addClass("entry").text(entry.username + " (" + entry.unique_id + ")"); + tag.on('click', event => { + container_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + set_selected(entry, false); + }); + container_list.append(tag); + if (entry.username === selected_entry) + tag.trigger('click'); + const text_mesh = (entry.username + " " + entry.unique_id + " " + entry.bounded_server).toLowerCase(); + filter_callbacks.push(text => { + if (typeof (text) === "undefined" || text_mesh.indexOf(text) != -1) { + tag.show(); + return true; + } + else { + tag.hide(); + return false; + } + }); + } + update_filter(); + container_list_empty.hide(); + button_update.prop('disabled', false); + }).catch(error => { + button_update.prop('disabled', false); + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { + set_error(_translations.gk7HleVb || (_translations.gk7HleVb = tr("No permissions"))); + return; + } + log.error(LogCategory.CLIENT, _translations.GUdKOS4n || (_translations.GUdKOS4n = tr("Failed to request the query list: %o")), error); + set_error(_translations.yZzadkPz || (_translations.yZzadkPz = tr("Failed to request list"))); + }); + }).catch(error => { + button_update.prop('disabled', false); + log.error(LogCategory.CLIENT, _translations.clwP2wmM || (_translations.clwP2wmM = tr("Failed to get own virtual server id: %o")), error); + set_error(_translations.tSViiNga || (_translations.tSViiNga = tr("Failed to query server id"))); + }); + }; + const set_selected = (entry, force) => { + if (entry === selected_query && !force) + return; + selected_query = entry; + if (!selected_query) { + detail_name.text("-"); + detail_unique_id.text("-"); + detail_bound_server.text("-"); + button_delete.prop('disabled', true); + button_rename.prop('disabled', true); + button_change_password.prop('disabled', true); + } + else { + detail_name.text(selected_query.username); + detail_unique_id.text(selected_query.unique_id); + if (selected_query.bounded_server == 0) + detail_bound_server.text(_translations.FnZFqOgS || (_translations.FnZFqOgS = tr("On the instance"))); + else if (selected_query.bounded_server === current_server) + detail_bound_server.text(_translations.xp1swftm || (_translations.xp1swftm = tr("On the current server"))); + else + detail_bound_server.text(selected_query.bounded_server.toString()); + button_delete.prop('disabled', !permission_delete && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_delete_own)); + button_rename.prop('disabled', !permission_rename && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_rename_own)); + if (selected_query.bounded_server != 0) { + button_change_password.prop('disabled', !permission_password && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); + } + else { + button_change_password.prop('disabled', !permission_password_global && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); + } + } + }; + const update_filter = () => { + let value = input_filter.val(); + if (!value) + value = undefined; + else + value = value.toLowerCase(); + const shown = filter_callbacks.filter(e => e(value)).length; + if (shown > 0) { + container_list_empty.hide(); + } + else { + container_list_empty.text(_translations.Kp_4nxoB || (_translations.Kp_4nxoB = tr("No accounts found"))).show(); + } + }; + input_filter.on('change keyup', update_filter); + /* all buttons */ + { + detail_unique_id_copy.on('click', event => { + if (!selected_query) + return; + copy_to_clipboard(selected_query.unique_id); + createInfoModal(_translations.iKboGUz6 || (_translations.iKboGUz6 = tr("Unique ID copied")), _translations.cLqUBiXb || (_translations.cLqUBiXb = tr("The unique id has been successfully copied to your clipboard."))).open(); + }); + button_create.on('click', event => { + Modals.spawnQueryCreate(client, (user, pass) => update_list(user)); + }); + button_delete.on('click', event => { + if (!selected_query) + return; + Modals.spawnYesNo(_translations.m9MLHYRV || (_translations.m9MLHYRV = tr("Are you sure?")), _translations.KIIc2CpG || (_translations.KIIc2CpG = tr("Do you really want to delete this account?")), result => { + if (result) { + client.serverConnection.send_command("querydelete", { + client_login_name: selected_query.username + }).then(() => { + createInfoModal(_translations.Hf32wf2q || (_translations.Hf32wf2q = tr("Account successfully deleted")), _translations.qiRW7EnN || (_translations.qiRW7EnN = tr("The query account has been successfully deleted!"))).open(); + update_list(undefined); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.Q_SsRGIG || (_translations.Q_SsRGIG = tr("Unable to delete account")), MessageHelper.formatMessage(_translations.rUve7dTz || (_translations.rUve7dTz = tr("Failed to delete account{:br:}Message: {}")), error)).open(); + }); + } + }); + }); + button_rename.on('click', () => { + if (!selected_query) + return; + createInputModal(_translations.qwlCVdpq || (_translations.qwlCVdpq = tr("Change account name")), _translations.lFO6F9nu || (_translations.lFO6F9nu = tr("Enter the new name for the login:")), text => text.length >= 3, result => { + if (result) { + client.serverConnection.send_command("queryrename", { + client_login_name: selected_query.username, + client_new_login_name: result + }).then(() => { + createInfoModal(_translations.NxEf3JtU || (_translations.NxEf3JtU = tr("Account successfully renamed")), _translations.Z2taR5dJ || (_translations.Z2taR5dJ = tr("The query account has been renamed!"))).open(); + update_list(result); + }).catch(error => { + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.g4NSz8jG || (_translations.g4NSz8jG = tr("Unable to rename account")), MessageHelper.formatMessage(_translations.ewpKfvcI || (_translations.ewpKfvcI = tr("Failed to rename account{:br:}Message: {}")), error)).open(); + }); + } + }).open(); + }); + button_change_password.on('click', () => { + if (!selected_query) + return; + createInputModal(_translations.uMip3QhX || (_translations.uMip3QhX = tr("Change account's password")), _translations.h4H98bLv || (_translations.h4H98bLv = tr("Enter a new password (leave blank for auto generation):")), text => true, result => { + if (result !== false) { + const single_handler = { + command: "notifyquerypasswordchanges", + function: command => { + Modals.spawnQueryCreated({ + username: command.arguments[0]["client_login_name"], + password: command.arguments[0]["client_login_password"] + }, false); + return true; + } + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + client.serverConnection.send_command("querychangepassword", { + client_login_name: selected_query.username, + client_login_password: result + }).catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.DoHvT4u4 || (_translations.DoHvT4u4 = tr("Unable to change password")), MessageHelper.formatMessage(_translations.wishvA6x || (_translations.wishvA6x = tr("Failed to change password{:br:}Message: {}")), error)).open(); + }); + } + }).open(); + }); + button_update.on('click', event => update_list(selected_query ? selected_query.username : undefined)); + } + modal.close_listener.push(() => filter_callbacks = undefined); + set_selected(undefined, true); + update_list(undefined); + template.dividerfy(); + return template; + }, + footer: null, + min_width: "25em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-query-manage"); + modal.open(); + } + Modals.spawnQueryManage = spawnQueryManage; +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["5331667997b364927974be0ef39bd37bedd8cf94fc7240456ded5de4f6e772d3"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["5331667997b364927974be0ef39bd37bedd8cf94fc7240456ded5de4f6e772d3"] = "5331667997b364927974be0ef39bd37bedd8cf94fc7240456ded5de4f6e772d3"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "YqHx1E4Q", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (7,21)" }, { name: "sR5xH__S", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (27,54)" }, { name: "KYruiqDh", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (30,42)" }, { name: "Jm3uN9QM", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (30,92)" }, { name: "SoMZi5vs", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (98,99)" }, { name: "wRj3c9p5", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (109,96)" }, { name: "OvjaxNtG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (110,96)" }, { name: "ukxiAAgl", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (112,87)" }, { name: "mPE1EX5O", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (124,21)" }, { name: "_IgsAh3M", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (131,108)" }, { name: "h_Cu5Dka", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (169,28)" }, { name: "SAM5Jdia", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (174,36)" }, { name: "gH3qLXyu", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (176,36)" }, { name: "tGesHS8_", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (183,28)" }, { name: "pZ9amcr9", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (188,36)" }, { name: "pgi8qIo2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (190,36)" }, { name: "Eqou3mDY", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (199,81)" }, { name: "kVOW_aDn", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (206,32)" }, { name: "awuXKKzF", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (208,32)" }, { name: "Fxejq6JG", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfo.ts (210,32)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + function openServerInfo(server) { + let modal; + let update_callbacks = []; + modal = createModal({ + header: (_translations.YqHx1E4Q || (_translations.YqHx1E4Q = tr("Server Information: "))) + server.properties.virtualserver_name, + body: () => { + const template = $("#tmpl_server_info").renderTag(); + const children = template.children(); + const top = template.find(".container-top"); + const update_values = () => { + apply_hostbanner(server, top); + apply_category_1(server, children, update_callbacks); + apply_category_2(server, children, update_callbacks); + apply_category_3(server, children, update_callbacks); + }; + const button_update = template.find(".button-update"); + button_update.on('click', event => { + button_update.prop("disabled", true); + server.updateProperties().then(() => { + update_callbacks = []; + update_values(); + }).catch(error => { + log.warn(LogCategory.CLIENT, _translations.sR5xH__S || (_translations.sR5xH__S = tr("Failed to refresh server properties: %o")), error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.KYruiqDh || (_translations.KYruiqDh = tr("Refresh failed")), MessageHelper.formatMessage(_translations.Jm3uN9QM || (_translations.Jm3uN9QM = tr("Failed to refresh server properties.{:br:}Error: {}")), error)).open(); + }).then(() => { + button_update.prop("disabled", false); + }); + }).trigger('click'); + update_values(); + tooltip(template); + return template.children(); + }, + footer: null, + min_width: "25em" + }); + const updater = setInterval(() => { + server.request_connection_info().then(info => update_callbacks.forEach(e => e(Modals.RequestInfoStatus.SUCCESS, info))).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + update_callbacks.forEach(e => e(Modals.RequestInfoStatus.NO_PERMISSION)); + return; + } + update_callbacks.forEach(e => e(Modals.RequestInfoStatus.UNKNOWN)); + }); + }, 1000); + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".button-show-bandwidth").on('click', event => { + const custom_callbacks = []; + const custom_callback_caller = (status, info) => { custom_callbacks.forEach(e => e(status, info)); }; + update_callbacks.push(custom_callback_caller); + Modals.openServerInfoBandwidth(server, custom_callbacks).close_listener.push(() => { + update_callbacks.remove(custom_callback_caller); + }); + }); + modal.htmlTag.find(".modal-body").addClass("modal-server-info"); + modal.open(); + modal.close_listener.push(() => clearInterval(updater)); + } + Modals.openServerInfo = openServerInfo; + function apply_hostbanner(server, tag) { + let container; + tag.empty().append(container = $.spawn("div").addClass("container-hostbanner")).addClass("hidden"); + const htag = Hostbanner.generate_tag(server.properties.virtualserver_hostbanner_gfx_url, server.properties.virtualserver_hostbanner_gfx_interval, server.properties.virtualserver_hostbanner_mode); + htag.then(t => { + if (!t) + return; + tag.removeClass("hidden"); + container.append(t); + }); + } + function apply_category_1(server, tag, update_callbacks) { + /* server name */ + { + const container = tag.find(".server-name"); + container.text(server.properties.virtualserver_name); + } + /* server region */ + { + const container = tag.find(".server-region").empty(); + container.append($.spawn("div").addClass("country flag-" + server.properties.virtualserver_country_code.toLowerCase()), $.spawn("a").text(i18n.country_name(server.properties.virtualserver_country_code, _translations.SoMZi5vs || (_translations.SoMZi5vs = tr("Global"))))); + } + /* slots */ + { + const container = tag.find(".server-slots"); + let text = server.properties.virtualserver_clientsonline + "/" + server.properties.virtualserver_maxclients; + if (server.properties.virtualserver_queryclientsonline) + text += " +" + (server.properties.virtualserver_queryclientsonline > 1 ? + server.properties.virtualserver_queryclientsonline + " " + (_translations.wRj3c9p5 || (_translations.wRj3c9p5 = tr("Queries"))) : + server.properties.virtualserver_queryclientsonline + " " + (_translations.OvjaxNtG || (_translations.OvjaxNtG = tr("Query")))); + if (server.properties.virtualserver_reserved_slots) + text += " (" + server.properties.virtualserver_reserved_slots + " " + (_translations.ukxiAAgl || (_translations.ukxiAAgl = tr("Reserved"))) + ")"; + container.text(text); + } + /* first run */ + { + const container = tag.find(".server-first-run"); + container.text(server.properties.virtualserver_created > 0 ? + moment(server.properties.virtualserver_created * 1000).format('MMMM Do YYYY, h:mm:ss a') : _translations.mPE1EX5O || (_translations.mPE1EX5O = tr("Unknown"))); + } + /* uptime */ + { + const container = tag.find(".server-uptime"); + const update = () => container.text(MessageHelper.format_time(server.calculateUptime() * 1000, _translations._IgsAh3M || (_translations._IgsAh3M = tr("just started")))); + update_callbacks.push(update); + update(); + } + } + function apply_category_2(server, tag, update_callbacks) { + /* ip */ + { + const container = tag.find(".server-ip"); + container.text(server.remote_address.host + (server.remote_address.port == 9987 ? "" : (":" + server.remote_address.port))); + } + /* version */ + { + const container = tag.find(".server-version"); + let timestamp = -1; + const version = (server.properties.virtualserver_version || "unknwon").replace(/ ?\[build: ?([0-9]+)]/gmi, (group, ts) => { + timestamp = parseInt(ts); + return ""; + }); + container.find("a").text(version); + container.find(".container-tooltip").toggle(timestamp > 0).find(".tooltip a").text(moment(timestamp * 1000).format('[Build timestamp:] YYYY-MM-DD HH:mm Z')); + } + /* platform */ + { + const container = tag.find(".server-platform"); + container.text(server.properties.virtualserver_platform); + } + /* ping */ + { + const container = tag.find(".server-ping"); + container.text(_translations.h_Cu5Dka || (_translations.h_Cu5Dka = tr("calculating..."))); + update_callbacks.push((status, data) => { + if (status === Modals.RequestInfoStatus.SUCCESS) + container.text(data.connection_ping.toFixed(0) + " " + "ms"); + else if (status === Modals.RequestInfoStatus.NO_PERMISSION) + container.text(_translations.SAM5Jdia || (_translations.SAM5Jdia = tr("No Permissions"))); + else + container.text(_translations.gH3qLXyu || (_translations.gH3qLXyu = tr("receiving..."))); + }); + } + /* packet loss */ + { + const container = tag.find(".server-packet-loss"); + container.text(_translations.tGesHS8_ || (_translations.tGesHS8_ = tr("receiving..."))); + update_callbacks.push((status, data) => { + if (status === Modals.RequestInfoStatus.SUCCESS) + container.text(data.connection_packetloss_total.toFixed(2) + "%"); + else if (status === Modals.RequestInfoStatus.NO_PERMISSION) + container.text(_translations.pZ9amcr9 || (_translations.pZ9amcr9 = tr("No Permissions"))); + else + container.text(_translations.pgi8qIo2 || (_translations.pgi8qIo2 = tr("receiving..."))); + }); + } + } + function apply_category_3(server, tag, update_callbacks) { + /* unique id */ + { + const container = tag.find(".server-unique-id"); + container.text(server.properties.virtualserver_unique_identifier || (_translations.Eqou3mDY || (_translations.Eqou3mDY = tr("Unknown")))); + } + /* voice encryption */ + { + const container = tag.find(".server-voice-encryption"); + if (server.properties.virtualserver_codec_encryption_mode == 0) + container.text(_translations.kVOW_aDn || (_translations.kVOW_aDn = tr("Globally off"))); + else if (server.properties.virtualserver_codec_encryption_mode == 1) + container.text(_translations.awuXKKzF || (_translations.awuXKKzF = tr("Individually configured per channel"))); + else + container.text(_translations.Fxejq6JG || (_translations.Fxejq6JG = tr("Globally on"))); + } + /* channel count */ + { + const container = tag.find(".server-channel-count"); + container.text(server.properties.virtualserver_channelsonline); + } + /* minimal security level */ + { + const container = tag.find(".server-min-security-level"); + container.text(server.properties.virtualserver_needed_identity_security_level); + } + /* complains */ + { + const container = tag.find(".server-complains"); + container.text(server.properties.virtualserver_complain_autoban_count); + } + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["96c184c097607021ac37903e53735d6fda33133b10545b2f6996c468c42fb041"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["96c184c097607021ac37903e53735d6fda33133b10545b2f6996c468c42fb041"] = "96c184c097607021ac37903e53735d6fda33133b10545b2f6996c468c42fb041"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "fEMH56K2", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfoBandwidth.ts (14,21)" }, { name: "E1HApHoa", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfoBandwidth.ts (58,101)" }, { name: "Zw6Mw8Ek", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfoBandwidth.ts (58,123)" }, { name: "UZm_3vp7", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfoBandwidth.ts (153,26)" }, { name: "yZmlolTK", path: "D:/TeaSpeak/web/shared/js/ui/modal/ModalServerInfoBandwidth.ts (155,26)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var Modals; +(function (Modals) { + let RequestInfoStatus; + (function (RequestInfoStatus) { + RequestInfoStatus[RequestInfoStatus["SUCCESS"] = 0] = "SUCCESS"; + RequestInfoStatus[RequestInfoStatus["UNKNOWN"] = 1] = "UNKNOWN"; + RequestInfoStatus[RequestInfoStatus["NO_PERMISSION"] = 2] = "NO_PERMISSION"; + })(RequestInfoStatus = Modals.RequestInfoStatus || (Modals.RequestInfoStatus = {})); + function openServerInfoBandwidth(server, update_callbacks) { + let modal; + let own_callbacks = !update_callbacks; + update_callbacks = update_callbacks || []; + modal = createModal({ + header: _translations.fEMH56K2 || (_translations.fEMH56K2 = tr("Server bandwidth data")), + body: () => { + const template = $("#tmpl_server_info_bandwidth").renderTag(); + const children = template.children(); + initialize_current_bandwidth(modal, children.find(".statistic-bandwidth"), update_callbacks); + initialize_ft_bandwidth(modal, children.find(".statistic-ft-bandwidth"), update_callbacks); + initialize_general(template.find(".top"), update_callbacks); + tooltip(template); + return template.children(); + }, + footer: null, + min_width: "25em" + }); + if (own_callbacks) { + const updater = setInterval(() => { + server.request_connection_info().then(info => update_callbacks.forEach(e => e(RequestInfoStatus.SUCCESS, info))).catch(error => { + if (error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + update_callbacks.forEach(e => e(RequestInfoStatus.NO_PERMISSION)); + return; + } + update_callbacks.forEach(e => e(RequestInfoStatus.UNKNOWN)); + }); + }, 1000); + modal.close_listener.push(() => clearInterval(updater)); + } + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".modal-body").addClass("modal-server-info-bandwidth"); + modal.open(); + return modal; + } + Modals.openServerInfoBandwidth = openServerInfoBandwidth; + function initialize_graph(modal, tag, callbacks, fields) { + const canvas = tag.find("canvas")[0]; + const label_upload = tag.find(".upload"); + const label_download = tag.find(".download"); + let last_info; + let custom_info = false; + const show_info = (upload, download) => { + let fallback_text = last_info && last_info.status === RequestInfoStatus.NO_PERMISSION ? _translations.E1HApHoa || (_translations.E1HApHoa = tr("No permission")) : _translations.Zw6Mw8Ek || (_translations.Zw6Mw8Ek = tr("receiving...")); + if (typeof upload !== "number") + upload = last_info ? last_info[fields.uplaod] : undefined; + if (typeof download !== "number") + download = last_info ? last_info[fields.download] : undefined; + if (typeof upload !== "number") + label_upload.text(fallback_text); + else + label_upload.text(MessageHelper.network.format_bytes(upload, { unit: "Bytes", time: "s", exact: false })); + if (typeof download !== "number") + label_download.text(fallback_text); + else + label_download.text(MessageHelper.network.format_bytes(download, { unit: "Bytes", time: "s", exact: false })); + }; + show_info(undefined, undefined); + const graph = new net.graph.Graph(canvas); + graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined }); + callbacks.push((status, values) => { + last_info = { status: status, info: values }; + if (!values) { + graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined }); + } + else { + graph.insert_entry({ + timestamp: Date.now(), + download: values[fields.download], + upload: values[fields.uplaod], + }); + } + /* set set that we want to show the entry within one second */ + graph._time_span.origin = Object.assign(graph.calculate_time_span(), { time: Date.now() }); + graph._time_span.target = { + begin: Date.now() - 120 * 1000, + end: Date.now(), + time: Date.now() + 200 + }; + graph.cleanup(); + if (!custom_info) { + show_info(undefined, undefined); + graph.resize(); /* just to ensure (we have to rethink this maybe; cause it causes a recalculates the style */ + } + }); + graph.max_gap_size(0); + graph.initialize(); + graph.callback_detailed_hide = () => { + custom_info = false; + show_info(undefined, undefined); + }; + graph.callback_detailed_info = (upload, download, timestamp, event) => { + custom_info = true; + show_info(upload, download); + }; + modal.close_listener.push(() => graph.terminate()); + modal.open_listener.push(() => graph.resize()); + tag.addClass("window-resize-listener").on('resize', event => graph.resize()); + } + function initialize_current_bandwidth(modal, tag, callbacks) { + initialize_graph(modal, tag, callbacks, { + uplaod: "connection_bandwidth_sent_last_second_total", + download: "connection_bandwidth_received_last_second_total" + }); + } + function initialize_ft_bandwidth(modal, tag, callbacks) { + initialize_graph(modal, tag, callbacks, { + uplaod: "connection_filetransfer_bandwidth_sent", + download: "connection_filetransfer_bandwidth_received" + }); + } + function initialize_general(tag, callbacks) { + const tag_packets_upload = tag.find(".statistic-packets .upload"); + const tag_packets_download = tag.find(".statistic-packets .download"); + const tag_bytes_upload = tag.find(".statistic-bytes .upload"); + const tag_bytes_download = tag.find(".statistic-bytes .download"); + const tag_ft_bytes_upload = tag.find(".statistic-ft-bytes .upload"); + const tag_ft_bytes_download = tag.find(".statistic-ft-bytes .download"); + const update = (tag, value) => { + if (typeof value === "undefined") + tag.text(_translations.UZm_3vp7 || (_translations.UZm_3vp7 = tr("receiving..."))); + else if (value === null) + tag.text(_translations.yZmlolTK || (_translations.yZmlolTK = tr("no permissions"))); + else + tag.text(MessageHelper.network.format_bytes(value, { unit: "Bytes", exact: false })); + }; + const props = [ + { tag: tag_packets_download, property: "connection_packets_received_total" }, + { tag: tag_packets_upload, property: "connection_packets_sent_total" }, + { tag: tag_bytes_download, property: "connection_bytes_received_total" }, + { tag: tag_bytes_upload, property: "connection_bytes_sent_total" }, + { tag: tag_ft_bytes_upload, property: "connection_filetransfer_bytes_received_total" }, + { tag: tag_ft_bytes_download, property: "connection_filetransfer_bytes_sent_total" }, + ]; + callbacks.push((status, info) => { + if (status === RequestInfoStatus.SUCCESS) { + for (const entry of props) + update(entry.tag, info[entry.property]); + } + else if (status === RequestInfoStatus.NO_PERMISSION) { + for (const entry of props) + update(entry.tag, null); + } + else { + for (const entry of props) + update(entry.tag, undefined); + } + }); + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["f08e46c4af644ceb3228f5583f0b483d9ab6d5f4699e3aa67659a9474d21ffab"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["f08e46c4af644ceb3228f5583f0b483d9ab6d5f4699e3aa67659a9474d21ffab"] = "f08e46c4af644ceb3228f5583f0b483d9ab6d5f4699e3aa67659a9474d21ffab"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "jgvFVcHz", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (94,24)" }, { name: "dcBTd0ku", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (270,63)" }, { name: "JpVF5Mzm", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (281,63)" }, { name: "ViyQ3FC3", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (296,63)" }, { name: "t7rGoAdc", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (313,63)" }, { name: "eKiUcJRA", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (433,63)" }, { name: "gNpxBbWx", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (443,63)" }, { name: "Oldifku2", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (457,63)" }, { name: "JAIS9OGe", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (473,63)" }, { name: "tDmnoexV", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (575,63)" }, { name: "xDRYefpc", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (590,63)" }, { name: "cYgmPzVb", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (604,63)" }, { name: "MyTOVvJi", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (625,63)" }, { name: "eH6peQwT", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (689,63)" }, { name: "PSWt_oAu", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (703,63)" }, { name: "Z5NC2zLF", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (717,63)" }, { name: "hdpOwMqg", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (737,63)" }, { name: "uyiLQc3y", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (808,35)" }, { name: "nuh4sliq", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (814,35)" }, { name: "zyTnu_qn", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (820,35)" }, { name: "qlgoozon", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (825,35)" }, { name: "Sp05Q7cm", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (850,27)" }, { name: "BDKgOv3v", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (881,41)" }, { name: "asEzKP4X", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (881,62)" }, { name: "ZmlrpljU", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (884,38)" }, { name: "zGFSOJO8", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (888,42)" }, { name: "ZxKo1R5a", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (888,100)" }, { name: "K7C5wnF2", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (897,34)" }, { name: "q1LAQS2V", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (897,54)" }, { name: "l_zRubf8", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (904,41)" }, { name: "jZWebIbu", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (904,62)" }, { name: "wKyPu_a7", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (907,38)" }, { name: "EOkDrLst", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (911,42)" }, { name: "wJbuc7yI", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (911,100)" }, { name: "aUVlGWcp", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (917,34)" }, { name: "KsmwzQ7d", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (917,61)" }, { name: "ABUF6IGq", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (924,28)" }, { name: "y2KLYSW7", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (924,77)" }, { name: "vTy1M6TK", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (932,41)" }, { name: "LzPrH7f2", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (932,62)" }, { name: "ExJ0DQQB", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (935,38)" }, { name: "pFsB8a2X", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (939,42)" }, { name: "PIpmf1Jv", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (939,100)" }, { name: "ZKRKRFdZ", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1011,35)" }, { name: "tz_xB0DK", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1017,35)" }, { name: "shQXAD3I", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1023,35)" }, { name: "N5puAby8", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1028,35)" }, { name: "wvb9mJQm", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1055,27)" }, { name: "BNdJT1cT", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1085,41)" }, { name: "PzBumF3C", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1085,62)" }, { name: "sNlSiT7C", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1088,38)" }, { name: "usfuFq3a", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1092,42)" }, { name: "XGuZ4MEv", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1092,100)" }, { name: "u4Fvc7m6", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1101,34)" }, { name: "ORzt406c", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1101,54)" }, { name: "sGFYk3BV", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1108,41)" }, { name: "Z6K53vUj", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1108,62)" }, { name: "Nvr1sNU1", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1111,38)" }, { name: "gZuGhFeW", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1115,42)" }, { name: "RAahtMoY", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1115,100)" }, { name: "JFTntZDs", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1121,34)" }, { name: "XSwWUXyj", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1121,61)" }, { name: "BtRlLNMi", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1128,28)" }, { name: "DjQ0gUq3", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1128,77)" }, { name: "Skc29nKb", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1136,41)" }, { name: "e9uYZsgU", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1136,62)" }, { name: "p5NZb93F", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1139,38)" }, { name: "Xe4iGn25", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1143,42)" }, { name: "QzcGXAWO", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1143,100)" }, { name: "rvTpLnM3", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1175,63)" }, { name: "lNfsBK1q", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1190,63)" }, { name: "l3qUWBQ2", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1204,63)" }, { name: "mH6z0xG8", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1225,63)" }, { name: "VSkQ3V09", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1310,39)" }, { name: "z40xhzvX", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1315,39)" }, { name: "RnTroWBE", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1320,39)" }, { name: "JrlJsqem", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1330,34)" }, { name: "QYvEqWFo", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1346,33)" }, { name: "w_grwuKP", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1349,38)" }, { name: "Xqg78pAj", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1349,69)" }, { name: "vp8oLTFs", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1354,34)" }, { name: "bqhbv6gi", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1354,68)" }, { name: "IaBow48n", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1376,41)" }, { name: "qXfwiEGs", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1379,46)" }, { name: "xvC5Mz5O", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1379,102)" }, { name: "RTOwbHRq", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1384,37)" }, { name: "Fqjboqd3", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1385,42)" }, { name: "E8Wmhi8B", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1385,70)" }, { name: "kwFlKJU2", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1396,37)" }, { name: "iBC3dxeB", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1399,42)" }, { name: "HHOHRQs8", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1411,27)" }, { name: "XhSxnxAW", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1445,46)" }, { name: "SJnJGsYd", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1445,76)" }, { name: "nztvRGaW", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1446,38)" }, { name: "ouOjBgEH", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1446,68)" }, { name: "Ombfpy2D", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/ModalPermissionEdit.ts (1454,21)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +/// +/// +var Modals; +(function (Modals) { + let PermissionEditorMode; + (function (PermissionEditorMode) { + PermissionEditorMode[PermissionEditorMode["VISIBLE"] = 0] = "VISIBLE"; + PermissionEditorMode[PermissionEditorMode["NO_PERMISSION"] = 1] = "NO_PERMISSION"; + PermissionEditorMode[PermissionEditorMode["UNSET"] = 2] = "UNSET"; + })(PermissionEditorMode = Modals.PermissionEditorMode || (Modals.PermissionEditorMode = {})); + class AbstractPermissionEditor { + constructor() { + this._listener_change = () => Promise.resolve(); + } + set_listener(listener) { + this._listener_change = listener || (() => Promise.resolve()); + } + set_listener_update(listener) { this._listener_update = listener; } + trigger_update() { + if (this._listener_update) + this._listener_update(); + } + } + Modals.AbstractPermissionEditor = AbstractPermissionEditor; + function _space() { + const now = Date.now(); + while (now + 100 > Date.now()) + ; + } + Modals._space = _space; + function spawnPermissionEdit(connection, selected_tab, options) { + options = options || {}; + const modal = createModal({ + header: function () { + return _translations.jgvFVcHz || (_translations.jgvFVcHz = tr("Server Permissions")); + }, + body: function () { + let properties = {}; + let tag = $("#tmpl_server_permissions").renderTag(properties); + /* build the permission editor */ + const permission_editor = (() => { + const editor = new pe.HTMLPermissionEditor(); + editor.initialize(connection.permissions.groupedPermissions()); + editor.icon_resolver = id => connection.fileManager.icons.resolve_icon(id).then((icon) => __awaiter(this, void 0, void 0, function* () { + if (!icon) + return undefined; + const tag = document.createElement("img"); + yield new Promise((resolve, reject) => { + tag.onerror = reject; + tag.onload = resolve; + tag.src = icon.url; + }); + return tag; + })); + editor.icon_selector = current_icon => new Promise(resolve => { + Modals.spawnIconSelect(connection, id => resolve(new Int32Array([id])[0]), current_icon); + }); + if (editor instanceof pe.CanvasPermissionEditor) + setTimeout(() => editor.update_ui(), 500); + return editor; + })(); + const container_tab_list = tag.find(".right > .header"); + { + const label_current = tag.find(".left .container-selected"); + const create_tab = (tab_entry, container_name, hidden_permissions) => { + const target_container = tag.find(".body .container." + container_name); + tab_entry.on('click', () => { + /* Using a timeout here prevents unnecessary style calculations required by other click event handlers */ + setTimeout(() => { + container_tab_list.find(".selected").removeClass("selected"); + tab_entry.addClass("selected"); + label_current.text(tab_entry.find("a").text()); + /* dont use show() here because it causes a style recalculation */ + for (const element of tag.find(".body .container")) + element.style.display = "none"; + permission_editor.set_hidden_permissions(settings.static_global(Settings.KEY_PERMISSIONS_SHOW_ALL) ? undefined : hidden_permissions); + permission_editor.html_tag()[0].remove(); + target_container.find(".permission-editor").trigger('show'); + target_container.find(".permission-editor").append(permission_editor.html_tag()); + for (const element of target_container) + element.style.display = null; + }, 0); + }); + }; + create_tab(container_tab_list.find(".sg"), "container-view-server-groups", permissions.senseless_server_group_permissions); + create_tab(container_tab_list.find(".cg"), "container-view-channel-groups", permissions.senseless_channel_group_permissions); + create_tab(container_tab_list.find(".chp"), "container-view-channel-permissions", permissions.senseless_channel_permissions); + create_tab(container_tab_list.find(".clp"), "container-view-client-permissions", permissions.senseless_client_permissions); + create_tab(container_tab_list.find(".clchp"), "container-view-client-channel-permissions", permissions.senseless_client_channel_permissions); + } + apply_server_groups(connection, permission_editor, tag.find(".left .container-view-server-groups"), tag.find(".right .container-view-server-groups")); + apply_channel_groups(connection, permission_editor, tag.find(".left .container-view-channel-groups"), tag.find(".right .container-view-channel-groups")); + apply_channel_permission(connection, permission_editor, tag.find(".left .container-view-channel-permissions"), tag.find(".right .container-view-channel-permissions")); + apply_client_permission(connection, permission_editor, tag.find(".left .container-view-client-permissions"), tag.find(".right .container-view-client-permissions"), selected_tab == "clp" ? options : {}); + apply_client_channel_permission(connection, permission_editor, tag.find(".left .container-view-client-channel-permissions"), tag.find(".right .container-view-client-channel-permissions"), selected_tab == "clchp" ? options : {}); + setTimeout(() => container_tab_list.find("." + (selected_tab || "sg")).trigger('click'), 0); + return tag.dividerfy(); + }, + footer: undefined, + min_width: "30em", + height: "80%", + trigger_tab: false, + full_size: true + }); + const tag = modal.htmlTag; + tag.find(".modal-body").addClass("modal-permission-editor"); + if (selected_tab) + setTimeout(() => tag.find(".tab-header .entry[x-id=" + selected_tab + "]").first().trigger("click"), 1); + tag.find(".btn_close").on('click', () => { + modal.close(); + }); + return modal; + } + Modals.spawnPermissionEdit = spawnPermissionEdit; + function build_channel_tree(connection, channel_list, selected_channel, select_callback) { + const root = connection.channelTree.get_first_channel(); + if (!root) + return; + const build_channel = (channel, level) => { + let tag = $.spawn("div").addClass("channel").css("padding-left", "calc(0.25em + " + (level * 16) + "px)").attr("channel-id", channel.channelId); + let icon_tag = connection.fileManager.icons.generateTag(channel.properties.channel_icon_id); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + { + let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name"); + name.appendTo(tag); + } + tag.on('click', event => { + channel_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + select_callback(channel, _update_icon); + }); + return tag; + }; + const build_channels = (root, level) => { + build_channel(root, level).appendTo(channel_list); + const child_head = root.children(false).find(e => e.channel_previous === undefined); + if (child_head) + build_channels(child_head, level + 1); + if (root.channel_next) + build_channels(root.channel_next, level); + }; + build_channels(root, 0); + let selected_channel_tag = channel_list.find(".channel[channel-id=" + selected_channel + "]"); + if (!selected_channel_tag || selected_channel_tag.length < 1) + selected_channel_tag = channel_list.find('.channel').first(); + setTimeout(() => selected_channel_tag.trigger('click'), 0); + } + function apply_client_channel_permission(connection, editor, tab_left, tab_right, options) { + let current_cldbid = 0; + let current_channel; + /* the editor */ + { + const pe_client = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_client.append(editor.html_tag()); + if (connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { + if (current_cldbid && current_channel) + editor.set_mode(PermissionEditorMode.VISIBLE); + else + editor.set_mode(PermissionEditorMode.UNSET); + } + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + if (!current_cldbid || !current_channel) + return; + connection.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => { + editor.set_permissions(result); + editor.set_mode(PermissionEditorMode.VISIBLE); + }).catch(error => { + console.log(error); //TODO handling? + }); + }); + /* TODO: Error handling? */ + editor.set_listener((permission, value) => __awaiter(this, void 0, void 0, function* () { + if (!current_cldbid) + throw "unset client"; + if (!current_channel) + throw "unset channel"; + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.dcBTd0ku || (_translations.dcBTd0ku = tr("Removing client channel permission %s. permission.id: %o")), permission.name, permission.id); + yield connection.serverConnection.send_command("channelclientdelperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id, + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.JpVF5Mzm || (_translations.JpVF5Mzm = tr("Removing client channel grant permission %s. permission.id: %o")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channelclientdelperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id_grant(), + }); + } + } + else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.ViyQ3FC3 || (_translations.ViyQ3FC3 = tr("Adding or updating client channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}")), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate); + yield connection.serverConnection.send_command("channelclientaddperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.t7rGoAdc || (_translations.t7rGoAdc = tr("Adding or updating client channel grant permission %s. permission.{id: %o, value: %o}")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channelclientaddperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + })); + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + build_channel_tree(connection, tab_left.find(".list-channel .entries"), options.channel_id || 0, channel => { + if (current_channel == channel) + return; + current_channel = channel; + /* TODO: Test for visibility */ + editor.trigger_update(); + }); + { + const tag_select = tab_left.find(".client-select"); + const tag_select_uid = tag_select.find("input"); + const tag_select_error = tag_select.find(".invalid-feedback"); + const tag_client_name = tab_left.find(".client-name"); + const tag_client_uid = tab_left.find(".client-uid"); + const tag_client_dbid = tab_left.find(".client-dbid"); + const resolve_client = () => { + let client_uid = tag_select_uid.val(); + connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { + if (!result || result.length == 0) + return Promise.reject("invalid data"); + tag_select.removeClass('is-invalid'); + tag_client_name.val(result[0].client_nickname); + tag_client_uid.val(result[0].client_unique_id); + tag_client_dbid.val(result[0].client_database_id); + current_cldbid = result[0].client_database_id; + editor.trigger_update(); + }).catch(error => { + console.log(error); + if (error instanceof CommandResult) { + if (error.id == ErrorID.EMPTY_RESULT) + error = "unknown client"; + else + error = error.extra_message || error.message; + } + tag_client_name.val(""); + tag_client_uid.val(""); + tag_client_dbid.val(""); + tag_select_error.text(error); + tag_select.addClass('is-invalid'); + editor.set_mode(PermissionEditorMode.UNSET); + }); + }; + tag_select_uid.on('change', event => resolve_client()); + if (options.unique_id) { + tag_select_uid.val(options.unique_id); + setTimeout(() => resolve_client()); + } + } + } + function apply_client_permission(connection, editor, tab_left, tab_right, options) { + let current_cldbid = 0; + /* the editor */ + { + const pe_client = tab_right.find("permission-editor.client"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_client.append(editor.html_tag()); + if (connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { + if (current_cldbid) + editor.set_mode(PermissionEditorMode.VISIBLE); + else + editor.set_mode(PermissionEditorMode.UNSET); + } + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + if (!current_cldbid) + return; + connection.permissions.requestClientPermissions(current_cldbid).then(result => { + editor.set_permissions(result); + editor.set_mode(PermissionEditorMode.VISIBLE); + }).catch(error => { + console.log(error); //TODO handling? + }); + }); + /* TODO: Error handling? */ + editor.set_listener((permission, value) => __awaiter(this, void 0, void 0, function* () { + if (!current_cldbid) + throw "unset client"; + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.eKiUcJRA || (_translations.eKiUcJRA = tr("Removing client permission %s. permission.id: %o")), permission.name, permission.id); + yield connection.serverConnection.send_command("clientdelperm", { + cldbid: current_cldbid, + permid: permission.id, + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.gNpxBbWx || (_translations.gNpxBbWx = tr("Removing client grant permission %s. permission.id: %o")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("clientdelperm", { + cldbid: current_cldbid, + permid: permission.id_grant(), + }); + } + } + else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.Oldifku2 || (_translations.Oldifku2 = tr("Adding or updating client permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}")), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate); + yield connection.serverConnection.send_command("clientaddperm", { + cldbid: current_cldbid, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.JAIS9OGe || (_translations.JAIS9OGe = tr("Adding or updating client grant permission %s. permission.{id: %o, value: %o}")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("clientaddperm", { + cldbid: current_cldbid, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + })); + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + const tag_select = tab_left.find(".client-select"); + const tag_select_uid = tag_select.find("input"); + const tag_select_error = tag_select.find(".invalid-feedback"); + const tag_client_name = tab_left.find(".client-name"); + const tag_client_uid = tab_left.find(".client-uid"); + const tag_client_dbid = tab_left.find(".client-dbid"); + const resolve_client = () => { + let client_uid = tag_select_uid.val(); + connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { + if (!result || result.length == 0) + return Promise.reject("invalid data"); + tag_select.removeClass("is-invalid"); + tag_client_name.val(result[0].client_nickname); + tag_client_uid.val(result[0].client_unique_id); + tag_client_dbid.val(result[0].client_database_id); + current_cldbid = result[0].client_database_id; + editor.trigger_update(); + }).catch(error => { + console.log(error); + if (error instanceof CommandResult) { + if (error.id == ErrorID.EMPTY_RESULT) + error = "unknown client"; + else + error = error.extra_message || error.message; + } + tag_client_name.val(""); + tag_client_uid.val(""); + tag_client_dbid.val(""); + tag_select_error.text(error); + tag_select.addClass("is-invalid"); + editor.set_mode(PermissionEditorMode.UNSET); + }); + }; + tag_select_uid.on('change', event => resolve_client()); + if (options.unique_id) { + tag_select_uid.val(options.unique_id); + setTimeout(() => resolve_client()); + } + } + function apply_channel_permission(connection, editor, tab_left, tab_right) { + let current_channel; + let update_channel_icon; + /* the editor */ + { + const pe_channel = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_channel.append(editor.html_tag()); + if (connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + if (!current_channel) + return; + connection.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => { + editor.set_permissions([]); + console.log(error); //TODO handling? + }); + }); + editor.set_listener((permission, value) => __awaiter(this, void 0, void 0, function* () { + if (!current_channel) + throw "unset channel"; + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.tDmnoexV || (_translations.tDmnoexV = tr("Removing channel permission %s. permission.id: %o")), permission.name, permission.id); + yield connection.serverConnection.send_command("channeldelperm", { + cid: current_channel.channelId, + permid: permission.id, + }).then(e => { + if (permission.name === "i_icon_id" && update_channel_icon) + update_channel_icon(undefined); + return e; + }); + } + else { + /* TODO Remove this because its totally useless. Remove this from the UI as well */ + log.info(LogCategory.PERMISSIONS, _translations.xDRYefpc || (_translations.xDRYefpc = tr("Removing channel grant permission %s. permission.id: %o")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channeldelperm", { + cid: current_channel.channelId, + permid: permission.id_grant(), + }); + } + } + else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.cYgmPzVb || (_translations.cYgmPzVb = tr("Adding or updating channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}")), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate); + yield connection.serverConnection.send_command("channeladdperm", { + cid: current_channel.channelId, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if (permission.name === "i_icon_id" && update_channel_icon) + update_channel_icon(value.value); + return e; + }); + } + else { + /* TODO Remove this because its totally useless. Remove this from the UI as well */ + log.info(LogCategory.PERMISSIONS, _translations.MyTOVvJi || (_translations.MyTOVvJi = tr("Adding or updating channel grant permission %s. permission.{id: %o, value: %o}")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channeladdperm", { + cid: current_channel.channelId, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + })); + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + let channel_list = tab_left.find(".list-channel .entries"); + build_channel_tree(connection, channel_list, 0, (channel, update) => { + current_channel = channel; + update_channel_icon = update; + editor.trigger_update(); + }); + } + function apply_channel_groups(connection, editor, tab_left, tab_right) { + let current_group; + let update_group_icon; + let update_groups; + let update_buttons; + /* the editor */ + { + const pe_server = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_server.append(editor.html_tag()); + if (connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + if (!current_group) + return; + connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { + console.log(error); //TODO handling? + }); + }); + editor.set_listener((permission, value) => __awaiter(this, void 0, void 0, function* () { + if (!current_group) + throw "unset channel group"; + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.eH6peQwT || (_translations.eH6peQwT = tr("Removing channel group permission %s. permission.id: %o")), permission.name, permission.id); + yield connection.serverConnection.send_command("channelgroupdelperm", { + cgid: current_group.id, + permid: permission.id, + }).then(e => { + if (permission.name === "i_icon_id" && update_group_icon) + update_group_icon(undefined); + return e; + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.PSWt_oAu || (_translations.PSWt_oAu = tr("Removing channel group grant permission %s. permission.id: %o")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channelgroupdelperm", { + cgid: current_group.id, + permid: permission.id_grant(), + }); + } + } + else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.Z5NC2zLF || (_translations.Z5NC2zLF = tr("Adding or updating channel group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}")), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate); + yield connection.serverConnection.send_command("channelgroupaddperm", { + cgid: current_group.id, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if (permission.name === "i_icon_id" && update_group_icon) + update_group_icon(value.value); + return e; + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.hdpOwMqg || (_translations.hdpOwMqg = tr("Adding or updating channel group grant permission %s. permission.{id: %o, value: %o}")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("channelgroupaddperm", { + cgid: current_group.id, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + })); + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + /* list all channel groups */ + { + let group_list = tab_left.find(".list-groups .entries"); + update_groups = (selected_group) => { + group_list.children().remove(); + const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); + const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); + for (let group of connection.groups.channelGroups.sort(GroupManager.sorter())) { + if (group.type == GroupType.QUERY) { + if (!allow_query_groups) + continue; + } + else if (group.type == GroupType.TEMPLATE) { + if (!allow_template_groups) + continue; + } + let tag = $.spawn("div").addClass("group").attr("group-id", group.id); + let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + { + let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name"); + if (group.properties.savedb) + name.addClass("savedb"); + if (connection.channelTree.server.properties.virtualserver_default_channel_group == group.id) + name.addClass("default"); + name.appendTo(tag); + } + tag.appendTo(group_list); + tag.on('click', event => { + current_group = group; + update_group_icon = _update_icon; + group_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + update_buttons(); + //TODO trigger only if the editor is in channel group mode! + editor.trigger_update(); + }); + tag.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.uyiLQc3y || (_translations.uyiLQc3y = tr("Create a channel group")), + icon_class: 'client-add', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1), + callback: () => tab_left.find(".button-add").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.nuh4sliq || (_translations.nuh4sliq = tr("Rename channel group")), + icon_class: 'client-edit', + invalidPermission: !connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), + callback: () => tab_left.find(".button-rename").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.zyTnu_qn || (_translations.zyTnu_qn = tr("Duplicate channel group")), + icon_class: 'client-copy', + callback: () => tab_left.find(".button-duplicate").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.qlgoozon || (_translations.qlgoozon = tr("Delete channel group")), + icon_class: 'client-delete', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_DELETE).granted(1), + callback: () => tab_left.find(".button-delete").trigger('click') + }); + event.preventDefault(); + }); + if (group.id === selected_group) { + setTimeout(() => tag.trigger('click'), 0); + selected_group = undefined; + } + } + /* because the server menu is the first which will be shown */ + if (typeof (selected_group) !== "undefined") { + setTimeout(() => group_list.find('.group').first().trigger('click'), 0); + } + }; + tab_left.find(".list-groups").on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.Sp05Q7cm || (_translations.Sp05Q7cm = tr("Create a channel group")), + icon_class: 'client-add', + callback: () => tab_left.find(".button-add").trigger('click') + }); + event.preventDefault(); + }); + } + { + const container_buttons = tab_left.find(".container-buttons"); + const button_add = container_buttons.find(".button-add"); + const button_rename = container_buttons.find(".button-rename"); + const button_duplicate = container_buttons.find(".button-duplicate"); + const button_delete = container_buttons.find(".button-delete"); + button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + update_buttons = () => { + const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); + button_rename.prop("disabled", !permission_modify); + button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + }; + button_add.on('click', () => { + spawnGroupAdd(false, connection.permissions, (name, type) => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == type), (name, type) => { + console.log("Creating channel group: %o, %o", name, type); + connection.serverConnection.send_command('channelgroupadd', { + name: name, + type: type + }).then(() => { + createInfoModal(_translations.BDKgOv3v || (_translations.BDKgOv3v = tr("Group created")), _translations.asEzKP4X || (_translations.asEzKP4X = tr("The channel group has been created."))).open(); + update_groups(0); //TODO: May get the created group? + }).catch(error => { + console.warn(_translations.ZmlrpljU || (_translations.ZmlrpljU = tr("Failed to create channel group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.zGFSOJO8 || (_translations.zGFSOJO8 = tr("Failed to create group")), MessageHelper.formatMessage(_translations.ZxKo1R5a || (_translations.ZxKo1R5a = tr("Failed to create group:{:br:}")), error)).open(); + }); + }); + }); + button_rename.on('click', () => { + if (!current_group) + return; + createInputModal(_translations.K7C5wnF2 || (_translations.K7C5wnF2 = tr("Rename group")), _translations.q1LAQS2V || (_translations.q1LAQS2V = tr("Enter the new group name")), name => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { + if (typeof (result) !== "string" || !result) + return; + connection.serverConnection.send_command('channelgrouprename', { + cgid: current_group.id, + name: result + }).then(() => { + createInfoModal(_translations.l_zRubf8 || (_translations.l_zRubf8 = tr("Group renamed")), _translations.jZWebIbu || (_translations.jZWebIbu = tr("The channel group has been renamed."))).open(); + update_groups(current_group.id); + }).catch(error => { + console.warn(_translations.wKyPu_a7 || (_translations.wKyPu_a7 = tr("Failed to rename channel group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.EOkDrLst || (_translations.EOkDrLst = tr("Failed to rename group")), MessageHelper.formatMessage(_translations.wJbuc7yI || (_translations.wJbuc7yI = tr("Failed to rename group:{:br:}")), error)).open(); + }); + }).open(); + }); + button_duplicate.on('click', () => { + createErrorModal(_translations.aUVlGWcp || (_translations.aUVlGWcp = tr("Not implemented yet")), _translations.KsmwzQ7d || (_translations.KsmwzQ7d = tr("This function hasn't been implemented yet!"))).open(); + }); + button_delete.on('click', () => { + if (!current_group) + return; + Modals.spawnYesNo(_translations.ABUF6IGq || (_translations.ABUF6IGq = tr("Are you sure?")), MessageHelper.formatMessage(_translations.y2KLYSW7 || (_translations.y2KLYSW7 = tr("Do you really want to delete the group {}?")), current_group.name), result => { + if (result !== true) + return; + connection.serverConnection.send_command("channelgroupdel", { + cgid: current_group.id, + force: true + }).then(() => { + createInfoModal(_translations.vTy1M6TK || (_translations.vTy1M6TK = tr("Group deleted")), _translations.LzPrH7f2 || (_translations.LzPrH7f2 = tr("The channel group has been deleted."))).open(); + update_groups(0); + }).catch(error => { + console.warn(_translations.ExJ0DQQB || (_translations.ExJ0DQQB = tr("Failed to delete channel group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.pFsB8a2X || (_translations.pFsB8a2X = tr("Failed to delete group")), MessageHelper.formatMessage(_translations.PIpmf1Jv || (_translations.PIpmf1Jv = tr("Failed to delete group:{:br:}")), error)).open(); + }); + }); + }); + } + update_groups(0); + } + function apply_server_groups(connection, editor, tab_left, tab_right) { + let current_group; + let current_group_changed = []; + let update_buttons; + /* list all groups */ + let update_icon = []; + let update_groups; + { + let group_list = tab_left.find(".container-group-list .list-groups .entries"); + let group_list_update_icon; + update_icon.push(i => group_list_update_icon(i)); + update_groups = (selected_group) => { + group_list.children().remove(); + const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); + const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); + for (const group of connection.groups.serverGroups.sort(GroupManager.sorter())) { + if (group.type == GroupType.QUERY) { + if (!allow_query_groups) + continue; + } + else if (group.type == GroupType.TEMPLATE) { + if (!allow_template_groups) + continue; + } + let tag = $.spawn("div").addClass("group").attr("group-id", group.id); + let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + { + let name = $.spawn("div").text(group.name + " (" + group.id + ")").addClass("name"); + if (group.properties.savedb) + name.addClass("savedb"); + if (connection.channelTree.server.properties.virtualserver_default_server_group == group.id) + name.addClass("default"); + name.appendTo(tag); + } + tag.appendTo(group_list); + tag.on('click', event => { + if (current_group === group) + return; + current_group = group; + group_list_update_icon = _update_icon; + if (update_buttons) + update_buttons(); + for (const entry of current_group_changed) + entry(); + group_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + editor.trigger_update(); + }); + tag.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.ZKRKRFdZ || (_translations.ZKRKRFdZ = tr("Create a server group")), + icon_class: 'client-add', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1), + callback: () => tab_left.find(".button-add").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.tz_xB0DK || (_translations.tz_xB0DK = tr("Rename server group")), + icon_class: 'client-edit', + invalidPermission: !connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), + callback: () => tab_left.find(".button-rename").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.shQXAD3I || (_translations.shQXAD3I = tr("Duplicate server group")), + icon_class: 'client-copy', + callback: () => tab_left.find(".button-duplicate").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.N5puAby8 || (_translations.N5puAby8 = tr("Delete server group")), + icon_class: 'client-delete', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1), + callback: () => tab_left.find(".button-delete").trigger('click') + }); + event.preventDefault(); + }); + if (group.id === selected_group) { + setTimeout(() => tag.trigger('click'), 0); + selected_group = undefined; + } + } + /* because the server menu is the first which will be shown */ + if (typeof (selected_group) !== "undefined") { + setTimeout(() => group_list.find('.group').first().trigger('click'), 0); + } + }; + tab_left.find(".list-groups").on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.wvb9mJQm || (_translations.wvb9mJQm = tr("Create a server group")), + icon_class: 'client-add', + callback: () => tab_left.find(".button-add").trigger('click') + }); + event.preventDefault(); + }); + } + { + const container_buttons = tab_left.find(".container-group-list .container-buttons"); + const button_add = container_buttons.find(".button-add"); + const button_rename = container_buttons.find(".button-rename"); + const button_duplicate = container_buttons.find(".button-duplicate"); + const button_delete = container_buttons.find(".button-delete"); + button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); + button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1)); + update_buttons = () => { + const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); + button_rename.prop("disabled", !permission_modify); + button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); + }; + button_add.on('click', () => { + spawnGroupAdd(true, connection.permissions, (name, type) => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == type), (name, type) => { + console.log("Creating group: %o, %o", name, type); + connection.serverConnection.send_command('servergroupadd', { + name: name, + type: type + }).then(() => { + createInfoModal(_translations.BNdJT1cT || (_translations.BNdJT1cT = tr("Group created")), _translations.PzBumF3C || (_translations.PzBumF3C = tr("The server group has been created."))).open(); + update_groups(0); //TODO: May get the created group? + }).catch(error => { + console.warn(_translations.sNlSiT7C || (_translations.sNlSiT7C = tr("Failed to create server group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.usfuFq3a || (_translations.usfuFq3a = tr("Failed to create group")), MessageHelper.formatMessage(_translations.XGuZ4MEv || (_translations.XGuZ4MEv = tr("Failed to create group:{:br:}")), error)).open(); + }); + }); + }); + button_rename.on('click', () => { + if (!current_group) + return; + createInputModal(_translations.u4Fvc7m6 || (_translations.u4Fvc7m6 = tr("Rename group")), _translations.ORzt406c || (_translations.ORzt406c = tr("Enter the new group name")), name => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { + if (typeof (result) !== "string" || !result) + return; + connection.serverConnection.send_command('servergrouprename', { + sgid: current_group.id, + name: result + }).then(() => { + createInfoModal(_translations.sGFYk3BV || (_translations.sGFYk3BV = tr("Group renamed")), _translations.Z6K53vUj || (_translations.Z6K53vUj = tr("The server group has been renamed."))).open(); + update_groups(current_group.id); + }).catch(error => { + console.warn(_translations.Nvr1sNU1 || (_translations.Nvr1sNU1 = tr("Failed to rename server group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.gZuGhFeW || (_translations.gZuGhFeW = tr("Failed to rename group")), MessageHelper.formatMessage(_translations.RAahtMoY || (_translations.RAahtMoY = tr("Failed to rename group:{:br:}")), error)).open(); + }); + }).open(); + }); + button_duplicate.on('click', () => { + createErrorModal(_translations.JFTntZDs || (_translations.JFTntZDs = tr("Not implemented yet")), _translations.XSwWUXyj || (_translations.XSwWUXyj = tr("This function hasn't been implemented yet!"))).open(); + }); + button_delete.on('click', () => { + if (!current_group) + return; + Modals.spawnYesNo(_translations.BtRlLNMi || (_translations.BtRlLNMi = tr("Are you sure?")), MessageHelper.formatMessage(_translations.DjQ0gUq3 || (_translations.DjQ0gUq3 = tr("Do you really want to delete the group {}?")), current_group.name), result => { + if (result !== true) + return; + connection.serverConnection.send_command("servergroupdel", { + sgid: current_group.id, + force: true + }).then(() => { + createInfoModal(_translations.Skc29nKb || (_translations.Skc29nKb = tr("Group deleted")), _translations.e9uYZsgU || (_translations.e9uYZsgU = tr("The server group has been deleted."))).open(); + update_groups(0); + }).catch(error => { + console.warn(_translations.p5NZb93F || (_translations.p5NZb93F = tr("Failed to delete server group: %o")), error); + if (error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(_translations.Xe4iGn25 || (_translations.Xe4iGn25 = tr("Failed to delete group")), MessageHelper.formatMessage(_translations.QzcGXAWO || (_translations.QzcGXAWO = tr("Failed to delete group:{:br:}")), error)).open(); + }); + }); + }); + } + update_groups(0); + /* the editor */ + { + const pe_server = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + pe_server.append(editor.html_tag()); + if (connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + console.log("Updating permissions"); + connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { + console.log(error); //TODO handling? + }); + }); + editor.set_listener((permission, value) => __awaiter(this, void 0, void 0, function* () { + if (!current_group) + throw "unset server group"; + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.rvTpLnM3 || (_translations.rvTpLnM3 = tr("Removing server group permission %s. permission.id: %o")), permission.name, permission.id); + yield connection.serverConnection.send_command("servergroupdelperm", { + sgid: current_group.id, + permid: permission.id, + }).then(e => { + if (permission.name === "i_icon_id") + for (const c of update_icon) + c(0); + return e; + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.lNfsBK1q || (_translations.lNfsBK1q = tr("Removing server group grant permission %s. permission.id: %o")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("servergroupdelperm", { + sgid: current_group.id, + permid: permission.id_grant(), + }); + } + } + else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, _translations.l3qUWBQ2 || (_translations.l3qUWBQ2 = tr("Adding or updating server group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}")), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate); + yield connection.serverConnection.send_command("servergroupaddperm", { + sgid: current_group.id, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if (permission.name === "i_icon_id") + for (const c of update_icon) + c(value.value); + return e; + }); + } + else { + log.info(LogCategory.PERMISSIONS, _translations.mH6z0xG8 || (_translations.mH6z0xG8 = tr("Adding or updating server group grant permission %s. permission.{id: %o, value: %o}")), permission.name, permission.id_grant(), value.granted); + yield connection.serverConnection.send_command("servergroupaddperm", { + sgid: current_group.id, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + })); + editor.trigger_update(); + }); + } + /* client list */ + { + //container-client-list container-group-list + let clients_visible = false; + let selected_client; + const container_client_list = tab_left.find(".container-client-list").addClass("hidden"); + const container_group_list = tab_left.find(".container-group-list"); + const container_selected_group = container_client_list.find(".container-current-group"); + const container_clients = container_client_list.find(".list-clients .entries"); + const input_filter = container_client_list.find(".filter-client-list"); + const button_add = container_client_list.find(".button-add"); + const button_delete = container_client_list.find(".button-delete"); + const update_filter = () => { + const filter_text = (input_filter.val() || "").toString().toLowerCase(); + if (!filter_text) { + container_clients.find(".entry").css('display', 'block'); + } + else { + const entries = container_clients.find(".entry"); + for (const _entry of entries) { + const entry = $(_entry); + if (entry.attr("search-string").toLowerCase().indexOf(filter_text) !== -1) + entry.css('display', 'block'); + else + entry.css('display', 'none'); + } + } + }; + const update_client_list = () => { + container_clients.empty(); + button_delete.prop('disabled', true); + connection.serverConnection.command_helper.request_clients_by_server_group(current_group.id).then(clients => { + for (const client of clients) { + const tag = $.spawn("div").addClass("client").text(client.client_nickname); + tag.attr("search-string", client.client_nickname + "-" + client.client_unique_identifier + "-" + client.client_database_id); + container_clients.append(tag); + tag.on('click contextmenu', event => { + container_clients.find(".selected").removeClass("selected"); + tag.addClass("selected"); + selected_client = { + tag: tag, + dbid: client.client_database_id + }; + button_delete.prop('disabled', false); + }); + tag.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.VSkQ3V09 || (_translations.VSkQ3V09 = tr("Add client")), + icon_class: 'client-add', + callback: () => button_add.trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.z40xhzvX || (_translations.z40xhzvX = tr("Remove client")), + icon_class: 'client-delete', + callback: () => button_delete.trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.RnTroWBE || (_translations.RnTroWBE = tr("Copy unique id")), + icon_class: 'client-copy', + callback: () => copy_to_clipboard(client.client_unique_identifier) + }); + }); + } + update_filter(); + }).catch(error => { + if (error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + console.warn(_translations.JrlJsqem || (_translations.JrlJsqem = tr("Failed to receive server group clients for group %d: %o")), current_group.id, error); + }); + }; + current_group_changed.push(update_client_list); + button_delete.on('click', event => { + const client = selected_client; + if (!client) + return; + connection.serverConnection.send_command("servergroupdelclient", { + sgid: current_group.id, + cldbid: client.dbid + }).then(() => { + selected_client.tag.detach(); + button_delete.prop('disabled', true); /* nothing is selected */ + }).catch(error => { + console.log(_translations.QYvEqWFo || (_translations.QYvEqWFo = tr("Failed to delete client %o from server group %o: %o")), client.dbid, current_group.id, error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.w_grwuKP || (_translations.w_grwuKP = tr("Failed to remove client")), _translations.Xqg78pAj || (_translations.Xqg78pAj = tr("Failed to remove client from server group"))).open(); + }); + }); + button_add.on('click', event => { + createInputModal(_translations.vp8oLTFs || (_translations.vp8oLTFs = tr("Add client to server group")), _translations.bqhbv6gi || (_translations.bqhbv6gi = tr("Enter the client unique id or database id")), text => { + if (!text) + return false; + if (!!text.match(/^[0-9]+$/)) + return true; + try { + return atob(text).length >= 20; + } + catch (error) { + return false; + } + }, (text) => __awaiter(this, void 0, void 0, function* () { + if (typeof (text) !== "string") + return; + let dbid; + if (!!text.match(/^[0-9]+$/)) { + dbid = parseInt(text); + debugger; + } + else { + try { + const data = yield connection.serverConnection.command_helper.info_from_uid(text.trim()); + dbid = data[0].client_database_id; + } + catch (error) { + console.log(_translations.IaBow48n || (_translations.IaBow48n = tr("Failed to resolve client database id from unique id (%s): %o")), text, error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.qXfwiEGs || (_translations.qXfwiEGs = tr("Failed to add client")), MessageHelper.formatMessage(_translations.xvC5Mz5O || (_translations.xvC5Mz5O = tr("Failed to add client to server group\nFailed to resolve database id: {}.")), error)).open(); + return; + } + } + if (!dbid) { + console.log(_translations.RTOwbHRq || (_translations.RTOwbHRq = tr("Failed to resolve client database id from unique id (%s): Client not found"))); + createErrorModal(_translations.Fqjboqd3 || (_translations.Fqjboqd3 = tr("Failed to add client")), _translations.E8Wmhi8B || (_translations.E8Wmhi8B = tr("Failed to add client to server group\nClient database id not found"))).open(); + return; + } + connection.serverConnection.send_command("servergroupaddclient", { + sgid: current_group.id, + cldbid: dbid + }).then(() => { + update_client_list(); + }).catch(error => { + console.log(_translations.kwFlKJU2 || (_translations.kwFlKJU2 = tr("Failed to add client %o to server group %o: %o")), dbid, current_group.id, error); + if (error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(_translations.iBC3dxeB || (_translations.iBC3dxeB = tr("Failed to add client")), tr("Failed to add client to server group\n" + error)).open(); + }); + })).open(); + }); + container_client_list.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.HHOHRQs8 || (_translations.HHOHRQs8 = tr("Add client")), + icon_class: 'client-add', + callback: () => button_add.trigger('click') + }); + }); + /* icon handler and current group display */ + { + let update_icon_callback; + update_icon.push(i => update_icon_callback(i)); + input_filter.on('change keyup', event => update_filter()); + current_group_changed.push(() => { + container_selected_group.empty(); + if (!current_group) + return; + let icon_container = $.spawn("div").addClass("icon-container").appendTo(container_selected_group); + connection.fileManager.icons.generateTag(current_group.properties.iconid).appendTo(icon_container); + update_icon_callback = icon => { + icon_container.empty(); + connection.fileManager.icons.generateTag(icon).appendTo(icon_container); + }; + $.spawn("div").addClass("name").text(current_group.name + " (" + current_group.id + ")").appendTo(container_selected_group); + }); + } + tab_right.on('show', event => { + editor.set_toggle_button(() => { + clients_visible = !clients_visible; + container_client_list.toggleClass("hidden", !clients_visible); + container_group_list.toggleClass("hidden", clients_visible); + return clients_visible ? _translations.XhSxnxAW || (_translations.XhSxnxAW = tr("Hide clients in group")) : _translations.SJnJGsYd || (_translations.SJnJGsYd = tr("Show clients in group")); + }, clients_visible ? _translations.nztvRGaW || (_translations.nztvRGaW = tr("Hide clients in group")) : _translations.ouOjBgEH || (_translations.ouOjBgEH = tr("Show clients in group"))); + }); + } + } + function spawnGroupAdd(server_group, permissions, valid_name, callback) { + let modal; + modal = createModal({ + header: _translations.Ombfpy2D || (_translations.Ombfpy2D = tr("Create a new group")), + body: () => { + let tag = $("#tmpl_group_add").renderTag({ + server_group: server_group + }); + tag.find(".group-type-template").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1)); + tag.find(".group-type-query").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1)); + const container_name = tag.find(".group-name"); + const button_create = tag.find(".button-create"); + const group_type = () => tag.find(".group-type")[0].selectedIndex; + container_name.on('keyup change', (event) => { + if (event.type === 'keyup') { + const kevent = event; + if (!kevent.shiftKey && kevent.key == 'Enter') { + button_create.trigger('click'); + return; + } + } + const valid = valid_name(container_name.val(), group_type()); + button_create.prop("disabled", !valid); + container_name.parent().toggleClass("is-invalid", !valid); + }).trigger('change'); + tag.find(".group-type").on('change', () => container_name.trigger('change')); + button_create.on('click', event => { + if (button_create.prop("disabled")) + return; + button_create.prop("disabled", true); /* disable double clicking */ + modal.close(); + callback(container_name.val(), group_type()); + }); + return tag; + }, + footer: null, + width: 600 + }); + modal.htmlTag.find(".modal-body").addClass("modal-group-add"); + modal.open_listener.push(() => { + modal.htmlTag.find(".group-name").focus(); + }); + modal.open(); + } +})(Modals || (Modals = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["34cd71225ad5db2a75a5d24e2b7d106cb2fb951faf9aeb6d5fcbd8cfcef9103a"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["34cd71225ad5db2a75a5d24e2b7d106cb2fb951faf9aeb6d5fcbd8cfcef9103a"] = "34cd71225ad5db2a75a5d24e2b7d106cb2fb951faf9aeb6d5fcbd8cfcef9103a"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "SNMslQQQ", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (781,46)" }, { name: "zfS9XdO_", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1406,31)" }, { name: "Tcfl6mLz", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1410,31)" }, { name: "mP58mGgk", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1448,46)" }, { name: "lZSZQS43", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1497,35)" }, { name: "Q9AfhdTW", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1503,35)" }, { name: "vFdcnsOq", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1514,35)" }, { name: "uE7PRt7i", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1520,35)" }, { name: "EPXyKw8w", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1530,31)" }, { name: "yaxyk7HH", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1535,31)" }, { name: "hwphI7MO", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1541,31)" }, { name: "pyMqO5eg", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1544,33)" }, { name: "GOZJ1o5q", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1545,33)" }, { name: "oJrVKMx7", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1551,31)" }, { name: "cBq9P5L9", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/CanvasPermissionEditor.ts (1588,42)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// /* first needs the AbstractPermissionEdit */ +/* Canvas Permission Editor */ +var pe; +(function (pe) { + let ui; + (function (ui) { + let RepaintMode; + (function (RepaintMode) { + RepaintMode[RepaintMode["NONE"] = 0] = "NONE"; + RepaintMode[RepaintMode["REPAINT"] = 1] = "REPAINT"; + RepaintMode[RepaintMode["REPAINT_OBJECT_FULL"] = 2] = "REPAINT_OBJECT_FULL"; + RepaintMode[RepaintMode["REPAINT_FULL"] = 3] = "REPAINT_FULL"; + })(RepaintMode = ui.RepaintMode || (ui.RepaintMode = {})); + let ClickEventType; + (function (ClickEventType) { + ClickEventType[ClickEventType["SIGNLE"] = 0] = "SIGNLE"; + ClickEventType[ClickEventType["DOUBLE"] = 1] = "DOUBLE"; + ClickEventType[ClickEventType["CONTEXT_MENU"] = 2] = "CONTEXT_MENU"; + })(ClickEventType = ui.ClickEventType || (ui.ClickEventType = {})); + class DrawableObject { + constructor() { + this._object_full_draw = false; + this._width = 0; + this._transforms = []; + this.colors = {}; + } + set_width(value) { + this._width = value; + } + request_full_draw() { + this._object_full_draw = true; + } + pop_full_draw() { + const result = this._object_full_draw; + this._object_full_draw = false; + return result; + } + width() { + return this._width; + } + push_transform(context) { + this._transforms.push(context.getTransform()); + } + pop_transform(context) { + const transform = this._transforms.pop(); + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f); + } + original_x(context, x) { + return context.getTransform().e + x; + } + original_y(context, y) { + return context.getTransform().f + y; + } + set_color_scheme(scheme) { + this.colors = scheme; + } + set_manager(manager) { + this.manager = manager; + } + } + class PermissionGroup extends DrawableObject { + constructor(group) { + super(); + this._sub_elements = []; + this.collapsed = false; + this.group = group; + this._element_permissions = new PermissionList(this.group.permissions); + for (const sub of this.group.children) + this._sub_elements.push(new PermissionGroup(sub)); + } + draw(context, full) { + const _full = this.pop_full_draw() || full; + this.push_transform(context); + context.translate(PermissionGroup.ARROW_SIZE + 20, PermissionGroup.HEIGHT); + let sum_height = 0; + /* let first draw the elements, because if the sum height is zero then we could hide ourselves */ + if (!this.collapsed) { /* draw the next groups */ + for (const group of this._sub_elements) { + group.draw(context, full); + const height = group.height(); + sum_height += height; + context.translate(0, height); + } + this._element_permissions.draw(context, full); + if (sum_height == 0) + sum_height += this._element_permissions.height(); + } + else { + const process_group = (group) => { + for (const g of group._sub_elements) + process_group(g); + group._element_permissions.handle_hide(); + if (sum_height == 0 && group._element_permissions.height() > 0) { + sum_height = 1; + } + }; + process_group(this); + } + this.pop_transform(context); + if (_full && sum_height > 0) { + const arrow_stretch = 2 / 3; + if (!full) { + context.clearRect(0, 0, this.width(), PermissionGroup.HEIGHT); + } + context.fillStyle = this.colors.group.name; + /* arrow */ + { + const x1 = this.collapsed ? PermissionGroup.ARROW_SIZE * arrow_stretch / 2 : 0; + const y1 = (PermissionGroup.HEIGHT - PermissionGroup.ARROW_SIZE) / 2 + (this.collapsed ? 0 : PermissionGroup.ARROW_SIZE * arrow_stretch / 2); /* center arrow */ + const x2 = this.collapsed ? x1 + PermissionGroup.ARROW_SIZE * arrow_stretch : x1 + PermissionGroup.ARROW_SIZE / 2; + const y2 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE / 2 : y1 + PermissionGroup.ARROW_SIZE * arrow_stretch; + const x3 = this.collapsed ? x1 : x1 + PermissionGroup.ARROW_SIZE; + const y3 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE : y1; + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(x3, y3); + context.moveTo(x2, y2); + context.lineTo(x3, y3); + context.fill(); + this._listener_colaps.region.x = this.original_x(context, 0); + this._listener_colaps.region.y = this.original_y(context, y1); + } + /* text */ + { + context.font = this.colors.group.name_font; + context.textBaseline = "middle"; + context.textAlign = "start"; + context.fillText(this.group.group.name, PermissionGroup.ARROW_SIZE + 5, PermissionGroup.HEIGHT / 2); + } + } + } + set_width(value) { + super.set_width(value); + for (const element of this._sub_elements) + element.set_width(value - PermissionGroup.ARROW_SIZE - 20); + this._element_permissions.set_width(value - PermissionGroup.ARROW_SIZE - 20); + } + set_color_scheme(scheme) { + super.set_color_scheme(scheme); + for (const child of this._sub_elements) + child.set_color_scheme(scheme); + this._element_permissions.set_color_scheme(scheme); + } + set_manager(manager) { + super.set_manager(manager); + for (const child of this._sub_elements) + child.set_manager(manager); + this._element_permissions.set_manager(manager); + } + height() { + let result = 0; + if (!this.collapsed) { + for (const element of this._sub_elements) + result += element.height(); + result += this._element_permissions.height(); + } + else { + //We've to figure out if we have permissions + const process_group = (group) => { + if (result == 0 && group._element_permissions.height() > 0) { + result = 1; + } + else { + for (const g of group._sub_elements) + process_group(g); + } + }; + process_group(this); + if (result > 0) + return PermissionGroup.HEIGHT; + return 0; + } + if (result > 0) { + result += PermissionGroup.HEIGHT; + return result; + } + else { + return 0; + } + } + initialize() { + for (const child of this._sub_elements) + child.initialize(); + this._element_permissions.initialize(); + this._listener_colaps = { + region: { + x: 0, + y: 0, + height: PermissionGroup.ARROW_SIZE, + width: PermissionGroup.ARROW_SIZE + }, + region_weight: 10, + /* + on_mouse_enter: () => { + this.collapsed_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.collapsed_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + */ + on_click: () => { + this.collapsed = !this.collapsed; + return RepaintMode.REPAINT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this.manager.intercept_manager().register_listener(this._listener_colaps); + } + finalize() { + for (const child of this._sub_elements) + child.finalize(); + this._element_permissions.finalize(); + } + collapse_group() { + for (const child of this._sub_elements) + child.collapse_group(); + this.collapsed = true; + } + expend_group() { + for (const child of this._sub_elements) + child.expend_group(); + this.collapsed = false; + } + } + PermissionGroup.HEIGHT = parseFloat(getComputedStyle(document.documentElement).fontSize) * (3 / 2); /* 24 */ + PermissionGroup.ARROW_SIZE = 10; /* 12 */ + class PermissionList extends DrawableObject { + constructor(permissions) { + super(); + this.permissions = []; + for (const permission of permissions) + this.permissions.push(new PermissionEntry(permission)); + } + set_width(value) { + super.set_width(value); + for (const entry of this.permissions) + entry.set_width(value); + } + draw(context, full) { + this.push_transform(context); + for (const permission of this.permissions) { + permission.draw(context, full); + context.translate(0, permission.height()); + } + this.pop_transform(context); + } + height() { + let height = 0; + for (const permission of this.permissions) + height += permission.height(); + return height; + } + set_color_scheme(scheme) { + super.set_color_scheme(scheme); + for (const entry of this.permissions) + entry.set_color_scheme(scheme); + } + set_manager(manager) { + super.set_manager(manager); + for (const entry of this.permissions) + entry.set_manager(manager); + } + initialize() { + for (const entry of this.permissions) + entry.initialize(); + } + finalize() { + for (const entry of this.permissions) + entry.finalize(); + } + handle_hide() { + for (const entry of this.permissions) + entry.handle_hide(); + } + } + class PermissionEntry extends DrawableObject { + constructor(permission) { + super(); + this.granted = 22; + this.flag_skip = true; + this._prev_selected = false; + this.flag_skip_hovered = false; + this.flag_negate_hovered = false; + this.flag_value_hovered = false; + this.flag_grant_hovered = false; + this._permission = permission; + } + set_icon_id_image(image) { + if (this._icon_image === image) + return; + this._icon_image = image; + if (image) { + image.height = 16; + image.width = 16; + } + } + permission() { + return this._permission; + } + draw(ctx, full) { + if (!this.pop_full_draw() && !full) { /* Note: do not change this order! */ + /* test for update! */ + return; + } + if (this.hidden) { + this.handle_hide(); + return; + } + ctx.lineWidth = 1; + /* debug box */ + if (false) { + ctx.fillStyle = "#FF0000"; + ctx.fillRect(0, 0, this.width(), PermissionEntry.HEIGHT); + ctx.fillStyle = "#000000"; + ctx.strokeRect(0, 0, this.width(), PermissionEntry.HEIGHT); + } + if (!full) { + const off = this.selected || this._prev_selected ? ctx.getTransform().e : 0; + ctx.clearRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); + } + if (this.selected) + ctx.fillStyle = this.colors.permission.background_selected; + else + ctx.fillStyle = this.colors.permission.background; + const off = this.selected ? ctx.getTransform().e : 0; + ctx.fillRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); + this._prev_selected = this.selected; + /* permission name */ + { + ctx.fillStyle = typeof (this.value) !== "undefined" ? this.colors.permission.name : this.colors.permission.name_unset; + ctx.textBaseline = "middle"; + ctx.textAlign = "start"; + ctx.font = this.colors.permission.name_font; + ctx.fillText(this._permission.name, 0, PermissionEntry.HALF_HEIGHT); + } + const original_y = this.original_y(ctx, 0); + const original_x = this.original_x(ctx, 0); + const width = this.width(); + /* draw granted */ + let w = width - PermissionEntry.COLUMN_GRANTED; + if (typeof (this.granted) === "number") { + this._listener_grant.region.x = original_x + w; + this._listener_grant.region.y = original_y; + this._draw_number_field(ctx, this.colors.permission.granted, w, 0, PermissionEntry.COLUMN_VALUE, this.granted, this.flag_grant_hovered); + } + else { + this._listener_grant.region.y = original_y; + this._listener_grant.region.x = + original_x + + width + - PermissionEntry.COLUMN_GRANTED; + } + /* draw value and the skip stuff */ + if (typeof (this.value) === "number") { + w -= PermissionEntry.COLUMN_SKIP + PermissionEntry.COLUMN_PADDING; + { + const x = w + (PermissionEntry.COLUMN_SKIP - PermissionEntry.CHECKBOX_HEIGHT) / 2; + const y = 1; + this._listener_checkbox_skip.region.x = original_x + x; + this._listener_checkbox_skip.region.y = original_y + y; + this._draw_checkbox_field(ctx, this.colors.permission.skip, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_skip, this.flag_skip_hovered); + } + w -= PermissionEntry.COLUMN_NEGATE + PermissionEntry.COLUMN_PADDING; + { + const x = w + (PermissionEntry.COLUMN_NEGATE - PermissionEntry.CHECKBOX_HEIGHT) / 2; + const y = 1; + this._listener_checkbox_negate.region.x = original_x + x; + this._listener_checkbox_negate.region.y = original_y + y; + this._draw_checkbox_field(ctx, this.colors.permission.negate, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_negate, this.flag_negate_hovered); + } + w -= PermissionEntry.COLUMN_VALUE + PermissionEntry.COLUMN_PADDING; + if (this._permission.is_boolean()) { + const x = w + PermissionEntry.COLUMN_VALUE - PermissionEntry.CHECKBOX_HEIGHT; + const y = 1; + this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; + this._listener_value.region.x = original_x + x; + this._listener_value.region.y = original_y + y; + this._draw_checkbox_field(ctx, this.colors.permission.value_b, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.value > 0, this.flag_value_hovered); + } + else if (this._permission.name === "i_icon_id" && this._icon_image) { + this._listener_value.region.x = original_x + w; + this._listener_value.region.y = original_y; + this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; + this._draw_icon_field(ctx, this.colors.permission.value_b, w, 0, PermissionEntry.COLUMN_VALUE, this.flag_value_hovered, this._icon_image); + } + else { + this._listener_value.region.width = PermissionEntry.COLUMN_VALUE; + this._listener_value.region.x = original_x + w; + this._listener_value.region.y = original_y; + this._draw_number_field(ctx, this.colors.permission.value, w, 0, PermissionEntry.COLUMN_VALUE, this.value, this.flag_value_hovered); + } + this._listener_value.disabled = false; + } + else { + this._listener_checkbox_skip.region.y = -1e8; + this._listener_checkbox_negate.region.y = -1e8; + this._listener_value.region.y = original_y; + this._listener_value.region.x = + original_x + + width + - PermissionEntry.COLUMN_GRANTED + - PermissionEntry.COLUMN_NEGATE + - PermissionEntry.COLUMN_VALUE + - PermissionEntry.COLUMN_PADDING * 4; + this._listener_value.disabled = true; + } + this._listener_general.region.y = original_y; + this._listener_general.region.x = original_x; + } + handle_hide() { + /* so the listener wound get triggered */ + this._listener_value.region.x = -1e8; + this._listener_grant.region.x = -1e8; + this._listener_checkbox_negate.region.x = -1e8; + this._listener_checkbox_skip.region.x = -1e8; + this._listener_general.region.x = -1e8; + } + _draw_icon_field(ctx, scheme, x, y, width, hovered, image) { + const line = ctx.lineWidth; + ctx.lineWidth = 2; + ctx.fillStyle = scheme.border; + ctx.strokeRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); + ctx.lineWidth = line; + ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; + ctx.fillRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); + const center_y = y + PermissionEntry.HEIGHT / 2; + const center_x = x + PermissionEntry.HEIGHT / 2; + ctx.drawImage(image, center_x - image.width / 2, center_y - image.height / 2); + } + _draw_number_field(ctx, scheme, x, y, width, value, hovered) { + ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; + ctx.fillRect(x, y, width, PermissionEntry.HEIGHT); + ctx.fillStyle = scheme.color; + ctx.font = scheme.font; //Math.floor(2/3 * PermissionEntry.HEIGHT) + "px Arial"; + ctx.textAlign = "start"; + ctx.fillText(value + "", x, y + PermissionEntry.HALF_HEIGHT, width); + ctx.strokeStyle = "#6e6e6e"; + const line = ctx.lineWidth; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x, y + PermissionEntry.HEIGHT - 2); + ctx.lineTo(x + width, y + PermissionEntry.HEIGHT - 2); + ctx.stroke(); + ctx.lineWidth = line; + } + _draw_checkbox_field(ctx, scheme, x, y, height, checked, hovered) { + ctx.fillStyle = scheme.border; + ctx.strokeRect(x, y, height, height); + ctx.fillStyle = checked ? + (hovered ? scheme.background_checked_hovered : scheme.background_checked) : + (hovered ? scheme.background_hovered : scheme.background); + ctx.fillRect(x + 1, y + 1, height - 2, height - 2); + if (checked) { + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = scheme.checkmark; + ctx.font = scheme.checkmark_font; //Math.floor((5/4) * PermissionEntry.HEIGHT) + "px Arial"; + ctx.fillText("✓", x + height / 2, y + height / 2); + } + } + height() { + return this.hidden ? 0 : PermissionEntry.HEIGHT; + } + set_width(value) { + super.set_width(value); + this._listener_general.region.width = value; + } + initialize() { + this._listener_checkbox_skip = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.CHECKBOX_HEIGHT, + width: PermissionEntry.CHECKBOX_HEIGHT + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_skip_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_skip_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this.flag_skip = !this.flag_skip; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_checkbox_negate = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.CHECKBOX_HEIGHT, + width: PermissionEntry.CHECKBOX_HEIGHT + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_negate_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_negate_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this.flag_negate = !this.flag_negate; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_value = { + region: { + x: -1e8, + y: -1e8, + height: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.HEIGHT, + width: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.COLUMN_VALUE + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_value_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_value_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + if (this._permission.is_boolean()) { + this.value = this.value > 0 ? 0 : 1; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + } + else if (this._permission.name === "i_icon_id") { + this.on_icon_select(this.value).then(value => { + this.value = value; + if (this.on_change) + this.on_change(); + }).catch(error => { + console.warn(_translations.SNMslQQQ || (_translations.SNMslQQQ = tr("Failed to select icon: %o")), error); + }); + } + else { + this._spawn_number_edit(this._listener_value.region.x, this._listener_value.region.y, this._listener_value.region.width, this._listener_value.region.height, this.colors.permission.value, this.value || 0, value => { + if (typeof (value) === "number") { + this.value = value; + this.request_full_draw(); + this.manager.request_draw(false); + if (this.on_change) + this.on_change(); + } + }); + } + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_grant = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.HEIGHT, + width: PermissionEntry.COLUMN_VALUE + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_grant_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_grant_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this._spawn_number_edit(this._listener_grant.region.x, this._listener_grant.region.y, this._listener_grant.region.width, this._listener_grant.region.height, this.colors.permission.granted, this.granted || 0, //TODO use max assignable value? + //TODO use max assignable value? + //TODO use max assignable value? + value => { + if (typeof (value) === "number") { + this.granted = value; + this.request_full_draw(); + this.manager.request_draw(false); + if (this.on_grant_change) + this.on_grant_change(); + } + }); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_general = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.HEIGHT, + width: 0 + }, + region_weight: 0, + /* + on_mouse_enter: () => { + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + return RepaintMode.REPAINT_OBJECT_FULL; + }, + */ + on_click: (event) => { + this.manager.set_selected_entry(this); + if (event.type == ClickEventType.DOUBLE && typeof (this.value) === "undefined") + return this._listener_value.on_click(event); + else if (event.type == ClickEventType.CONTEXT_MENU) { + const mouse = this.manager.mouse; + if (this.on_context_menu) { + this.on_context_menu(mouse.x, mouse.y); + event.consumed = true; + } + } + return RepaintMode.NONE; + }, + set_full_draw: () => this.request_full_draw(), + }; + this.manager.intercept_manager().register_listener(this._listener_checkbox_negate); + this.manager.intercept_manager().register_listener(this._listener_checkbox_skip); + this.manager.intercept_manager().register_listener(this._listener_value); + this.manager.intercept_manager().register_listener(this._listener_grant); + this.manager.intercept_manager().register_listener(this._listener_general); + } + finalize() { + } + _spawn_number_edit(x, y, width, height, color, value, callback) { + const element = $.spawn("div"); + element.prop("contentEditable", true); + element + .css("pointer-events", "none") + .css("background", color.background) + .css("display", "block") + .css("position", "absolute") + .css("top", y) + .css("left", x) + .css("width", width) + .css("height", height) + .css("z-index", 1e6); + element.text(value); + element.appendTo(this.manager.canvas_container); + element.focus(); + element.on('focusout', event => { + console.log("permission changed to " + element.text()); + if (!isNaN(parseInt(element.text()))) { + callback(parseInt(element.text())); + } + else { + callback(undefined); + } + element.remove(); + }); + element.on('keypress', event => { + if (event.which == KeyCode.KEY_RETURN) + element.trigger('focusout'); + const text = String.fromCharCode(event.which); + if (isNaN(parseInt(text)) && text != "-") + event.preventDefault(); + if (element.text().length > 7) + event.preventDefault(); + }); + if (window.getSelection) { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element[0]); + selection.removeAllRanges(); + selection.addRange(range); + } + } + trigger_value_assign() { + this._listener_value.on_click(undefined); + } + trigger_grant_assign() { + this._listener_grant.on_click(undefined); + } + } + PermissionEntry.HEIGHT = PermissionGroup.HEIGHT; /* 24 */ + PermissionEntry.HALF_HEIGHT = PermissionEntry.HEIGHT / 2; + PermissionEntry.CHECKBOX_HEIGHT = PermissionEntry.HEIGHT - 2; + PermissionEntry.COLUMN_PADDING = 2; + PermissionEntry.COLUMN_VALUE = 75; + PermissionEntry.COLUMN_GRANTED = 75; + //public static readonly COLUMN_NEGATE = 25; + //public static readonly COLUMN_SKIP = 25; + PermissionEntry.COLUMN_NEGATE = 75; + PermissionEntry.COLUMN_SKIP = 75; + class InteractionManager { + constructor() { + this._listeners = []; + this._entered_listeners = []; + } + register_listener(listener) { + this._listeners.push(listener); + } + remove_listener(listener) { + this._listeners.remove(listener); + } + process_mouse_move(new_x, new_y) { + let _entered_listeners = []; + for (const listener of this._listeners) { + const aabb = listener.region; + if (listener.disabled) + continue; + if (new_x < aabb.x || new_x > aabb.x + aabb.width) + continue; + if (new_y < aabb.y || new_y > aabb.y + aabb.height) + continue; + _entered_listeners.push(listener); + } + let repaint = RepaintMode.NONE; + _entered_listeners.sort((a, b) => (a.region_weight || 0) - (b.region_weight || 0)); + for (const listener of this._entered_listeners) { + if (listener.on_mouse_leave && _entered_listeners.indexOf(listener) == -1) { + let mode = listener.on_mouse_leave(); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + } + for (const listener of _entered_listeners) { + if (listener.on_mouse_enter && this._entered_listeners.indexOf(listener) == -1) { + let mode = listener.on_mouse_enter(); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + } + this._entered_listeners = _entered_listeners; + let cursor; + for (const listener of _entered_listeners) + if (typeof (listener.mouse_cursor) === "string") { + cursor = listener.mouse_cursor; + } + return { + repaint: repaint, + cursor: cursor + }; + } + process_click_event(x, y, event) { + const move_result = this.process_mouse_move(x, y); + let repaint = move_result.repaint; + for (const listener of this._entered_listeners) + if (listener.on_click) { + let mode = listener.on_click(event); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + return repaint; + } + process_click(x, y) { + const event = { + consumed: false, + type: ClickEventType.SIGNLE, + offset_x: x, + offset_y: y + }; + return this.process_click_event(x, y, event); + } + process_dblclick(x, y) { + const event = { + consumed: false, + type: ClickEventType.DOUBLE, + offset_x: x, + offset_y: y + }; + return this.process_click_event(x, y, event); + } + process_context_menu(js_event, x, y) { + const event = { + consumed: js_event.defaultPrevented, + type: ClickEventType.CONTEXT_MENU, + offset_x: x, + offset_y: y + }; + const result = this.process_click_event(x, y, event); + if (event.consumed) + js_event.preventDefault(); + return result; + } + } + ui.InteractionManager = InteractionManager; + class PermissionEditor { + constructor(permissions) { + this._max_height = 0; + this._permission_count = 0; + this._permission_group_count = 0; + this._draw_requested = false; + this._draw_requested_full = false; + this._elements = []; + this._permission_entry_map = {}; + this.mouse = { + x: 0, + y: 0 + }; + this.grouped_permissions = permissions; + this.canvas_container = $.spawn("div") + .addClass("window-resize-listener") /* we want to handle resized */ + .css("min-width", "750px") + .css("position", "relative") + .css("user-select", "none")[0]; + this.canvas = $.spawn("canvas")[0]; + this.canvas_container.appendChild(this.canvas); + this._intersect_manager = new InteractionManager(); + this.canvas_container.onmousemove = event => { + this.mouse.x = event.pageX; + this.mouse.y = event.pageY; + const draw = this._intersect_manager.process_mouse_move(event.offsetX, event.offsetY); + this.canvas_container.style.cursor = draw.cursor || ""; + this._handle_repaint(draw.repaint); + }; + this.canvas_container.onclick = event => { + this._handle_repaint(this._intersect_manager.process_click(event.offsetX, event.offsetY)); + }; + this.canvas_container.ondblclick = event => { + this._handle_repaint(this._intersect_manager.process_dblclick(event.offsetX, event.offsetY)); + }; + this.canvas_container.oncontextmenu = (event) => { + this._handle_repaint(this._intersect_manager.process_context_menu(event, event.offsetX, event.offsetY)); + }; + this.canvas_container.onresize = () => this.request_draw(true); + this.initialize(); + } + _handle_repaint(mode) { + if (mode == RepaintMode.REPAINT || mode == RepaintMode.REPAINT_FULL) + this.request_draw(mode == RepaintMode.REPAINT_FULL); + } + request_draw(full) { + this._draw_requested_full = this._draw_requested_full || full; + if (this._draw_requested) + return; + this._draw_requested = true; + requestAnimationFrame(() => { + this.draw(this._draw_requested_full); + }); + } + draw(full) { + this._draw_requested = false; + this._draw_requested_full = false; + /* clear max height */ + this.canvas_container.style.overflowY = "shown"; + this.canvas_container.style.height = undefined; + const max_height = this._max_height; + const max_width = this.canvas_container.clientWidth; + const update_width = this.canvas.width != max_width; + const full_draw = typeof (full) !== "boolean" || full || update_width; + if (update_width) { + this.canvas.width = max_width; + for (const element of this._elements) + element.set_width(max_width); + } + console.log("Drawing%s on %dx%d", full_draw ? " full" : "", max_width, max_height); + if (full_draw) + this.canvas.height = max_height; + const ctx = this._canvas_context; + ctx.resetTransform(); + if (full_draw) + ctx.clearRect(0, 0, max_width, max_height); + let sum_height = 0; + for (const element of this._elements) { + element.draw(ctx, full_draw); + const height = element.height(); + sum_height += height; + ctx.translate(0, height); + } + this.canvas_container.style.overflowY = "hidden"; + this.canvas_container.style.height = sum_height + "px"; + } + initialize() { + /* setup the canvas */ + { + const apply_group = (group) => { + for (const g of group.children || []) + apply_group(g); + this._permission_group_count++; + this._permission_count += group.permissions.length; + }; + for (const group of this.grouped_permissions) + apply_group(group); + this._max_height = this._permission_count * PermissionEditor.PERMISSION_HEIGHT + this._permission_group_count * PermissionEditor.PERMISSION_GROUP_HEIGHT; + console.log("%d permissions and %d groups required %d height", this._permission_count, this._permission_group_count, this._max_height); + this.canvas.style.width = "100%"; + this.canvas.style.flexShrink = "0"; + this.canvas_container.style.flexShrink = "0"; + this._canvas_context = this.canvas.getContext("2d"); + } + const font = Math.floor(2 / 3 * PermissionEntry.HEIGHT) + "px Arial"; + const font_checkmark = Math.floor((5 / 4) * PermissionEntry.HEIGHT) + "px Arial"; + const checkbox = { + background: "#303036", + background_hovered: "#CCCCCC", + background_checked: "#0000AA", + background_checked_hovered: "#0000AA77", + border: "#000000", + checkmark: "#303036", + checkmark_font: font_checkmark + }; + const input = { + color: "#000000", + font: font, + background_hovered: "#CCCCCCCC", + background: "#30303600" + }; + const color_scheme = { + group: { + name: "#808080", + name_font: font + }, + //#28282c + permission: { + name: "#808080", + name_unset: "#1a1a1a", + name_font: font, + background: "#303036", + background_selected: "#00007788", + value: input, + value_b: checkbox, + granted: input, + negate: checkbox, + skip: checkbox + } + }; + window.scheme = color_scheme; + /* setup elements to draw */ + { + const process_group = (group) => { + for (const permission of group._element_permissions.permissions) + this._permission_entry_map[permission.permission().id] = permission; + for (const g of group._sub_elements) + process_group(g); + }; + for (const group of this.grouped_permissions) { + const element = new PermissionGroup(group); + element.set_color_scheme(color_scheme); + element.set_manager(this); + process_group(element); + this._elements.push(element); + } + for (const element of this._elements) { + element.initialize(); + } + } + } + intercept_manager() { + return this._intersect_manager; + } + set_selected_entry(entry) { + if (this._selected_entry === entry) + return; + if (this._selected_entry) { + this._selected_entry.selected = false; + this._selected_entry.request_full_draw(); + } + this._selected_entry = entry; + if (this._selected_entry) { + this._selected_entry.selected = true; + this._selected_entry.request_full_draw(); + } + this.request_draw(false); + } + permission_entries() { + return Object.keys(this._permission_entry_map).map(e => this._permission_entry_map[e]); + } + collapse_all() { + for (const group of this._elements) + group.collapse_group(); + this.request_draw(true); + } + expend_all() { + for (const group of this._elements) + group.expend_group(); + this.request_draw(true); + } + } + PermissionEditor.PERMISSION_HEIGHT = PermissionEntry.HEIGHT; + PermissionEditor.PERMISSION_GROUP_HEIGHT = PermissionGroup.HEIGHT; + ui.PermissionEditor = PermissionEditor; + })(ui || (ui = {})); + class CanvasPermissionEditor extends Modals.AbstractPermissionEditor { + constructor() { + super(); + /* references within the container tag */ + this.permission_value_map = {}; + } + initialize(permissions) { + this._permissions = permissions; + this.entry_editor = new ui.PermissionEditor(permissions); + this.build_tag(); + } + html_tag() { return this.container; } + build_tag() { + this.container = $("#tmpl_permission_editor_canvas").renderTag(); + /* search for that as long we've not that much nodes */ + this.mode_container_permissions = this.container.find(".container-mode-permissions"); + this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); + this.mode_container_unset = this.container.find(".container-mode-unset"); + this.set_mode(Modals.PermissionEditorMode.UNSET); + /* the filter */ + { + const tag_filter_input = this.container.find(".filter-input"); + const tag_filter_granted = this.container.find(".filter-granted"); + tag_filter_granted.on('change', event => tag_filter_input.trigger('change')); + tag_filter_input.on('keyup change', event => { + let filter_mask = tag_filter_input.val(); + let req_granted = tag_filter_granted.prop("checked"); + for (const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + let shown = filter_mask.length == 0 || permission.name.indexOf(filter_mask) != -1; + if (shown && req_granted) { + const value = this.permission_value_map[permission.id]; + shown = value && (value.hasValue() || value.hasGrant()); + } + entry.hidden = !shown; + } + this.entry_editor.request_draw(true); + }); + } + /* update button */ + { + this.container.find(".button-update").on('click', this.trigger_update.bind(this)); + } + /* global context menu listener */ + { + this.container.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + /* TODO allow collapse and expend all */ + }); + } + { + const tag_container = this.container.find(".entry-editor-container"); + tag_container.append(this.entry_editor.canvas_container); + tag_container.parent().on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.zfS9XdO_ || (_translations.zfS9XdO_ = tr("Expend all")), + callback: () => this.entry_editor.expend_all() + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.Tcfl6mLz || (_translations.Tcfl6mLz = tr("Collapse all")), + callback: () => this.entry_editor.collapse_all() + }); + }); + } + /* setup the permissions */ + for (const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + entry.on_change = () => { + const flag_remove = typeof (entry.value) !== "number"; + this._listener_change(permission, { + remove: flag_remove, + flag_negate: entry.flag_negate, + flag_skip: entry.flag_skip, + value: flag_remove ? -2 : entry.value + }).then(() => { + if (flag_remove) { + const element = this.permission_value_map[permission.id]; + if (!element) + return; /* This should never happen, if so how are we displaying this permission?! */ + element.value = undefined; + element.flag_negate = false; + element.flag_skip = false; + } + else { + const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); + element.value = entry.value; + element.flag_skip = entry.flag_skip; + element.flag_negate = entry.flag_negate; + } + if (permission.name === "i_icon_id") { + this.icon_resolver(entry.value).then(e => { + entry.set_icon_id_image(e); + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(error => { + console.warn(_translations.mP58mGgk || (_translations.mP58mGgk = tr("Failed to load icon for permission editor: %o")), error); + }); + } + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(() => { + const element = this.permission_value_map[permission.id]; + entry.value = element && element.hasValue() ? element.value : undefined; + entry.flag_skip = element && element.flag_skip; + entry.flag_negate = element && element.flag_negate; + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }); + }; + entry.on_grant_change = () => { + const flag_remove = typeof (entry.granted) !== "number"; + this._listener_change(permission, { + remove: flag_remove, + granted: flag_remove ? -2 : entry.granted, + }).then(() => { + if (flag_remove) { + const element = this.permission_value_map[permission.id]; + if (!element) + return; /* This should never happen, if so how are we displaying this permission?! */ + element.granted_value = undefined; + } + else { + const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); + element.granted_value = entry.granted; + } + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(() => { + const element = this.permission_value_map[permission.id]; + entry.granted = element && element.hasGrant() ? element.granted_value : undefined; + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }); + }; + entry.on_context_menu = (x, y) => { + let entries = []; + if (typeof (entry.value) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.lZSZQS43 || (_translations.lZSZQS43 = tr("Add permission")), + callback: () => entry.trigger_value_assign() + }); + } + else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.Q9AfhdTW || (_translations.Q9AfhdTW = tr("Remove permission")), + callback: () => { + entry.value = undefined; + entry.on_change(); + } + }); + } + if (typeof (entry.granted) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.vFdcnsOq || (_translations.vFdcnsOq = tr("Add grant permission")), + callback: () => entry.trigger_grant_assign() + }); + } + else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.uE7PRt7i || (_translations.uE7PRt7i = tr("Remove grant permission")), + callback: () => { + entry.granted = undefined; + entry.on_grant_change(); + } + }); + } + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.EPXyKw8w || (_translations.EPXyKw8w = tr("Expend all")), + callback: () => this.entry_editor.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.yaxyk7HH || (_translations.yaxyk7HH = tr("Collapse all")), + callback: () => this.entry_editor.collapse_all() + }); + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.hwphI7MO || (_translations.hwphI7MO = tr("Show permission description")), + callback: () => { + createInfoModal(_translations.pyMqO5eg || (_translations.pyMqO5eg = tr("Permission description")), (_translations.GOZJ1o5q || (_translations.GOZJ1o5q = tr("Permission description for permission "))) + permission.name + ":
" + permission.description).open(); + } + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.oJrVKMx7 || (_translations.oJrVKMx7 = tr("Copy permission name")), + callback: () => { + copy_to_clipboard(permission.name); + } + }); + contextmenu.spawn_context_menu(x, y, ...entries); + }; + } + } + set_permissions(permissions) { + permissions = permissions || []; + this.permission_value_map = {}; + for (const permission of permissions) + this.permission_value_map[permission.type.id] = permission; + for (const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + const value = this.permission_value_map[permission.id]; + if (permission.name === "i_icon_id") { + entry.set_icon_id_image(undefined); + entry.on_icon_select = this.icon_selector; + } + if (value && value.hasValue()) { + entry.value = value.value; + entry.flag_skip = value.flag_skip; + entry.flag_negate = value.flag_negate; + if (permission.name === "i_icon_id") { + this.icon_resolver(value.value).then(e => { + entry.set_icon_id_image(e); + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(error => { + console.warn(_translations.cBq9P5L9 || (_translations.cBq9P5L9 = tr("Failed to load icon for permission editor: %o")), error); + }); + } + } + else { + entry.value = undefined; + entry.flag_skip = false; + entry.flag_negate = false; + } + if (value && value.hasGrant()) { + entry.granted = value.granted_value; + } + else { + entry.granted = undefined; + } + } + this.entry_editor.request_draw(true); + } + set_mode(mode) { + this.mode_container_permissions.css('display', mode == Modals.PermissionEditorMode.VISIBLE ? 'flex' : 'none'); + this.mode_container_error_permission.css('display', mode == Modals.PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); + this.mode_container_unset.css('display', mode == Modals.PermissionEditorMode.UNSET ? 'block' : 'none'); + if (mode == Modals.PermissionEditorMode.VISIBLE) + this.entry_editor.draw(true); + } + update_ui() { + this.entry_editor.draw(true); + } + set_toggle_button(callback, initial) { + throw "not implemented"; + } + set_hidden_permissions(permissions) { + //TODO: Stuff here + } + } + pe.CanvasPermissionEditor = CanvasPermissionEditor; +})(pe || (pe = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["ff9f1f09e95203c4b91e242665e830b617f2f879056e6b6bb92b91154deb8387"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["ff9f1f09e95203c4b91e242665e830b617f2f879056e6b6bb92b91154deb8387"] = "ff9f1f09e95203c4b91e242665e830b617f2f879056e6b6bb92b91154deb8387"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "mNnKmhXi", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (127,39)" }, { name: "eIdiXluN", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (209,38)" }, { name: "pXafuMz0", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (243,35)" }, { name: "CymMMUWf", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (249,35)" }, { name: "CNnWyAfz", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (266,35)" }, { name: "h2seh3NA", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (272,35)" }, { name: "z39HymaK", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (289,35)" }, { name: "V3A7waSE", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (295,35)" }, { name: "uEA6K1Gr", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (300,31)" }, { name: "okoskUbi", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (305,31)" }, { name: "VJROSIto", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (311,31)" }, { name: "bex2YiQD", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (314,33)" }, { name: "dcxxYR_b", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (315,33)" }, { name: "n3RjiSxp", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (321,31)" }, { name: "gI5Pynkj", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (517,31)" }, { name: "dr8vqd7I", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (523,31)" }, { name: "GXXzvrDW", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (530,27)" }, { name: "q5n25a7q", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (535,27)" }, { name: "dO3GqXlP", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (703,56)" }, { name: "uqljZhQI", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (813,68)" }, { name: "DC0OXrYd", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (818,67)" }, { name: "QbNs0KSO", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (821,63)" }, { name: "bjKH7Da6", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (824,60)" }, { name: "jVdioG3L", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (837,63)" }, { name: "ZcA_dMgB", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (840,59)" }, { name: "w9vA7Svd", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (853,27)" }, { name: "ArHoLzcP", path: "D:/TeaSpeak/web/shared/js/ui/modal/permission/HTMLPermissionEditor.ts (858,27)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// /* first needs the AbstractPermissionEdit */ +var pe; +(function (pe) { + class HTMLPermission { + constructor(handle, group, permission, index) { + /* the "actual" values */ + this._mask = 0; /* fourth bit: hidden by filer | third bit: value type | second bit: grant shown | first bit: value shown */ + this.handle = handle; + this.permission = permission; + this.index = index; + this.group = group; + this.build_tag(); + } + static build_checkbox() { + let tag, input; + tag = $.spawn("label").addClass("switch").append([ + input = $.spawn("input").attr("type", "checkbox"), + $.spawn("span").addClass("slider").append($.spawn("div").addClass("dot")) + ]); + return { tag: tag, input: input }; + } + build_tag() { + this.tag = $.spawn("div").addClass("entry permission").css('padding-left', this.index + "em").append([ + this.tag_name = $.spawn("div").addClass("column-name").text(this.permission.name), + this.tag_container_value = $.spawn("div").addClass("column-value"), + this.tag_container_skip = $.spawn("div").addClass("column-skip"), + this.tag_container_negate = $.spawn("div").addClass("column-negate"), + this.tag_container_granted = $.spawn("div").addClass("column-granted") + ]); + if (this.permission.is_boolean()) { + let value = HTMLPermission.build_checkbox(); + this._tag_value = value.tag; + this._tag_value_input = value.input; + this._tag_value_input.on('change', event => { + const value = this._tag_value_input.prop('checked') ? 1 : 0; + this.handle.trigger_change(this.permission, { + remove: false, + value: value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + this._value = value; + }).catch(error => { + this._reset_value(); + }); + }); + this._mask |= 0x04; + } + else { + this._tag_value = $.spawn("input").addClass("number"); + this._tag_value_input = this._tag_value; + this._tag_value_input.on('keydown', HTMLPermission.number_filter); + this._tag_value_input.on('change', event => { + const str_value = this._tag_value_input.val(); + const value = parseInt(str_value); + if (!HTMLPermission.number_filter_re.test(str_value) || value == NaN) { + console.warn(_translations.mNnKmhXi || (_translations.mNnKmhXi = tr("Failed to parse given permission value string: %s")), this._tag_value_input.val()); + this._reset_value(); + return; + } + this.handle.trigger_change(this.permission, { + remove: false, + value: value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + this._value = value; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + { + let skip = HTMLPermission.build_checkbox(); + this._tag_skip = skip.tag; + this._tag_skip_input = skip.input; + this._tag_skip_input.on('change', event => { + const value = this._tag_skip_input.prop('checked'); + this.handle.trigger_change(this.permission, { + remove: false, + value: this._value, + flag_skip: value, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + if (value) + this.flags |= 0x01; + else + this.flags &= ~0x1; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + { + let negate = HTMLPermission.build_checkbox(); + this._tag_negate = negate.tag; + this._tag_negate_input = negate.input; + this._tag_negate_input.on('change', event => { + const value = this._tag_negate_input.prop('checked'); + console.log("Negate value: %o", value); + this.handle.trigger_change(this.permission, { + remove: false, + value: this._value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: value + }).then(() => { + if (value) + this.flags |= 0x02; + else + this.flags &= ~0x2; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + { + this._tag_granted = $.spawn("input").addClass("number"); + this._tag_granted_input = this._tag_granted; + this._tag_granted_input.on('keydown', HTMLPermission.number_filter); + this._tag_granted_input.on('change', event => { + const str_value = this._tag_granted_input.val(); + const value = parseInt(str_value); + if (!HTMLPermission.number_filter_re.test(str_value) || Number.isNaN(value)) { + console.warn(_translations.eIdiXluN || (_translations.eIdiXluN = tr("Failed to parse given permission granted value string: %s")), this._tag_granted_input.val()); + this._reset_value(); + return; + } + this.handle.trigger_change(this.permission, { + remove: false, + granted: value + }).then(() => { + this._grant = value; + this._update_active_class(); + }).catch(error => { + this._reset_grant(); + }); + }); + } + /* double click handler */ + { + this.tag.on('dblclick', event => this._trigger_value_assign()); + } + /* context menu */ + { + this.tag.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + let entries = []; + if (typeof (this._value) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.pXafuMz0 || (_translations.pXafuMz0 = tr("Add permission")), + callback: () => this._trigger_value_assign() + }); + } + else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.CymMMUWf || (_translations.CymMMUWf = tr("Remove permission")), + callback: () => { + this.handle.trigger_change(this.permission, { + remove: true, + value: 0 + }).then(() => { + this.value(undefined); + }).catch(error => { + //We have to do nothing + }); + } + }); + } + if (typeof (this._grant) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.CNnWyAfz || (_translations.CNnWyAfz = tr("Add grant permission")), + callback: () => this._trigger_grant_assign() + }); + } + else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.h2seh3NA || (_translations.h2seh3NA = tr("Remove grant permission")), + callback: () => { + this.handle.trigger_change(this.permission, { + remove: true, + granted: 0 + }).then(() => { + this.granted(undefined); + }).catch(error => { + //We have to do nothing + }); + } + }); + } + entries.push(contextmenu.Entry.HR()); + if (this.group.collapsed) + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.z39HymaK || (_translations.z39HymaK = tr("Expend group")), + callback: () => this.group.expend() + }); + else + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.V3A7waSE || (_translations.V3A7waSE = tr("Collapse group")), + callback: () => this.group.collapse() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.uEA6K1Gr || (_translations.uEA6K1Gr = tr("Expend all")), + callback: () => this.handle.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.okoskUbi || (_translations.okoskUbi = tr("Collapse all")), + callback: () => this.handle.collapse_all() + }); + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.VJROSIto || (_translations.VJROSIto = tr("Show permission description")), + callback: () => { + createInfoModal(_translations.bex2YiQD || (_translations.bex2YiQD = tr("Permission description")), (_translations.dcxxYR_b || (_translations.dcxxYR_b = tr("Permission description for permission "))) + this.permission.name + ":
" + this.permission.description).open(); + } + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.n3RjiSxp || (_translations.n3RjiSxp = tr("Copy permission name")), + callback: () => { + copy_to_clipboard(this.permission.name); + } + }); + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + }); + } + } + _trigger_value_assign() { + if (typeof (this._value) === "undefined") + this.value(this._grant || 1, false, false); //TODO: Use max granted value? + this._tag_value_input.focus(); + if (this.permission.is_boolean()) + this._tag_value_input.trigger('change'); + } + _trigger_grant_assign() { + this.granted(1); //TODO: Use max granted value? + this._tag_granted_input.focus(); + } + hide() { + this._mask &= ~0x08; + for (const element of this.tag) + element.style.display = 'none'; + } + show() { + this._mask |= 0x08; + for (const element of this.tag) + element.style.display = 'flex'; + } + is_filtered() { + return (this._mask & 0x10) > 0; + } + set_filtered(flag) { + if (flag) + this._mask |= 0x10; + else + this._mask &= ~0x10; + } + is_set() { + return (this._mask & 0x03) > 0; + } + get_value() { return this._value; } + value(value, skip, negate) { + if (typeof value === "undefined") { + this._tag_value.detach(); + this._tag_negate.detach(); + this._tag_skip.detach(); + this._value = undefined; + this.flags = 0; + this._update_active_class(); + this._mask &= ~0x1; + return; + } + if ((this._mask & 0x1) == 0) { + this._tag_value.appendTo(this.tag_container_value); + this._tag_negate.appendTo(this.tag_container_negate); + this._tag_skip.appendTo(this.tag_container_skip); + this._update_active_class(); + this._mask |= 0x01; + } + if ((this._mask & 0x04) > 0) + this._tag_value_input.prop('checked', !!value); + else + this._tag_value_input.val(value); + this._tag_skip_input.prop('checked', !!skip); + this._tag_negate_input.prop('checked', !!negate); + this._value = value; + this.flags = (!!skip ? 0x01 : 0) | (!!negate ? 0x2 : 0); + } + granted(value) { + if (typeof value === "undefined") { + this._tag_granted.detach(); + this._update_active_class(); + this._grant = undefined; + this._mask &= ~0x2; + return; + } + if ((this._mask & 0x2) == 0) { + this._mask |= 0x02; + this._tag_granted.appendTo(this.tag_container_granted); + this._update_active_class(); + } + this._tag_granted_input.val(value); + this._grant = value; + } + reset() { + this._mask &= ~0x03; + this._tag_value.detach(); + this._tag_negate.detach(); + this._tag_skip.detach(); + this._tag_granted.detach(); + this._value = undefined; + this._grant = undefined; + this.flags = 0; + const tag = this.tag[0]; + tag.classList.remove("active"); + } + _reset_value() { + if (typeof (this._value) === "undefined") { + if ((this._mask & 0x1) != 0) + this.value(undefined); + } + else { + this.value(this._value, (this.flags & 0x1) > 1, (this.flags & 0x2) > 1); + } + } + _reset_grant() { + if (typeof (this._grant) === "undefined") { + if ((this._mask & 0x2) != 0) + this.granted(undefined); + } + else { + this.granted(this._grant); + } + } + _update_active_class() { + const value = typeof (this._value) !== "undefined" || typeof (this._grant) !== "undefined"; + const tag = this.tag[0]; + if (value) + tag.classList.add("active"); + else + tag.classList.remove("active"); + } + } + HTMLPermission.number_filter_re = /^[-+]?([0-9]{0,9})$/; + HTMLPermission.number_filter = (event) => { + if (event.ctrlKey) + return; + const target = event.target; + if (event.key === "Enter") { + target.blur(); + return; + } + if ('keyCode' in event) { + /* everything under 46 is a control key except 32 its space */ + if (event.keyCode < 46 && event.keyCode != 32) + return; + if (!HTMLPermission.number_filter_re.test(target.value + String.fromCharCode(event.keyCode))) { + event.preventDefault(); + return; + } + } + else { + const e = event; /* for some reason typescript deducts the event type to "never" */ + if (!HTMLPermission.number_filter_re.test(e.key)) { + e.preventDefault(); + return; + } + } + }; + class HTMLPermissionGroup { + constructor(handle, group, index) { + this.permissions = []; + this.children = []; + this.handle = handle; + this.group = group; + this.index = index; + this._build_tag(); + } + _build_tag() { + this.tag = $.spawn("div").addClass("entry group").css('padding-left', this.index + "em").append([ + $.spawn("div").addClass("column-name").append([ + this._tag_arrow = $.spawn("div").addClass("arrow down"), + $.spawn("div").addClass("group-name").text(this.group.name) + ]), + $.spawn("div").addClass("column-value"), + $.spawn("div").addClass("column-skip"), + $.spawn("div").addClass("column-negate"), + $.spawn("div").addClass("column-granted") + ]); + this.tag.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + const entries = []; + if (this.collapsed) + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.gI5Pynkj || (_translations.gI5Pynkj = tr("Expend group")), + callback: () => this.expend(), + }); + else + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.dr8vqd7I || (_translations.dr8vqd7I = tr("Collapse group")), + callback: () => this.collapse(), + }); + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.GXXzvrDW || (_translations.GXXzvrDW = tr("Expend all")), + callback: () => this.handle.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.q5n25a7q || (_translations.q5n25a7q = tr("Collapse all")), + callback: () => this.handle.collapse_all() + }); + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + }); + this._tag_arrow.on('click', event => { + if (this.collapsed) + this.expend(); + else + this.collapse(); + }); + } + update_visibility() { + let flag = false; + if (!flag) { + for (const group of this.children) { + if (group.visible) { + flag = true; + break; + } + } + } + if (!flag) { + for (const permission of this.permissions) { + if (!permission.is_filtered()) { + flag = true; + break; + } + } + } + this.visible = flag; + flag = flag && !this.parent_collapsed; + for (const element of this.tag) + element.style.display = flag ? 'flex' : 'none'; + const arrow_node = this._tag_arrow[0]; + arrow_node.classList.remove(this.collapsed ? "down" : "right"); + arrow_node.classList.add(!this.collapsed ? "down" : "right"); + } + collapse() { + this.collapsed = true; + const children = [...this.children]; + while (true) { + const child = children.pop(); + if (!child) + break; + child.parent_collapsed = true; + children.push(...child.children); + } + this.handle.update_view(); + } + expend() { + this.collapsed = false; + if (this.parent_collapsed) + return; + const children = [...this.children]; + while (true) { + const child = children.pop(); + if (!child) + break; + child.parent_collapsed = false; + if (!child.collapsed) + children.push(...child.children); + } + this.handle.update_view(); + } + } + class HTMLPermissionEditor extends Modals.AbstractPermissionEditor { + constructor() { + super(); + } + initialize(permissions) { + this._permissions = permissions; + this.build_tag(); + } + set_hidden_permissions(permissions) { + this.hidden_permissions = permissions; + this.update_filter(); + } + update_filter() { + const value = this.filter_input.val().toLowerCase(); + const grant = !!this.filter_grant.prop('checked'); + const _filter = (permission) => { + if (value && permission.permission.name.indexOf(value) == -1) + return false; + if (grant && !permission.is_set()) + return false; + if (this.hidden_permissions && this.hidden_permissions.find(e => e && e.toLocaleLowerCase() == permission.permission.name.toLowerCase())) + return false; + return true; + }; + for (let id = 1; id < this.permission_map.length; id++) { + const permission = this.permission_map[id]; + let flag = _filter(permission); + permission.set_filtered(!flag); + flag = flag && !permission.group.collapsed && !permission.group.parent_collapsed; /* hide when parent is filtered */ + if (flag) + permission.show(); + else + permission.hide(); + } + /* run in both directions, to update the parent visibility and the actiual visibility */ + for (const group of this.permission_groups) + group.update_visibility(); + for (const group of this.permission_groups.slice().reverse()) + group.update_visibility(); + let index = 0; + for (const entry of this.even_list) { + if (!entry.visible()) + continue; + entry.set_even((index++ & 0x1) == 0); + } + } + update_icon() { + const permission = this.icon_shown ? this.permission_map.find(e => e && e.permission.name === "i_icon_id") : undefined; + const icon_id = permission ? permission.get_value() : 0; + const icon_node = this.container.find(".container-icon-select .icon-preview"); + icon_node.children().remove(); + let resolve; + if (icon_id >= 0 && icon_id <= 1000) + resolve = Promise.resolve(IconManager.generate_tag({ id: icon_id, url: "" })); + else + resolve = this.icon_resolver(permission ? permission.get_value() : 0).then(e => $(e)); + resolve.then(tag => tag.appendTo(icon_node)) + .catch(error => { + log.error(LogCategory.PERMISSIONS, _translations.dO3GqXlP || (_translations.dO3GqXlP = tr("Failed to generate empty icon preview: %o")), error); + }); + } + build_tag() { + this.container = $("#tmpl_permission_editor_html").renderTag(); + this.container.find("input").on('change', event => { + $(event.target).parents(".form-group").toggleClass('is-filled', !!event.target.value); + }); + /* search for that as long we've not that much nodes */ + this.mode_container_permissions = this.container.find(".container-mode-permissions"); + this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); + this.mode_container_unset = this.container.find(".container-mode-unset"); + this.filter_input = this.container.find(".filter-input"); + this.filter_input.on('change keyup', event => this.update_filter()); + this.filter_grant = this.container.find(".filter-granted"); + this.filter_grant.on('change', event => this.update_filter()); + this.button_toggle = this.container.find(".button-toggle-clients"); + this.button_toggle.on('click', () => { + if (this._toggle_callback) + this.button_toggle.text(this._toggle_callback()); + }); + this.container.find(".button-update").on('click', event => this.trigger_update()); + /* allocate array space */ + { + let max_index = 0; + let tmp = []; + while (true) { + const entry = tmp.pop(); + if (!entry) + break; + for (const permission of entry.permissions) + if (permission.id > max_index) + max_index = permission.id; + tmp.push(...entry.children); + } + this.permission_map = new Array(max_index + 1); + } + this.permission_groups = []; + this.even_list = []; + { + const container_permission = this.mode_container_permissions.find(".container-permission-list .body"); + const build_group = (pgroup, group, index) => { + const hgroup = new HTMLPermissionGroup(this, group.group, index); + hgroup.tag.appendTo(container_permission); + this.even_list.push({ + set_even(flag) { + if (flag) + hgroup.tag[0].classList.add('even'); + else + hgroup.tag[0].classList.remove('even'); + }, + visible() { + return !hgroup.parent_collapsed && hgroup.visible; + } + }); + if (pgroup) + pgroup.children.push(hgroup); + this.permission_groups.push(hgroup); + index++; + for (const child of group.children) + build_group(hgroup, child, index); + for (const permission of group.permissions) { + const perm = new HTMLPermission(this, hgroup, permission, index); + this.permission_map[perm.permission.id] = perm; + perm.tag.appendTo(container_permission); + hgroup.permissions.push(perm); + this.even_list.push({ + set_even(flag) { + if (flag) + perm.tag[0].classList.add('even'); + else + perm.tag[0].classList.remove('even'); + }, + visible() { + return !perm.is_filtered() && !perm.group.collapsed && !perm.group.parent_collapsed; + } + }); + } + }; + for (const group of this._permissions) + build_group(undefined, group, 0); + } + { + const container = this.container.find(".container-icon-select"); + container.find(".button-select-icon").on('click', event => { + const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); + this.icon_selector(permission ? permission.get_value() : 0).then(id => { + const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); + if (permission) { + this.trigger_change(permission.permission, { + remove: false, + value: id, + flag_skip: false, + flag_negate: false + }, false).then(() => { + log.debug(LogCategory.PERMISSIONS, _translations.uqljZhQI || (_translations.uqljZhQI = tr("Selected new icon %s")), id); + permission.value(id, false, false); + this.update_icon(); + }).catch(error => { + log.warn(LogCategory.PERMISSIONS, _translations.DC0OXrYd || (_translations.DC0OXrYd = tr("Failed to set icon permission within permission editor: %o")), error); + }); + } + else { + log.warn(LogCategory.PERMISSIONS, _translations.QbNs0KSO || (_translations.QbNs0KSO = tr("Failed to find icon permissions within permission editor"))); + } + }).catch(error => { + log.error(LogCategory.PERMISSIONS, _translations.bjKH7Da6 || (_translations.bjKH7Da6 = tr("Failed to select an icon for the icon permission: %o")), error); + }); + }); + container.find(".button-icon-remove").on('click', event => { + const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); + if (permission) { + this.trigger_change(permission.permission, { + remove: true, + }, false).then(() => { + permission.value(undefined); + this.update_icon(); + }).catch(error => { + log.warn(LogCategory.PERMISSIONS, _translations.jVdioG3L || (_translations.jVdioG3L = tr("Failed to remove icon permission within permission editor: %o")), error); + }); + } + else { + log.warn(LogCategory.PERMISSIONS, _translations.ZcA_dMgB || (_translations.ZcA_dMgB = tr("Failed to find icon permission within permission editor"))); + } + }); + } + this.mode_container_permissions.on('contextmenu', event => { + if (event.isDefaultPrevented()) + return; + event.preventDefault(); + const entries = []; + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.w9vA7Svd || (_translations.w9vA7Svd = tr("Expend all")), + callback: () => this.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: _translations.ArHoLzcP || (_translations.ArHoLzcP = tr("Collapse all")), + callback: () => this.collapse_all() + }); + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + }); + this.set_mode(Modals.PermissionEditorMode.UNSET); + } + html_tag() { + return this.container; + } + set_permissions(u_permissions) { + const permissions = new Array(this.permission_map.length); + /* initialize update array, boundary checks are already made by js */ + for (const perm of u_permissions) + permissions[perm.type.id] = perm; + /* there is no permission with id 0 */ + for (let id = 1; id < permissions.length; id++) { + const new_permission = permissions[id]; + const permission_handle = this.permission_map[id]; + if (!new_permission) { + permission_handle.reset(); + continue; + } + permission_handle.value(new_permission.value, new_permission.flag_skip, new_permission.flag_negate); + permission_handle.granted(new_permission.granted_value); + } + this.update_icon(); + this.update_filter(); + } + set_mode(mode) { + this.mode_container_permissions.css('display', mode == Modals.PermissionEditorMode.VISIBLE ? 'flex' : 'none'); + this.mode_container_error_permission.css('display', mode == Modals.PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); + this.mode_container_unset.css('display', mode == Modals.PermissionEditorMode.UNSET ? 'block' : 'none'); + if (this.icon_shown != (mode == Modals.PermissionEditorMode.VISIBLE)) { + this.icon_shown = mode == Modals.PermissionEditorMode.VISIBLE; + this.update_icon(); + } + } + trigger_change(permission, value, update_icon) { + if (this._listener_change) { + if ((typeof (update_icon) !== "boolean" || update_icon) && permission && permission.name === "i_icon_id") + return this._listener_change(permission, value).then(e => { + setTimeout(() => this.update_icon(), 0); /* we need to fully handle the response and then only we're able to update the icon */ + return e; + }); + else + return this._listener_change(permission, value); + } + return Promise.reject(); + } + collapse_all() { + for (const group of this.permission_groups) { + group.collapsed = true; + for (const child of group.children) + child.parent_collapsed = true; + } + this.update_filter(); /* update display state of all entries */ + } + expend_all() { + for (const group of this.permission_groups) { + group.collapsed = false; + group.parent_collapsed = false; + } + this.update_filter(); /* update display state of all entries */ + } + update_view() { return this.update_filter(); } + set_toggle_button(callback, initial) { + this._toggle_callback = callback; + if (this._toggle_callback) { + this.button_toggle.text(initial); + this.button_toggle.show(); + } + else { + this.button_toggle.hide(); + } + } + } + pe.HTMLPermissionEditor = HTMLPermissionEditor; +})(pe || (pe = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["c3f77abe415be432cfd830b2810233936c398e3aacff06ea939745caaa7b782e"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["c3f77abe415be432cfd830b2810233936c398e3aacff06ea939745caaa7b782e"] = "c3f77abe415be432cfd830b2810233936c398e3aacff06ea939745caaa7b782e"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +/// +var permissions; +(function (permissions) { + permissions.senseless_server_group_permissions = [ + PermissionType.B_CHANNEL_GROUP_INHERITANCE_END + ]; + const filter = (text, ignore_type) => Object.keys(PermissionType) + .filter(e => e.toLowerCase().substr(ignore_type ? 1 : 0).startsWith(text)).map(e => PermissionType[e]); + permissions.senseless_channel_group_permissions = [ + //Not sensefull to assign serverinstance permission to channel groups + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_serverinstance_")).map(e => PermissionType[e]), + PermissionType.B_SERVERQUERY_LOGIN, + //Not sensefull to assign virtual server permission to channel groups + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_virtualserver") && e.toLowerCase() === "b_virtualserver_channel_permission_list").map(e => PermissionType[e]), + //Not sensefull to require some playlist permissions + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_playlist")).map(e => PermissionType[e]), + PermissionType.B_PLAYLIST_CREATE, + //Not sensefull to require some playlist permissions + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_client_music")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_music")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_server_group")).map(e => PermissionType[e]), + PermissionType.I_MAX_ICON_FILESIZE, + PermissionType.I_MAX_PLAYLIST_SIZE, + PermissionType.I_MAX_PLAYLISTS, + PermissionType.I_CLIENT_KICK_FROM_SERVER_POWER, + PermissionType.I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER, + PermissionType.I_CLIENT_BAN_POWER, + PermissionType.I_CLIENT_NEEDED_BAN_POWER, + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_complain")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_ban")).map(e => PermissionType[e]), + PermissionType.I_CLIENT_BAN_MAX_BANTIME, + PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_query")).map(e => PermissionType[e]), + PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN, + PermissionType.B_CLIENT_DELETE_DBPROPERTIES, + PermissionType.B_CLIENT_MODIFY_DBPROPERTIES + ]; + permissions.senseless_channel_permissions = [ + ...permissions.senseless_channel_group_permissions, + ...filter("_channel_create", true), + ...filter("_client", true), + ...filter("_channel_group", true), + ...filter("_group", true), + ...filter("b_channel_", false), + ...Object.keys(PermissionType).filter(e => { + e = e.toLowerCase(); + return e.indexOf("_power") > 0 && e.indexOf("_needed_") == -1; + }).map(e => PermissionType[e]), + PermissionType.B_ICON_MANAGE, + PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, + PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, + PermissionType.B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER + ]; + permissions.senseless_client_permissions = []; + permissions.senseless_client_channel_permissions = []; +})(permissions || (permissions = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["27c1036c60874525f05c5900623d88fc60a288957a6b0b374d296c90f747c966"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["27c1036c60874525f05c5900623d88fc60a288957a6b0b374d296c90f747c966"] = "27c1036c60874525f05c5900623d88fc60a288957a6b0b374d296c90f747c966"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of []) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +var audio; +(function (audio) { + let recorder; + (function (recorder) { + let InputConsumerType; + (function (InputConsumerType) { + InputConsumerType[InputConsumerType["CALLBACK"] = 0] = "CALLBACK"; + InputConsumerType[InputConsumerType["NODE"] = 1] = "NODE"; + InputConsumerType[InputConsumerType["NATIVE"] = 2] = "NATIVE"; + })(InputConsumerType = recorder.InputConsumerType || (recorder.InputConsumerType = {})); + let filter; + (function (filter) { + let Type; + (function (Type) { + Type[Type["THRESHOLD"] = 0] = "THRESHOLD"; + Type[Type["VOICE_LEVEL"] = 1] = "VOICE_LEVEL"; + Type[Type["STATE"] = 2] = "STATE"; + })(Type = filter.Type || (filter.Type = {})); + })(filter = recorder.filter || (recorder.filter = {})); + let InputState; + (function (InputState) { + InputState[InputState["PAUSED"] = 0] = "PAUSED"; + InputState[InputState["INITIALIZING"] = 1] = "INITIALIZING"; + InputState[InputState["RECORDING"] = 2] = "RECORDING"; + InputState[InputState["DRY"] = 3] = "DRY"; + })(InputState = recorder.InputState || (recorder.InputState = {})); + let InputStartResult; + (function (InputStartResult) { + InputStartResult["EOK"] = "eok"; + InputStartResult["EUNKNOWN"] = "eunknown"; + InputStartResult["EBUSY"] = "ebusy"; + InputStartResult["ENOTALLOWED"] = "enotallowed"; + InputStartResult["ENOTSUPPORTED"] = "enotsupported"; + })(InputStartResult = recorder.InputStartResult || (recorder.InputStartResult = {})); + })(recorder = audio.recorder || (audio.recorder = {})); +})(audio || (audio = {})); +typeof _translations !== "undefined" || (_translations = {}); +_translations["declared"] = _translations["declared"] || (_translations["declared"] = {}); +_translations["declared_files"] = _translations["declared_files"] || (_translations["declared_files"] = {}); +unique_translation_check: { + if (_translations["declared_files"]["40c0a0ccf3b451cbca3133f589b3c49ad1383cc1a1064fb3e33f50b1892dfdfc"] !== undefined) { + console.warn("This file has already been loaded!\nAre you executing scripts twice?"); + break unique_translation_check; + } + else + _translations["declared_files"]["40c0a0ccf3b451cbca3133f589b3c49ad1383cc1a1064fb3e33f50b1892dfdfc"] = "40c0a0ccf3b451cbca3133f589b3c49ad1383cc1a1064fb3e33f50b1892dfdfc"; + /*Auto generated helper for testing if the translation keys are unique*/ + for (var { name: _i, path: _a } of [{ name: "A6UWPV3j", path: "D:/TeaSpeak/web/shared/js/voice/RecorderProfile.ts (134,41)" }, { name: "oKaNjzJq", path: "D:/TeaSpeak/web/shared/js/voice/RecorderProfile.ts (138,46)" }, { name: "WZgGQyvw", path: "D:/TeaSpeak/web/shared/js/voice/RecorderProfile.ts (189,45)" }]) { + if (_translations["declared"][_i] !== undefined) + throw "Translation with generated name \"" + _i + "\" already exists!\nIt has been already defined here: " + _translations["declared"][_i] + "\nAttempted to redefine here: " + _a + "\nRegenerate and/or fix your program!"; + else + _translations["declared"][_i] = _a; + } +} +let default_recorder; /* needs initialize */ +class RecorderProfile { + constructor(name, volatile) { + this.name = name; + this.volatile = typeof (volatile) === "boolean" ? volatile : false; + this._ppt_hook = { + callback_release: () => { + if (this._ppt_timeout) + clearTimeout(this._ppt_timeout); + this._ppt_timeout = setTimeout(() => { + const filter = this.input.get_filter(audio.recorder.filter.Type.STATE); + if (filter) + filter.set_state(true); + }, Math.min(this.config.vad_push_to_talk.delay, 0)); + }, + callback_press: () => { + if (this._ppt_timeout) + clearTimeout(this._ppt_timeout); + const filter = this.input.get_filter(audio.recorder.filter.Type.STATE); + if (filter) + filter.set_state(false); + }, + cancel: false + }; + this._ppt_hook_registered = false; + this.record_supported = true; + } + initialize() { + return __awaiter(this, void 0, void 0, function* () { + audio.player.on_ready(() => __awaiter(this, void 0, void 0, function* () { + this.initialize_input(); + yield this.load(); + yield this.reinitialize_filter(); + })); + }); + } + initialize_input() { + this.input = audio.recorder.create_input(); + this.input.callback_begin = () => { + log.debug(LogCategory.VOICE, "Voice start"); + if (this.callback_start) + this.callback_start(); + }; + this.input.callback_end = () => { + log.debug(LogCategory.VOICE, "Voice end"); + if (this.callback_stop) + this.callback_stop(); + }; + } + load() { + return __awaiter(this, void 0, void 0, function* () { + const config = settings.static_global(Settings.FN_PROFILE_RECORD(this.name), {}); + /* default values */ + this.config = { + version: 1, + device_id: undefined, + volume: 100, + vad_threshold: { + threshold: 50 + }, + vad_type: "threshold", + vad_push_to_talk: { + delay: 300, + key_alt: false, + key_ctrl: false, + key_shift: false, + key_windows: false, + key_code: 't' + } + }; + Object.assign(this.config, config || {}); + this.input.set_volume(this.config.volume / 100); + { + const all_devices = audio.recorder.devices(); + const devices = all_devices.filter(e => e.default_input || e.unique_id === this.config.device_id); + const device = devices.find(e => e.unique_id === this.config.device_id) || devices[0]; + log.info(LogCategory.VOICE, _translations.A6UWPV3j || (_translations.A6UWPV3j = tr("Loaded record profile device %s | %o (%o)")), this.config.device_id, device, all_devices); + try { + yield this.input.set_device(device); + } + catch (error) { + log.error(LogCategory.VOICE, _translations.oKaNjzJq || (_translations.oKaNjzJq = tr("Failed to set input device (%o)")), error); + } + } + }); + } + save(enforce) { + if (enforce || !this.volatile) + settings.changeGlobal(Settings.FN_PROFILE_RECORD(this.name), this.config); + } + reinitialize_filter() { + return __awaiter(this, void 0, void 0, function* () { + if (!this.input) + return; + this.input.clear_filter(); + if (this._ppt_hook_registered) { + ppt.unregister_key_hook(this._ppt_hook); + this._ppt_hook_registered = false; + } + if (this.config.vad_type === "threshold") { + const filter = this.input.get_filter(audio.recorder.filter.Type.THRESHOLD); + yield filter.set_threshold(this.config.vad_threshold.threshold); + yield filter.set_margin_frames(10); /* 500ms */ + /* legacy client support */ + if ('set_attack_smooth' in filter) + filter.set_attack_smooth(.25); + if ('set_release_smooth' in filter) + filter.set_release_smooth(.9); + this.input.enable_filter(audio.recorder.filter.Type.THRESHOLD); + } + else if (this.config.vad_type === "push_to_talk") { + const filter = this.input.get_filter(audio.recorder.filter.Type.STATE); + yield filter.set_state(true); + for (const key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"]) + this._ppt_hook[key] = this.config.vad_push_to_talk[key]; + ppt.register_key_hook(this._ppt_hook); + this._ppt_hook_registered = true; + this.input.enable_filter(audio.recorder.filter.Type.STATE); + } + else if (this.config.vad_type === "active") { } + }); + } + unmount() { + return __awaiter(this, void 0, void 0, function* () { + if (this.callback_unmount) + this.callback_unmount(); + if (this.input) { + try { + yield this.input.set_consumer(undefined); + } + catch (error) { + log.warn(LogCategory.VOICE, _translations.WZgGQyvw || (_translations.WZgGQyvw = tr("Failed to unmount input consumer for profile (%o)")), error); + } + } + this.callback_start = undefined; + this.callback_stop = undefined; + this.callback_unmount = undefined; + this.current_handler = undefined; + }); + } + get_vad_type() { return this.config.vad_type; } + set_vad_type(type) { + if (this.config.vad_type === type) + return true; + if (["push_to_talk", "threshold", "active"].findIndex(e => e === type) == -1) + return false; + this.config.vad_type = type; + this.reinitialize_filter(); + this.save(); + return true; + } + get_vad_threshold() { return parseInt(this.config.vad_threshold.threshold); } /* for some reason it might be a string... */ + set_vad_threshold(value) { + if (this.config.vad_threshold.threshold === value) + return; + this.config.vad_threshold.threshold = value; + this.reinitialize_filter(); + this.save(); + } + get_vad_ppt_key() { return this.config.vad_push_to_talk; } + set_vad_ppt_key(key) { + for (const _key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"]) + this.config.vad_push_to_talk[_key] = key[_key]; + this.reinitialize_filter(); + this.save(); + } + get_vad_ppt_delay() { return this.config.vad_push_to_talk.delay; } + set_vad_ppt_delay(value) { + if (this.config.vad_push_to_talk.delay === value) + return; + this.config.vad_push_to_talk.delay = value; + this.reinitialize_filter(); + this.save(); + } + current_device() { return this.input.current_device(); } + set_device(device) { + this.config.device_id = device ? device.unique_id : undefined; + this.save(); + return this.input.set_device(device); + } + get_volume() { return this.input ? (this.input.get_volume() * 100) : this.config.volume; } + set_volume(volume) { + if (this.config.volume === volume) + return; + this.config.volume = volume; + this.input && this.input.set_volume(volume / 100); + this.save(); + } +} +//# sourceMappingURL=shared.js.map +//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/client/generated/client.js.map b/client/generated/client.js.map new file mode 100644 index 00000000..8cfde89f --- /dev/null +++ b/client/generated/client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"client.js","sourceRoot":"","sources":["../../shared/generated/shared.js"],"names":[],"mappings":"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AAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAA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EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iDAAiD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kDAAkD,EAAE,EAAE,EAAE,IAAI,EAAE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EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+CAA+C,EAAE,CAAC,EAAE;QACt6D,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS;YAC3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,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAA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,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,oEAAoE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAA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AAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,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AAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,8DAA8D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,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AE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,0DAA0D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,0DAA0D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,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UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC9C,UAAU,EAAE,U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AAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,yDAAyD,EAA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,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,4DAA4D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,6DAA6D,EAAE,CAAC,EAAE;QAClmI,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,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,CAAC,EAAE;QAClmG,IAAI,aAAa,C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E,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+DAA+D,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,E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E,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gEAAgE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IA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U,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iEAAiE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,kEAAkE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,I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,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,+EAA+E,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iFAAiF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iFAAiF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,iFAAiF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,gFAAgF,EAAE,EAAE,EAAE,IAAI,E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"} \ No newline at end of file diff --git a/scripts/build_declarations.sh b/scripts/build_declarations.sh index 1e7d8c7c..8d366527 100755 --- a/scripts/build_declarations.sh +++ b/scripts/build_declarations.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash BASEDIR=$(dirname "$0") +# shellcheck disable=SC1090 source "${BASEDIR}/resolve_commands.sh" -cd "$BASEDIR/../" +cd "$BASEDIR/../" || { echo "Failed to enter parent directory!"; exit 1; } function generate_link() { if [[ ! -L $2 ]] || [[ "${BASH_ARGV[0]}" == "force" ]]; then @@ -20,12 +21,19 @@ function replace_tribble() { #Building the generator -./tools/build_dtsgen.sh -if [[ $? -ne 0 ]]; then - echo "Failed to build typescript declaration generator" +./tools/build_dtsgen.sh; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to build typescript declaration generator ($_exit_code)" exit 1 fi +#Shared +./shared/generate_declarations.sh; _exit_code=$? +[[ $_exit_code -ne 0 ]] && { + echo "Failed to generate shared ($_exit_code)" +} + +exit 0 #Easy going: Each "module" has it's exports and imports #So lets first build the exports and ignore any errors #Note: For the client we have to use the given file @@ -40,12 +48,6 @@ npm run dtsgen -- --config client/tsconfig/dtsconfig.json -v replace_tribble client/declarations/exports.d.ts echo "Generated client declarations" -#Shared -./shared/generate_declarations.sh -[[ $? -ne 0 ]] && { - echo "Failed to generate shared" -} - #replace_tribble shared/declarations/exports.d.ts echo "Generated shared declarations" diff --git a/shared/generate_declarations.sh b/shared/generate_declarations.sh index 32e2d64b..8029651f 100755 --- a/shared/generate_declarations.sh +++ b/shared/generate_declarations.sh @@ -1,21 +1,21 @@ #!/usr/bin/env bash BASEDIR=$(dirname "$0") -cd "$BASEDIR" +cd "$BASEDIR" || { echo "Failed to enter script base dir"; exit 1; } source ../scripts/resolve_commands.sh function generate_declaration() { echo "Generating declarations for project $1 ($2)" - if [[ -e ${2} ]]; then - rm ${2} - if [[ $? -ne 0 ]]; then - echo "Failed to remove old declaration file ($2)!" + if [[ -e "${2}" ]]; then + rm "${2}"; _exit_code=$? + if [[ $_exit_code -ne 0 ]]; then + echo "Failed to remove old declaration file ($2): $_exit_code!" echo "This could be critical later!" fi fi - npm run dtsgen -- --config $(pwd)/tsconfig/$1 -v + npm run dtsgen -- --config "$(pwd)/tsconfig/$1" -v if [[ ! -e $2 ]]; then echo "Failed to generate definitions" exit 1 diff --git a/shared/tsconfig/dtsconfig_app.json b/shared/tsconfig/dtsconfig_app.json index 3b78ff87..317d73b6 100644 --- a/shared/tsconfig/dtsconfig_app.json +++ b/shared/tsconfig/dtsconfig_app.json @@ -1,9 +1,11 @@ { "source_files": [ - "../js/**/*.ts" + "**/*.ts" ], "exclude": [ - "../js/workers/**/*.ts" + "workers/**/*.ts" ], - "target_file": "../declarations/exports_app.d.ts" -} + "base_directory": "shared/js/", + "target_directory": "../declarations/shared-app", + "modular": true +} \ No newline at end of file diff --git a/tools/dtsgen/declarator.ts b/tools/dtsgen/declarator.ts index 46424aa9..29858933 100644 --- a/tools/dtsgen/declarator.ts +++ b/tools/dtsgen/declarator.ts @@ -39,7 +39,7 @@ function append_modifier(modifiers: ts.ModifiersA const comparator = (a: ts.Modifier, b: ts.Modifier) => sort_oder[a.kind] - sort_oder[b.kind]; return ts.createNodeArray( - [ts.createModifier(target as number), ...(modifiers || [])].map((e, index, array) => { + [ts.createModifier(target as number), ...(modifiers || [])].sort(comparator).map((e, index, array) => { const range = ts.getCommentRange(e); if(range.end === -1 && range.pos === -1) return e; @@ -51,7 +51,7 @@ function append_modifier(modifiers: ts.ModifiersA else console.warn("Dropping comment on node because first node already has a comment"); return e; - }).sort(comparator), + }), (modifiers || {hasTrailingComma: false}).hasTrailingComma ); } @@ -167,6 +167,8 @@ export interface Settings { log?: { unhandled_types: boolean; } | boolean; + + module_mode?: boolean; } class _Settings implements Settings { @@ -182,7 +184,9 @@ class _Settings implements Settings { unhandled_types: boolean; } = { unhandled_types: false - } + }; + + module_mode?: boolean; } function specify_settings(settings?: Settings) : _Settings { @@ -201,7 +205,8 @@ function specify_settings(settings?: Settings) : _Settings { result.log = { unhandled_types: settings.log, }; - + if(typeof(settings.module_mode) !== "boolean") + result.module_mode = false; return result; } @@ -210,7 +215,13 @@ export function generate(file: ts.SourceFile, settings?: Settings) : ts.Node[]{ const stack = new StackParameters(); const _settings = specify_settings(settings); + stack.push({ + flag_class: false, + flag_declare: false, + flag_namespace: _settings.module_mode + }); _generate(_settings, stack, layer, file); + stack.pop(); return layer; } @@ -235,12 +246,9 @@ generators[SyntaxKind.ModuleBlock] = (settings, stack, node: ts.ModuleBlock) => }; generators[SyntaxKind.ModuleDeclaration] = (settings, stack, node: ts.ModuleDeclaration) => { - switch (node.flags) { - case ts.NodeFlags.Namespace: - break; - default: - //throw "flag " + node.flags + " isn't supported yet!"; /* TODO wrap with more info */ - } + //if (node.flags & ~(ts.NodeFlags.Namespace | ts.NodeFlags.NestedNamespace | ts.NodeFlags.ExportContext)) { + // throw "Some module declaration flags are not jet supported (flags: " + Object.keys(ts.NodeFlags).filter(e => node.flags & ts.NodeFlags[e]).join(", ") + ")"; + //} stack.push({ @@ -425,24 +433,25 @@ generators[SyntaxKind.EnumDeclaration] = (settings, stack, node: ts.EnumDeclarat return ts.createEnumDeclaration(undefined, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, members); }; -generators[SyntaxKind.HeritageClause] = (settings, stack, node: ts.HeritageClause) => { - return undefined; -}; +generators[SyntaxKind.HeritageClause] = () => undefined; /* every variable in a block has no global scope! */ -generators[SyntaxKind.Block] = (settings, stack, node: ts.Block) => { - return undefined; -}; +generators[SyntaxKind.Block] = () => undefined; +generators[SyntaxKind.IfStatement] = () => undefined; -generators[SyntaxKind.IfStatement] = (settings, stack, node: ts.IfStatement) => { - return undefined; -}; +/* Example for an ExpressionStatement would be: Module["text"] = "XXX"; */ +generators[SyntaxKind.ExpressionStatement] = () => undefined; +generators[SyntaxKind.SemicolonClassElement] = () => undefined; -/* Example for an ExpressionStatement would be: Modul["text"] = "XXX"; */ -generators[SyntaxKind.ExpressionStatement] = (settings, stack, node: ts.ExpressionStatement) => { - return undefined; -}; +generators[SyntaxKind.ImportDeclaration] = (settings, stack, node: ts.ImportDeclaration) => { + const specifier = node.moduleSpecifier as ts.StringLiteral; + if(specifier.kind !== SyntaxKind.StringLiteral) + throw "cant handle import declaration with specifier of type " + specifier.kind; -generators[SyntaxKind.SemicolonClassElement] = (settings, stack, node: ts.ExpressionStatement) => { - return undefined; + return ts.createImportDeclaration( + node.decorators, + node.modifiers, + node.importClause, + ts.createStringLiteral(specifier.text + ".d") + ); }; \ No newline at end of file diff --git a/tools/dtsgen/index.ts b/tools/dtsgen/index.ts index 8b9742ff..f6d0deb4 100644 --- a/tools/dtsgen/index.ts +++ b/tools/dtsgen/index.ts @@ -1,40 +1,44 @@ -import {readFileSync, writeFileSync, mkdir} from "fs"; -import {isArray, isString} from "util"; +import {readFileSync, writeFileSync, mkdir, existsSync} from "fs"; import * as ts from "typescript"; import * as decl from "./declarator"; import * as glob from "glob"; import * as path from "path"; import * as mkdirp from "mkdirp"; +import {removeSync} from "fs-extra"; let source_files: string[] = []; let exclude_files: string[] = []; -let target_file: string = "out.d.ts"; +let target_directory: string = "out.d/"; let verbose: boolean = false; let config_file: string = undefined; let base_path = process.cwd(); +let module_mode: boolean = false; let args = process.argv.slice(2); while(args.length > 0) { - if(args[0] == "--file") { + if(args[0] === "--file") { source_files.push(args[1]); args = args.slice(2); - } else if(args[0] == "--exclude") { + } else if(args[0] === "--exclude") { exclude_files.push(args[1]); args = args.slice(2); - } else if(args[0] == "--destination") { - target_file = args[1]; + } else if(args[0] === "--destination") { + target_directory = args[1]; args = args.slice(2); - } else if(args[0] == "-v" || args[0] == "--verbose") { + } else if(args[0] === "-v" || args[0] === "--verbose") { verbose = true; args = args.slice(1); - } else if(args[0] == "-c" || args[0] == "--config") { + } else if(args[0] === "-c" || args[0] === "--config") { config_file = args[1]; base_path = path.normalize(path.dirname(config_file)); args = args.slice(2); - } else if(args[0] == "-b" || args[0] == "--base") { + } else if(args[0] === "-b" || args[0] === "--base-directory") { base_path = args[1]; base_path = path.normalize(base_path); args = args.slice(2); + } else if(args[0] === "-m" || args[0] === "--module") { + module_mode = true; + args = args.slice(1); } else { console.error("Invalid command line option %s", args[0]); process.exit(1); @@ -49,12 +53,16 @@ if(config_file) { process.exit(1); } - if(isArray(json["source_files"])) + if(Array.isArray(json["source_files"])) source_files.push(...json["source_files"]); - if(isArray(json["exclude"])) + if(Array.isArray(json["exclude"])) exclude_files.push(...json["exclude"]); - if(isString(json["target_file"])) - target_file = json["target_file"]; + if(typeof json["target_directory"] === "string") + target_directory = json["target_directory"]; + if(typeof json["base_directory"] === "string") + base_path = json["base_directory"]; + if(typeof json["modular"] === "boolean") + module_mode = json["modular"]; } if(verbose) { @@ -62,15 +70,30 @@ if(verbose) { console.log("Input files:"); for(const file of source_files) console.log(" - " + file); - console.log("Target file: " + target_file); + console.log("Target directory: " + target_directory); +} + +if(existsSync(target_directory)) { + removeSync(target_directory); + if(existsSync(target_directory)) { + console.error("Failed to remove target directory (%s)", target_directory); + process.exit(1); + } } const negate_files: string[] = [].concat.apply([], exclude_files.map(file => glob.sync(base_path + "/" + file))).map(file => path.normalize(file)); -let result = ""; source_files.forEach(file => { - glob.sync(base_path + "/" + file).forEach(_file => { + const glob_base = path.normalize(path.join(process.cwd(), base_path)); + if(verbose) + console.log("Globbing %s", glob_base); + glob.sync(glob_base + "/" + file).forEach(_file => { _file = path.normalize(_file); + if(!_file.startsWith(glob_base)) { + /* this should never happen */ + console.log("Skipping %s because of unmatching base directory.", _file); + return; + } for(const n_file of negate_files) { if(n_file == _file) { console.log("Skipping %s", _file); @@ -78,6 +101,7 @@ source_files.forEach(file => { } } + const relpath = _file.substr(glob_base.length); let source = ts.createSourceFile( _file, readFileSync(_file).toString(), @@ -85,15 +109,17 @@ source_files.forEach(file => { true ); - console.log("Compile " + _file); - result += "\n/* File: " + _file + " */\n" + decl.print(source, decl.generate(source, { - remove_private: false + console.log("Compile %s (%s)", _file, relpath); + const result = decl.print(source, decl.generate(source, { + remove_private: false, + module_mode: module_mode })); + + let fpath = path.join(base_path, target_directory, relpath); + fpath = fpath.substr(0, fpath.lastIndexOf(".")) + ".d.ts"; + mkdirp(path.normalize(path.dirname(fpath)), error => { + if(error) throw error; + writeFileSync(fpath, result); + }); }); }); - -mkdirp(path.normalize(path.dirname(base_path + "/" + target_file)), error => { - if(error) - throw error; - writeFileSync(base_path + "/" + target_file, result); -}); \ No newline at end of file diff --git a/tools/dtsgen/out.d.ts b/tools/dtsgen/out.d.ts deleted file mode 100644 index 2ae7e1fb..00000000 --- a/tools/dtsgen/out.d.ts +++ /dev/null @@ -1,6 +0,0 @@ - -/* File: /home/wolverindev/TeaSpeak/Web-Client/tools/dtsgen/test/test_07.ts */ -declare namespace C { } -declare namespace C { - export function test(arg: string); -} diff --git a/tools/dtsgen/out.d/module_a.d.ts b/tools/dtsgen/out.d/module_a.d.ts new file mode 100644 index 00000000..cecc643c --- /dev/null +++ b/tools/dtsgen/out.d/module_a.d.ts @@ -0,0 +1,8 @@ +export declare class TestClass { + public say_hi(); +} +export declare function say_hello_a(); +export declare namespace X { + export class Y { + } +} diff --git a/tools/dtsgen/out.d/test_01.d.ts b/tools/dtsgen/out.d/test_01.d.ts new file mode 100644 index 00000000..bb5f153c --- /dev/null +++ b/tools/dtsgen/out.d/test_01.d.ts @@ -0,0 +1,5 @@ +import * as module_a from "./module_a.d"; +export declare class C extends module_a.TestClass { +} +export declare const say_a; +export declare function say_b(); diff --git a/tools/dtsgen/out.d/test_02.d.ts b/tools/dtsgen/out.d/test_02.d.ts new file mode 100644 index 00000000..23752014 --- /dev/null +++ b/tools/dtsgen/out.d/test_02.d.ts @@ -0,0 +1,8 @@ +import * as module_a from "./module_a.d"; +/* CLASS COMMENT!*/ +export declare class C extends module_a.TestClass { +} +/* Say a comment */ +export declare const say_a; +/* Say b comment */ +export declare function say_b(); diff --git a/tools/dtsgen/test_modular/module_a.ts b/tools/dtsgen/test_modular/module_a.ts new file mode 100644 index 00000000..d8c86f08 --- /dev/null +++ b/tools/dtsgen/test_modular/module_a.ts @@ -0,0 +1,11 @@ +export class TestClass { + public say_hi() {} +} + +export function say_hello_a() { + +} + +export namespace X { + export class Y {} +} \ No newline at end of file diff --git a/tools/dtsgen/test_modular/test_01.ts b/tools/dtsgen/test_modular/test_01.ts new file mode 100644 index 00000000..c492eb0c --- /dev/null +++ b/tools/dtsgen/test_modular/test_01.ts @@ -0,0 +1,9 @@ +import * as module_a from "./module_a"; + +export class C extends module_a.TestClass {} + +export const say_a = module_a.say_hello_a; +export function say_b() { + console.log("B!"); + module_a.say_hello_a(); +} \ No newline at end of file diff --git a/tools/dtsgen/test_modular/test_02.ts b/tools/dtsgen/test_modular/test_02.ts new file mode 100644 index 00000000..478befd7 --- /dev/null +++ b/tools/dtsgen/test_modular/test_02.ts @@ -0,0 +1,14 @@ +/* IMPOTZ COMMENT */ +import * as module_a from "./module_a"; + +/* CLASS COMMENT!*/ +export class C extends module_a.TestClass {} + +/* Say a comment */ +export const say_a = module_a.say_hello_a; + +/* Say b comment */ +export function say_b() { + console.log("B!"); + module_a.say_hello_a(); +} \ No newline at end of file diff --git a/tools/dtsgen/test_modular/tools/dtsgen/out.d b/tools/dtsgen/test_modular/tools/dtsgen/out.d new file mode 100644 index 00000000..2596a91c --- /dev/null +++ b/tools/dtsgen/test_modular/tools/dtsgen/out.d @@ -0,0 +1,5 @@ + +/* File: tools\dtsgen\test_modular\test_01.ts */ +import * as module_a from "./module_a"; +export declare const say_a; +export declare function say_b(); diff --git a/tools/dtsgen/test/test_01.ts b/tools/dtsgen/test_non_modular/test_01.ts similarity index 100% rename from tools/dtsgen/test/test_01.ts rename to tools/dtsgen/test_non_modular/test_01.ts diff --git a/tools/dtsgen/test/test_02.ts b/tools/dtsgen/test_non_modular/test_02.ts similarity index 100% rename from tools/dtsgen/test/test_02.ts rename to tools/dtsgen/test_non_modular/test_02.ts diff --git a/tools/dtsgen/test/test_03.ts b/tools/dtsgen/test_non_modular/test_03.ts similarity index 100% rename from tools/dtsgen/test/test_03.ts rename to tools/dtsgen/test_non_modular/test_03.ts diff --git a/tools/dtsgen/test/test_04.ts b/tools/dtsgen/test_non_modular/test_04.ts similarity index 100% rename from tools/dtsgen/test/test_04.ts rename to tools/dtsgen/test_non_modular/test_04.ts diff --git a/tools/dtsgen/test/test_05.ts b/tools/dtsgen/test_non_modular/test_05.ts similarity index 100% rename from tools/dtsgen/test/test_05.ts rename to tools/dtsgen/test_non_modular/test_05.ts diff --git a/tools/dtsgen/test/test_06.ts b/tools/dtsgen/test_non_modular/test_06.ts similarity index 100% rename from tools/dtsgen/test/test_06.ts rename to tools/dtsgen/test_non_modular/test_06.ts diff --git a/tools/dtsgen/test/test_07.ts b/tools/dtsgen/test_non_modular/test_07.ts similarity index 100% rename from tools/dtsgen/test/test_07.ts rename to tools/dtsgen/test_non_modular/test_07.ts diff --git a/tsconfig.json b/tsconfig.json index dbf1f46a..95a8baf6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,11 @@ "sourceMap": true, "lib": ["es6", "dom", "dom.iterable"], "removeComments": true, /* we dont really need them within the target files */ - "jsx": "react" + "jsx": "react", + "baseUrl": ".", + "paths": { + "*": ["shared/declarations/*"] + } }, "exclude": [ "node_modules", diff --git a/web/js/audio/AudioPlayer.ts b/web/js/audio/AudioPlayer.ts index 25538997..a0c6ea34 100644 --- a/web/js/audio/AudioPlayer.ts +++ b/web/js/audio/AudioPlayer.ts @@ -1,3 +1,5 @@ +import {log, LogCategory} from "shared-app/log"; + namespace audio.player { let _globalContext: AudioContext; let _global_destination: GainNode; From 447e84ed0f9bdbb1797ab64b0f59c6b706ca0a16 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sun, 29 Mar 2020 12:54:15 +0200 Subject: [PATCH 05/23] Reset all js files and start from zero, this time more structured --- shared/js/BrowserIPC.ts | 4 +- shared/js/ConnectionHandler.ts | 26 +- shared/js/FileManager.ts | 19 +- shared/js/MessageFormatter.ts | 8 +- shared/js/PPTListener.ts | 4 +- shared/js/bookmarks.ts | 12 +- shared/js/connection/CommandHandler.ts | 2120 ++++++++--------- shared/js/connection/CommandHelper.ts | 842 ++++--- shared/js/connection/ConnectionBase.ts | 368 ++- shared/js/connection/HandshakeHandler.ts | 19 +- .../connection/ServerConnectionDeclaration.ts | 22 +- shared/js/crypto/asn1.ts | 2 +- shared/js/crypto/crc32.ts | 2 +- shared/js/crypto/hex.ts | 2 +- shared/js/crypto/sha.ts | 2 +- shared/js/crypto/uid.ts | 8 - shared/js/dns.ts | 2 +- shared/js/events.ts | 6 +- shared/js/i18n/country.ts | 3 +- shared/js/i18n/localize.ts | 15 +- shared/js/log.ts | 6 +- shared/js/main.ts | 46 +- shared/js/permission/GroupManager.ts | 21 +- shared/js/permission/PermissionManager.ts | 27 +- shared/js/profiles/ConnectionProfile.ts | 477 ++-- shared/js/profiles/Identity.ts | 188 +- shared/js/profiles/identities/NameIdentity.ts | 142 +- .../profiles/identities/TeaForumIdentity.ts | 216 +- .../profiles/identities/TeamSpeakIdentity.ts | 1509 ++++++------ .../js/profiles/identities/teaspeak-forum.ts | 23 +- shared/js/proto.ts | 2 + shared/js/settings.ts | 15 +- shared/js/sound/Sounds.ts | 8 +- shared/js/stats.ts | 4 +- shared/js/ui/channel-tree/channel.css | 81 - shared/js/ui/channel-tree/channel.css.map | 1 - shared/js/ui/channel-tree/channel.scss | 106 - shared/js/ui/channel-tree/channel.tsx | 120 - shared/js/ui/channel-tree/colors.css | 3 - shared/js/ui/channel-tree/colors.css.map | 1 - shared/js/ui/channel-tree/colors.scss | 3 - shared/js/ui/channel-tree/tree.css | 38 - shared/js/ui/channel-tree/tree.css.map | 1 - shared/js/ui/channel-tree/tree.scss | 50 - shared/js/ui/channel-tree/tree.tsx | 5 - shared/js/{channel-tree => ui}/channel.ts | 13 +- shared/js/{channel-tree => ui}/client.ts | 25 +- shared/js/ui/client_move.ts | 4 +- shared/js/ui/elements/context_divider.ts | 6 +- shared/js/ui/elements/context_menu.ts | 2 +- shared/js/ui/elements/modal.ts | 94 +- shared/js/ui/elements/net_graph.ts | 2 +- shared/js/ui/elements/slider.ts | 6 +- shared/js/ui/elements/tab.ts | 161 ++ shared/js/ui/elements/tooltip.ts | 4 +- shared/js/ui/frames/ControlBar.ts | 27 +- shared/js/ui/frames/MenuBar.ts | 10 +- shared/js/ui/frames/chat.ts | 6 +- shared/js/ui/frames/chat_frame.ts | 8 +- shared/js/ui/frames/connection_handlers.ts | 8 +- shared/js/ui/frames/hostbanner.ts | 6 +- shared/js/ui/frames/image_preview.ts | 2 +- shared/js/ui/frames/server_log.ts | 985 ++++---- shared/js/ui/frames/side/chat_box.ts | 2 +- shared/js/ui/frames/side/chat_helper.ts | 2 +- shared/js/ui/frames/side/client_info.ts | 9 +- shared/js/ui/frames/side/conversations.ts | 8 +- shared/js/ui/frames/side/music_info.ts | 5 +- .../ui/frames/side/private_conversations.ts | 6 +- shared/js/ui/htmltags.ts | 2 +- shared/js/ui/modal/ModalAbout.ts | 6 +- shared/js/ui/modal/ModalAvatar.ts | 6 +- shared/js/ui/modal/ModalAvatarList.ts | 6 +- shared/js/ui/modal/ModalBanClient.ts | 5 + shared/js/ui/modal/ModalBanList.ts | 7 +- shared/js/ui/modal/ModalBookmarks.ts | 6 +- shared/js/ui/modal/ModalChangeLatency.ts | 6 +- shared/js/ui/modal/ModalChangeVolume.ts | 6 +- shared/js/ui/modal/ModalChannelInfo.ts | 2 +- shared/js/ui/modal/ModalClientInfo.ts | 2 +- shared/js/ui/modal/ModalConnect.ts | 6 +- shared/js/ui/modal/ModalCreateChannel.ts | 4 +- shared/js/ui/modal/ModalGroupAssignment.ts | 2 +- shared/js/ui/modal/ModalIconSelect.ts | 6 +- shared/js/ui/modal/ModalIdentity.ts | 2 +- shared/js/ui/modal/ModalInvite.ts | 6 +- shared/js/ui/modal/ModalKeySelect.ts | 2 +- shared/js/ui/modal/ModalMusicManage.ts | 6 +- shared/js/ui/modal/ModalNewcomer.ts | 6 +- shared/js/ui/modal/ModalPlaylistEdit.ts | 6 +- shared/js/ui/modal/ModalPlaylistList.ts | 7 +- shared/js/ui/modal/ModalPoke.ts | 6 +- shared/js/ui/modal/ModalQuery.ts | 6 +- shared/js/ui/modal/ModalQueryManage.ts | 6 +- shared/js/ui/modal/ModalServerEdit.ts | 2 +- shared/js/ui/modal/ModalServerInfo.ts | 2 +- .../js/ui/modal/ModalServerInfoBandwidth.ts | 2 +- shared/js/ui/modal/ModalSettings.ts | 2 +- shared/js/ui/modal/ModalYesNo.ts | 4 +- .../permission/CanvasPermissionEditor.ts | 4 +- .../modal/permission/HTMLPermissionEditor.ts | 6 +- .../modal/permission/ModalPermissionEdit.ts | 6 +- .../modal/permission/SenselessPermissions.ts | 4 +- shared/js/{channel-tree => ui}/server.ts | 12 +- shared/js/{channel-tree => ui}/view.ts | 6 +- shared/js/utils/helpers.ts | 6 +- shared/js/voice/RecorderBase.ts | 2 +- shared/js/voice/RecorderProfile.ts | 10 +- tsconfig.json | 4 +- web/js/audio/AudioPlayer.ts | 2 - webpack.config.js | 4 + 111 files changed, 3946 insertions(+), 4237 deletions(-) delete mode 100644 shared/js/crypto/uid.ts delete mode 100644 shared/js/ui/channel-tree/channel.css delete mode 100644 shared/js/ui/channel-tree/channel.css.map delete mode 100644 shared/js/ui/channel-tree/channel.scss delete mode 100644 shared/js/ui/channel-tree/channel.tsx delete mode 100644 shared/js/ui/channel-tree/colors.css delete mode 100644 shared/js/ui/channel-tree/colors.css.map delete mode 100644 shared/js/ui/channel-tree/colors.scss delete mode 100644 shared/js/ui/channel-tree/tree.css delete mode 100644 shared/js/ui/channel-tree/tree.css.map delete mode 100644 shared/js/ui/channel-tree/tree.scss delete mode 100644 shared/js/ui/channel-tree/tree.tsx rename shared/js/{channel-tree => ui}/channel.ts (99%) rename shared/js/{channel-tree => ui}/client.ts (99%) create mode 100644 shared/js/ui/elements/tab.ts rename shared/js/{channel-tree => ui}/server.ts (98%) rename shared/js/{channel-tree => ui}/view.ts (99%) diff --git a/shared/js/BrowserIPC.ts b/shared/js/BrowserIPC.ts index a371cba7..b7271f6e 100644 --- a/shared/js/BrowserIPC.ts +++ b/shared/js/BrowserIPC.ts @@ -1,8 +1,8 @@ -export interface Window { +interface Window { BroadcastChannel: BroadcastChannel; } -export namespace bipc { +namespace bipc { export interface BroadcastMessage { timestamp: number; receiver: string; diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts index d1256c5f..85c3b8af 100644 --- a/shared/js/ConnectionHandler.ts +++ b/shared/js/ConnectionHandler.ts @@ -1,6 +1,6 @@ /// /// -/// +/// /// /// /// @@ -8,17 +8,7 @@ /// /// -import {ChannelTree} from "./channel-tree/view"; -import {LocalClientEntry} from "./channel-tree/client"; -import {ServerAddress} from "./channel-tree/server"; -import {ChannelEntry} from "./channel-tree/channel"; -import {AbstractServerConnection} from "./connection/ConnectionBase"; -import {PermissionManager} from "./permission/PermissionManager"; -import {GroupManager} from "./permission/GroupManager"; -import {ServerSettings} from "./settings"; -import {Hostbanner} from "./ui/frames/hostbanner"; - -export enum DisconnectReason { +enum DisconnectReason { HANDLER_DESTROYED, REQUESTED, DNS_FAILED, @@ -38,7 +28,7 @@ export enum DisconnectReason { UNKNOWN } -export enum ConnectionState { +enum ConnectionState { UNCONNECTED, CONNECTING, INITIALISING, @@ -46,7 +36,7 @@ export enum ConnectionState { DISCONNECTING } -export enum ViewReasonId { +enum ViewReasonId { VREASON_USER_ACTION = 0, VREASON_MOVED = 1, VREASON_SYSTEM = 2, @@ -61,7 +51,7 @@ export enum ViewReasonId { VREASON_SERVER_SHUTDOWN = 11 } -export interface VoiceStatus { +interface VoiceStatus { input_hardware: boolean; input_muted: boolean; output_muted: boolean; @@ -78,7 +68,7 @@ export interface VoiceStatus { queries_visible: boolean; } -export interface ConnectParameters { +interface ConnectParameters { nickname?: string; channel?: { target: string | number; @@ -89,10 +79,10 @@ export interface ConnectParameters { auto_reconnect_attempt?: boolean; } -export class ConnectionHandler { +class ConnectionHandler { channelTree: ChannelTree; - serverConnection: AbstractServerConnection; + serverConnection: connection.AbstractServerConnection; fileManager: FileManager; diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index b20e369a..e20cc336 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -1,26 +1,21 @@ -import {ChannelEntry} from "./channel-tree/channel"; -import {AbstractCommandHandler, ServerCommand} from "./connection/ConnectionBase"; -import {ConnectionHandler} from "./ConnectionHandler"; -import {CommandResult} from "./connection/ServerConnectionDeclaration"; -import {log, LogCategory} from "./log"; -import {ClientEntry} from "./channel-tree/client"; -import {hex} from "./crypto/hex"; +/// +/// -export class FileEntry { +class FileEntry { name: string; datetime: number; type: number; size: number; } -export class FileListRequest { +class FileListRequest { path: string; entries: FileEntry[]; callback: (entries: FileEntry[]) => void; } -export namespace transfer { +namespace transfer { export interface TransferKey { client_transfer_id: number; server_transfer_id: number; @@ -157,7 +152,7 @@ class RequestFileUpload implements transfer.UploadTransfer { } } -class FileManager extends AbstractCommandHandler { +class FileManager extends connection.AbstractCommandHandler { handle: ConnectionHandler; icons: IconManager; avatars: AvatarManager; @@ -196,7 +191,7 @@ class FileManager extends AbstractCommandHandler { this.avatars = undefined; } - handle_command(command: ServerCommand): boolean { + handle_command(command: connection.ServerCommand): boolean { switch (command.command) { case "notifyfilelist": this.notifyFileList(command.arguments); diff --git a/shared/js/MessageFormatter.ts b/shared/js/MessageFormatter.ts index 3ef9906c..66bb2d7f 100644 --- a/shared/js/MessageFormatter.ts +++ b/shared/js/MessageFormatter.ts @@ -1,10 +1,4 @@ -import {Settings, settings} from "./settings"; -import {contextmenu} from "./ui/elements/context_menu"; -import {image_preview} from "./ui/frames/image_preview"; -import {guid} from "./crypto/uid"; - -declare const xbbcode; -export namespace messages.formatter { +namespace messages.formatter { export namespace bbcode { const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]"; const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/; diff --git a/shared/js/PPTListener.ts b/shared/js/PPTListener.ts index 939963a8..b0d32f5d 100644 --- a/shared/js/PPTListener.ts +++ b/shared/js/PPTListener.ts @@ -1,4 +1,4 @@ -export enum KeyCode { +enum KeyCode { KEY_CANCEL = 3, KEY_HELP = 6, KEY_BACK_SPACE = 8, @@ -118,7 +118,7 @@ export enum KeyCode { KEY_META = 224 } -export namespace ppt { +namespace ppt { export enum EventType { KEY_PRESS, KEY_RELEASE, diff --git a/shared/js/bookmarks.ts b/shared/js/bookmarks.ts index 60a1268a..cb12ca5e 100644 --- a/shared/js/bookmarks.ts +++ b/shared/js/bookmarks.ts @@ -1,6 +1,14 @@ -import {profiles} from "./profiles/ConnectionProfile"; +namespace bookmarks { + function guid() { + function s4() { + return Math + .floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + } -export namespace bookmarks { export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => { const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile(); if(profile.valid()) { diff --git a/shared/js/connection/CommandHandler.ts b/shared/js/connection/CommandHandler.ts index e2a72bb1..f5e4bfed 100644 --- a/shared/js/connection/CommandHandler.ts +++ b/shared/js/connection/CommandHandler.ts @@ -1,466 +1,309 @@ -import {ConnectionHandler, DisconnectReason, ViewReasonId} from "../ConnectionHandler"; -import { - AbstractCommandHandler, - AbstractCommandHandlerBoss, - AbstractServerConnection, - CommandOptions, ServerCommand -} from "./ConnectionBase"; -import {CommandResult, ErrorID} from "./ServerConnectionDeclaration"; -import {Sound} from "../sound/Sounds"; -import {log, LogCategory} from "../log"; -import {MessageHelper} from "../ui/frames/chat"; -import { - ClientConnectionInfo, - ClientEntry, - ClientType, - LocalClientEntry, - MusicClientEntry, SongInfo -} from "../channel-tree/client"; -import {ChannelEntry} from "../channel-tree/channel"; -import {chat as pchat} from "../ui/frames/side/private_conversations"; -import {Modals} from "../ui/modal/ModalPoke"; -import {chat} from "../ui/frames/side/conversations"; -import Conversation = chat.channel.Conversation; -import {createErrorModal, createInfoModal, createInputModal, createModal} from "../ui/elements/modal"; -import {server_connections} from "../ui/frames/connection_handlers"; -import {server} from "../ui/frames/server_log"; +/// -export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { - constructor(connection: AbstractServerConnection) { - super(connection); - } -} +namespace connection { + import Conversation = chat.channel.Conversation; + import MusicInfo = chat.MusicInfo; -export class ConnectionCommandHandler extends AbstractCommandHandler { - readonly connection: AbstractServerConnection; - readonly connection_handler: ConnectionHandler; - - constructor(connection: AbstractServerConnection) { - super(connection); - this.connection_handler = connection.client; - - this["error"] = this.handleCommandResult; - this["channellist"] = this.handleCommandChannelList; - this["channellistfinished"] = this.handleCommandChannelListFinished; - this["notifychannelcreated"] = this.handleCommandChannelCreate; - this["notifychanneldeleted"] = this.handleCommandChannelDelete; - this["notifychannelhide"] = this.handleCommandChannelHide; - this["notifychannelshow"] = this.handleCommandChannelShow; - - this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; - this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; - - this["notifycliententerview"] = this.handleCommandClientEnterView; - this["notifyclientleftview"] = this.handleCommandClientLeftView; - this["notifyclientmoved"] = this.handleNotifyClientMoved; - this["initserver"] = this.handleCommandServerInit; - this["notifychannelmoved"] = this.handleNotifyChannelMoved; - this["notifychanneledited"] = this.handleNotifyChannelEdited; - this["notifytextmessage"] = this.handleNotifyTextMessage; - this["notifyclientchatcomposing"] = this.notifyClientChatComposing; - this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; - this["notifyclientupdated"] = this.handleNotifyClientUpdated; - this["notifyserveredited"] = this.handleNotifyServerEdited; - this["notifyserverupdated"] = this.handleNotifyServerUpdated; - - this["notifyclientpoke"] = this.handleNotifyClientPoke; - - this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; - - this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; - this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; - this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; - - this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; - this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; - - this["notifyconversationhistory"] = this.handleNotifyConversationHistory; - this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; - - this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; - this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; - - this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; - this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; - this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; - this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { + constructor(connection: AbstractServerConnection) { + super(connection); + } } - private loggable_invoker(unique_id, client_id, name) : server.base.Client | undefined { - const id = parseInt(client_id); - if(typeof(client_id) === "undefined" || Number.isNaN(id)) - return undefined; + export class ConnectionCommandHandler extends AbstractCommandHandler { + readonly connection: AbstractServerConnection; + readonly connection_handler: ConnectionHandler; + + constructor(connection: AbstractServerConnection) { + super(connection); + this.connection_handler = connection.client; + + this["error"] = this.handleCommandResult; + this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; + this["notifychannelcreated"] = this.handleCommandChannelCreate; + this["notifychanneldeleted"] = this.handleCommandChannelDelete; + this["notifychannelhide"] = this.handleCommandChannelHide; + this["notifychannelshow"] = this.handleCommandChannelShow; + + this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; + this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; + + this["notifycliententerview"] = this.handleCommandClientEnterView; + this["notifyclientleftview"] = this.handleCommandClientLeftView; + this["notifyclientmoved"] = this.handleNotifyClientMoved; + this["initserver"] = this.handleCommandServerInit; + this["notifychannelmoved"] = this.handleNotifyChannelMoved; + this["notifychanneledited"] = this.handleNotifyChannelEdited; + this["notifytextmessage"] = this.handleNotifyTextMessage; + this["notifyclientchatcomposing"] = this.notifyClientChatComposing; + this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; + this["notifyclientupdated"] = this.handleNotifyClientUpdated; + this["notifyserveredited"] = this.handleNotifyServerEdited; + this["notifyserverupdated"] = this.handleNotifyServerUpdated; + + this["notifyclientpoke"] = this.handleNotifyClientPoke; + + this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; + + this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; + this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; + this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; + + this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; + this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; + + this["notifyconversationhistory"] = this.handleNotifyConversationHistory; + this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; + + this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; + this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; + + this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; + this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; + this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; + this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + } + + private loggable_invoker(unique_id, client_id, name) : log.server.base.Client | undefined { + const id = parseInt(client_id); + if(typeof(client_id) === "undefined" || Number.isNaN(id)) + return undefined; + + if(id == 0) + return { + client_id: 0, + client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, + client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, + }; - if(id == 0) return { - client_id: 0, - client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, - client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, + client_unique_id: unique_id, + client_name: name, + client_id: client_id }; + } - return { - client_unique_id: unique_id, - client_name: name, - client_id: client_id - }; - } + proxy_command_promise(promise: Promise, options: connection.CommandOptions) { + if(!options.process_result) + return promise; - proxy_command_promise(promise: Promise, options: CommandOptions) { - if(!options.process_result) - return promise; - - return promise.catch(ex => { - if(options.process_result) { - if(ex instanceof CommandResult) { - let res = ex; - if(!res.success) { - if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error - const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); - res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); - this.connection_handler.log.log(server.Type.ERROR_PERMISSION, { - permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) - }); - this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } else if(res.id != ErrorID.EMPTY_RESULT) { - this.connection_handler.log.log(server.Type.ERROR_CUSTOM, { - message: res.extra_message.length == 0 ? res.message : res.extra_message - }); + return promise.catch(ex => { + if(options.process_result) { + if(ex instanceof CommandResult) { + let res = ex; + if(!res.success) { + if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error + const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); + res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); + this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, { + permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) + }); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } else if(res.id != ErrorID.EMPTY_RESULT) { + this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, { + message: res.extra_message.length == 0 ? res.message : res.extra_message + }); + } } + } else if(typeof(ex) === "string") { + this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, {error: ex}); + } else { + log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); } - } else if(typeof(ex) === "string") { - this.connection_handler.log.log(server.Type.CONNECTION_COMMAND_ERROR, {error: ex}); - } else { - log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); } - } - return Promise.reject(ex); - }); - } - - handle_command(command: ServerCommand) : boolean { - if(this[command.command]) { - this[command.command](command.arguments); - return true; - } - - return false; - } - - set_handler(command: string, handler: any) { - this[command] = handler; - } - - unset_handler(command: string, handler?: any) { - if(handler && this[command] != handler) return; - this[command] = undefined; - } - - handleCommandResult(json) { - json = json[0]; //Only one bulk - - let code : string = json["return_code"]; - if(!code || code.length == 0) { - log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); - return; - } - let retListeners = this.connection["_retListener"]; - - for(let e of retListeners) { - if(e.code != code) continue; - retListeners.remove(e); - let result = new CommandResult(json); - if(result.success) - e.resolve(result); - else - e.reject(result); - break; - } - } - - handleCommandServerInit(json){ - //We could setup the voice channel - if(this.connection.support_voice()) { - log.debug(LogCategory.NETWORKING, tr("Setting up voice")); - } else { - log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); - } - - - json = json[0]; //Only one bulk - - this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); - this.connection.client.side_bar.channel_conversations().reset(); - this.connection.client.clientId = parseInt(json["aclid"]); - this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "aclid") continue; - if(key === "acn") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - - const properties = this.connection.client.channelTree.server.properties; - /* host message */ - if(properties.virtualserver_hostmessage_mode > 0) { - if(properties.virtualserver_hostmessage_mode == 1) { - /* show in log */ - this.connection_handler.log.log(server.Type.SERVER_HOST_MESSAGE, { - message: properties.virtualserver_hostmessage - }); - } else { - /* create modal/create modal and quit */ - createModal({ - header: tr("Host message"), - body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), - footer: undefined - }).open(); - - if(properties.virtualserver_hostmessage_mode == 3) { - /* first let the client initialize his stuff */ - setTimeout(() => { - this.connection_handler.log.log(server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { - message: properties.virtualserver_welcomemessage - }); - - this.connection.disconnect("host message disconnect"); - this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); - this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); - }, 100); - } - } - } - - /* welcome message */ - if(properties.virtualserver_welcomemessage) { - this.connection_handler.log.log(server.Type.SERVER_WELCOME_MESSAGE, { - message: properties.virtualserver_welcomemessage + return Promise.reject(ex); }); } - /* priviledge key */ - if(properties.virtualserver_ask_for_privilegekey) { - createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { - if(!result) return; - const scon = server_connections.active_connection_handler(); - - if(scon.serverConnection.connected) - scon.serverConnection.send_command("tokenuse", { - token: result - }).then(() => { - createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); - }).catch(error => { - createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); - }); - }, { field_placeholder: 'Enter Privilege Key' }).open(); - } - - this.connection_handler.log.log(server.Type.CONNECTION_CONNECTED, { - own_client: this.connection_handler.getClient().log_data() - }); - this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); - this.connection.client.onConnected(); - } - - handleNotifyServerConnectionInfo(json) { - json = json[0]; - - /* everything is a number, so lets parse it */ - for(const key of Object.keys(json)) - json[key] = parseFloat(json[key]); - - this.connection_handler.channelTree.server.set_connection_info(json); - } - - handleNotifyConnectionInfo(json) { - json = json[0]; - - const object = new ClientConnectionInfo(); - /* everything is a number (except ip), so lets parse it */ - for(const key of Object.keys(json)) { - if(key === "connection_client_ip") - object[key] = json[key]; - else - object[key] = parseFloat(json[key]); - } - - const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); - if(!client) { - log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); - return; - } - - client.set_connection_info(object); - } - - private createChannelFromJson(json, ignoreOrder: boolean = false) { - let tree = this.connection.client.channelTree; - - let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); - tree.insertChannel(channel); - if(json["channel_order"] !== "0") { - let prev = tree.findChannel(json["channel_order"]); - if(!prev && json["channel_order"] != 0) { - if(!ignoreOrder) { - log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); - return; - } + handle_command(command: ServerCommand) : boolean { + if(this[command.command]) { + this[command.command](command.arguments); + return true; } - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + return false; + } + + set_handler(command: string, handler: any) { + this[command] = handler; + } + + unset_handler(command: string, handler?: any) { + if(handler && this[command] != handler) return; + this[command] = undefined; + } + + handleCommandResult(json) { + json = json[0]; //Only one bulk + + let code : string = json["return_code"]; + if(!code || code.length == 0) { + log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); return; } - tree.moveChannel(channel, prev, parent); //TODO test if channel exists! - } - if(ignoreOrder) { - for(let ch of tree.channels) { - if(ch.properties.channel_order == channel.channelId) { - tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) - } + let retListeners = this.connection["_retListener"]; + + for(let e of retListeners) { + if(e.code != code) continue; + retListeners.remove(e); + let result = new CommandResult(json); + if(result.success) + e.resolve(result); + else + e.reject(result); + break; } } - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "cpid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - } - - handleCommandChannelList(json) { - this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ - log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); - for(let index = 0; index < json.length; index++) - this.createChannelFromJson(json[index], true); - } - - - handleCommandChannelListFinished(json) { - this.connection.client.channelTree.show_channel_tree(); - } - - handleCommandChannelCreate(json) { - this.createChannelFromJson(json[0]); - } - - handleCommandChannelShow(json) { - this.createChannelFromJson(json[0]); //TODO may chat? - } - - handleCommandChannelDelete(json) { - let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); - } - } - - handleCommandChannelHide(json) { - let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); - } - } - - handleCommandClientEnterView(json) { - let tree = this.connection.client.channelTree; - - let client: ClientEntry; - let channel = undefined; - let old_channel = undefined; - let reason_id, reason_msg; - - let invokerid, invokername, invokeruid; - - for(const entry of json) { - /* attempt to update properties if given */ - channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; - old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; - reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; - reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; - - invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; - invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; - invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; - - client = tree.findClient(parseInt(entry["clid"])); - - if(!client) { - if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { - client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } else { - client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } - - client.properties.client_type = parseInt(entry["client_type"]); - client = tree.insertClient(client, channel); + handleCommandServerInit(json){ + //We could setup the voice channel + if(this.connection.support_voice()) { + log.debug(LogCategory.NETWORKING, tr("Setting up voice")); } else { - tree.moveClient(client, channel); + log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); } - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(server.Type.CLIENT_VIEW_ENTER, { - channel_from: old_channel ? old_channel.log_data() : undefined, - channel_to: channel ? channel.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(invokeruid, invokerid, invokername), - message:reason_msg, - reason: parseInt(reason_id), - own_channel: channel == own_channel - }); - if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - if(own_channel == channel) - if(old_channel) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else - this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); - } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { + json = json[0]; //Only one bulk + this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); + this.connection.client.side_bar.channel_conversations().reset(); + this.connection.client.clientId = parseInt(json["aclid"]); + this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "aclid") continue; + if(key === "acn") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + + const properties = this.connection.client.channelTree.server.properties; + /* host message */ + if(properties.virtualserver_hostmessage_mode > 0) { + if(properties.virtualserver_hostmessage_mode == 1) { + /* show in log */ + this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, { + message: properties.virtualserver_hostmessage + }); } else { - console.warn(tr("Unknown reasonid for %o"), reason_id); + /* create modal/create modal and quit */ + createModal({ + header: tr("Host message"), + body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), + footer: undefined + }).open(); + + if(properties.virtualserver_hostmessage_mode == 3) { + /* first let the client initialize his stuff */ + setTimeout(() => { + this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { + message: properties.virtualserver_welcomemessage + }); + + this.connection.disconnect("host message disconnect"); + this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); + this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); + }, 100); + } + } + } + + /* welcome message */ + if(properties.virtualserver_welcomemessage) { + this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, { + message: properties.virtualserver_welcomemessage + }); + } + + /* priviledge key */ + if(properties.virtualserver_ask_for_privilegekey) { + createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { + if(!result) return; + const scon = server_connections.active_connection_handler(); + + if(scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); + }).catch(error => { + createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); + }); + }, { field_placeholder: 'Enter Privilege Key' }).open(); + } + + this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, { + own_client: this.connection_handler.getClient().log_data() + }); + this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); + this.connection.client.onConnected(); + } + + handleNotifyServerConnectionInfo(json) { + json = json[0]; + + /* everything is a number, so lets parse it */ + for(const key of Object.keys(json)) + json[key] = parseFloat(json[key]); + + this.connection_handler.channelTree.server.set_connection_info(json); + } + + handleNotifyConnectionInfo(json) { + json = json[0]; + + const object = new ClientConnectionInfo(); + /* everything is a number (except ip), so lets parse it */ + for(const key of Object.keys(json)) { + if(key === "connection_client_ip") + object[key] = json[key]; + else + object[key] = parseFloat(json[key]); + } + + const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); + if(!client) { + log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); + return; + } + + client.set_connection_info(object); + } + + private createChannelFromJson(json, ignoreOrder: boolean = false) { + let tree = this.connection.client.channelTree; + + let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); + tree.insertChannel(channel); + if(json["channel_order"] !== "0") { + let prev = tree.findChannel(json["channel_order"]); + if(!prev && json["channel_order"] != 0) { + if(!ignoreOrder) { + log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); + return; + } + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + return; + } + tree.moveChannel(channel, prev, parent); //TODO test if channel exists! + } + if(ignoreOrder) { + for(let ch of tree.channels) { + if(ch.properties.channel_order == channel.channelId) { + tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) + } } } @@ -468,113 +311,159 @@ export class ConnectionCommandHandler extends AbstractCommandHandler { key: string, value: string }[] = []; - - for(let key in entry) { - if(key == "cfid") continue; - if(key == "ctid") continue; + for(let key in json) { + if(key === "cid") continue; + if(key === "cpid") continue; if(key === "invokerid") continue; if(key === "invokername") continue; if(key === "invokeruid") continue; if(key === "reasonid") continue; - updates.push({key: key, value: entry[key]}); + updates.push({key: key, value: json[key]}); } + channel.updateVariables(...updates); + } - client.updateVariables(...updates); + handleCommandChannelList(json) { + this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ + log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); + for(let index = 0; index < json.length; index++) + this.createChannelFromJson(json[index], true); + } - /* if its a new client join, or a system reason (like we joined) */ - if(!old_channel || reason_id == 2) { - /* client new join */ - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - unique_id: client.properties.client_unique_identifier, - client_id: client.clientId(), - name: client.clientNickName() - }, { - create: false, - attach: true - }); - if(conversation) - client.flag_text_unread = conversation.is_unread(); - } - if(client instanceof LocalClientEntry) { - client.initializeListener(); - this.connection_handler.update_voice_status(); - this.connection_handler.side_bar.info_frame().update_channel_talk(); - const conversations = this.connection.client.side_bar.channel_conversations(); - conversations.set_current_channel(client.currentChannel().channelId); + handleCommandChannelListFinished(json) { + this.connection.client.channelTree.show_channel_tree(); + } + + handleCommandChannelCreate(json) { + this.createChannelFromJson(json[0]); + } + + handleCommandChannelShow(json) { + this.createChannelFromJson(json[0]); //TODO may chat? + } + + handleCommandChannelDelete(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); } } - } - handleCommandClientLeftView(json) { - let reason_id = -1; - - for(const entry of json) { - reason_id = entry["reasonid"] || reason_id; + handleCommandChannelHide(json) { let tree = this.connection.client.channelTree; - let client = tree.findClient(entry["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client left!")); - return 0; - } - if(client == this.connection.client.getClient()) { - if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else { - this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); + continue; } - this.connection_handler.side_bar.info_frame().update_channel_talk(); - return; + tree.deleteChannel(channel); } + } + handleCommandClientEnterView(json) { + let tree = this.connection.client.channelTree; - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - let channel_from = tree.findChannel(entry["cfid"]); - let channel_to = tree.findChannel(entry["ctid"]); + let client: ClientEntry; + let channel = undefined; + let old_channel = undefined; + let reason_id, reason_msg; - const is_own_channel = channel_from == own_channel; - this.connection_handler.log.log(server.Type.CLIENT_VIEW_LEAVE, { - channel_from: channel_from ? channel_from.log_data() : undefined, - channel_to: channel_to ? channel_to.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), - message: entry["reasonmsg"], - reason: parseInt(entry["reasonid"]), - ban_time: parseInt(entry["bantime"]), - own_channel: is_own_channel - }); + let invokerid, invokername, invokeruid; - if(is_own_channel) { - if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { - this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); - } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); - } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { - this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + for(const entry of json) { + /* attempt to update properties if given */ + channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; + old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; + reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; + reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; + + invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; + invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; + invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; + + client = tree.findClient(parseInt(entry["clid"])); + + if(!client) { + if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { + client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); } else { - log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); + client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } + + client.properties.client_type = parseInt(entry["client_type"]); + client = tree.insertClient(client, channel); + } else { + tree.moveClient(client, channel); + } + + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, { + channel_from: old_channel ? old_channel.log_data() : undefined, + channel_to: channel ? channel.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(invokeruid, invokerid, invokername), + message:reason_msg, + reason: parseInt(reason_id), + own_channel: channel == own_channel + }); + + if(reason_id == ViewReasonId.VREASON_USER_ACTION) { + if(own_channel == channel) + if(old_channel) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else + this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { + + } else { + console.warn(tr("Unknown reasonid for %o"), reason_id); } } - if(!channel_to) { - /* client left the server */ + let updates: { + key: string, + value: string + }[] = []; + + for(let key in entry) { + if(key == "cfid") continue; + if(key == "ctid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: entry[key]}); + } + + client.updateVariables(...updates); + + /* if its a new client join, or a system reason (like we joined) */ + if(!old_channel || reason_id == 2) { + /* client new join */ const conversation_manager = this.connection_handler.side_bar.private_conversations(); const conversation = conversation_manager.find_conversation({ unique_id: client.properties.client_unique_identifier, @@ -582,599 +471,690 @@ export class ConnectionCommandHandler extends AbstractCommandHandler { name: client.clientNickName() }, { create: false, - attach: false + attach: true }); - if(conversation) { - conversation.set_state(pchat.PrivateConversationState.DISCONNECTED); + if(conversation) + client.flag_text_unread = conversation.is_unread(); + } + + if(client instanceof LocalClientEntry) { + client.initializeListener(); + this.connection_handler.update_voice_status(); + this.connection_handler.side_bar.info_frame().update_channel_talk(); + const conversations = this.connection.client.side_bar.channel_conversations(); + conversations.set_current_channel(client.currentChannel().channelId); + } + } + } + + handleCommandClientLeftView(json) { + let reason_id = -1; + + for(const entry of json) { + reason_id = entry["reasonid"] || reason_id; + let tree = this.connection.client.channelTree; + let client = tree.findClient(entry["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client left!")); + return 0; + } + if(client == this.connection.client.getClient()) { + if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } else { + this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); + } + this.connection_handler.side_bar.info_frame().update_channel_talk(); + return; + } + + + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + let channel_from = tree.findChannel(entry["cfid"]); + let channel_to = tree.findChannel(entry["ctid"]); + + const is_own_channel = channel_from == own_channel; + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, { + channel_from: channel_from ? channel_from.log_data() : undefined, + channel_to: channel_to ? channel_to.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), + message: entry["reasonmsg"], + reason: parseInt(entry["reasonid"]), + ban_time: parseInt(entry["bantime"]), + own_channel: is_own_channel + }); + + if(is_own_channel) { + if(reason_id == ViewReasonId.VREASON_USER_ACTION) { + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { + this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); + } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); + } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { + this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } else { + log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); + } + } + + if(!channel_to) { + /* client left the server */ + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + unique_id: client.properties.client_unique_identifier, + client_id: client.clientId(), + name: client.clientNickName() + }, { + create: false, + attach: false + }); + if(conversation) { + conversation.set_state(chat.PrivateConversationState.DISCONNECTED); + } } } + + tree.deleteClient(client); + } + } + + handleNotifyClientMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + let self = client instanceof LocalClientEntry; + + let channel_to = tree.findChannel(parseInt(json["ctid"])); + let channel_from = tree.findChannel(parseInt(json["cfid"])); + + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); + return 0; } - tree.deleteClient(client); - } - } - - handleNotifyClientMoved(json) { - json = json[0]; //Only one bulk - let tree = this.connection.client.channelTree; - let client = tree.findClient(json["clid"]); - let self = client instanceof LocalClientEntry; - - let channel_to = tree.findChannel(parseInt(json["ctid"])); - let channel_from = tree.findChannel(parseInt(json["cfid"])); - - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); - return 0; - } - - if(!channel_to) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); - return 0; - } - - if(!self) { - if(!channel_from) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); - channel_from = client.currentChannel(); - } else if(channel_from != client.currentChannel()) { - log.error(LogCategory.NETWORKING, - tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), - client.currentChannel().channelId, channel_from.channelId - ); + if(!channel_to) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); + return 0; } - } else { - channel_from = client.currentChannel(); - } - tree.moveClient(client, channel_to); - - if(self) { - this.connection_handler.update_voice_status(channel_to); - - for(const entry of client.channelTree.clientsByChannel(channel_from)) { - if(entry !== client && entry.get_audio_handle()) { - entry.get_audio_handle().abort_replay(); - entry.speaking = false; + if(!self) { + if(!channel_from) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); + channel_from = client.currentChannel(); + } else if(channel_from != client.currentChannel()) { + log.error(LogCategory.NETWORKING, + tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), + client.currentChannel().channelId, channel_from.channelId + ); } + } else { + channel_from = client.currentChannel(); } - const side_bar = this.connection_handler.side_bar; - side_bar.info_frame().update_channel_talk(); + tree.moveClient(client, channel_to); - const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); - if(conversation_to) - conversation_to.update_private_state(); - - if(channel_from) { - const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); - if(conversation_from) - conversation_from.update_private_state(); - } - - side_bar.channel_conversations().update_chat_box(); - } - - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(server.Type.CLIENT_VIEW_MOVE, { - channel_from: channel_from ? { - channel_id: channel_from.channelId, - channel_name: channel_from.channelName() - } : undefined, - channel_from_own: channel_from == own_channel, - - channel_to: channel_to ? { - channel_id: channel_to.channelId, - channel_name: channel_to.channelName() - } : undefined, - channel_to_own: channel_to == own_channel, - - client: { - client_id: client.clientId(), - client_name: client.clientNickName(), - client_unique_id: client.properties.client_unique_identifier - }, - client_own: self, - - invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), - - message: json["reasonmsg"], - reason: parseInt(json["reasonid"]), - }); - if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { - if(self) - this.connection_handler.sound.play(Sound.USER_MOVED_SELF); - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); - } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - if(self) {} //If we do an action we wait for the error response - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { if(self) { - this.connection_handler.sound.play(Sound.CHANNEL_KICKED); - } else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else { - console.warn(tr("Unknown reason id %o"), json["reasonid"]); - } - } + this.connection_handler.update_voice_status(channel_to); - handleNotifyChannelMoved(json) { - json = json[0]; //Only one bulk + for(const entry of client.channelTree.clientsByChannel(channel_from)) { + if(entry !== client && entry.get_audio_handle()) { + entry.get_audio_handle().abort_replay(); + entry.speaking = false; + } + } - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); - return 0; - } + const side_bar = this.connection_handler.side_bar; + side_bar.info_frame().update_channel_talk(); - let prev = tree.findChannel(json["order"]); - if(!prev && json["order"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); - return 0; - } + const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); + if(conversation_to) + conversation_to.update_private_state(); - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); - return 0; - } + if(channel_from) { + const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); + if(conversation_from) + conversation_from.update_private_state(); + } - tree.moveChannel(channel, prev, parent); - } - - handleNotifyChannelEdited(json) { - json = json[0]; //Only one bulk - - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); - return 0; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - - if(this.connection_handler.getClient().currentChannel() === channel) { - //TODO: Playback sound that your channel has been edited - this.connection_handler.update_voice_status(); - } - } - - handleNotifyTextMessage(json) { - json = json[0]; //Only one bulk - - let mode = json["targetmode"]; - if(mode == 1){ - //json["invokerid"], json["invokername"], json["invokeruid"] - const target_client_id = parseInt(json["target"]); - const target_own = target_client_id === this.connection.client.getClientId(); - - if(target_own && target_client_id === json["invokerid"]) { - log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o"), json); - return; + side_bar.channel_conversations().update_chat_box(); } + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, { + channel_from: channel_from ? { + channel_id: channel_from.channelId, + channel_name: channel_from.channelName() + } : undefined, + channel_from_own: channel_from == own_channel, + + channel_to: channel_to ? { + channel_id: channel_to.channelId, + channel_name: channel_to.channelName() + } : undefined, + channel_to_own: channel_to == own_channel, + + client: { + client_id: client.clientId(), + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier + }, + client_own: self, + + invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), + + message: json["reasonmsg"], + reason: parseInt(json["reasonid"]), + }); + if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { + if(self) + this.connection_handler.sound.play(Sound.USER_MOVED_SELF); + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + if(self) {} //If we do an action we wait for the error response + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + if(self) { + this.connection_handler.sound.play(Sound.CHANNEL_KICKED); + } else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else { + console.warn(tr("Unknown reason id %o"), json["reasonid"]); + } + } + + handleNotifyChannelMoved(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); + return 0; + } + + let prev = tree.findChannel(json["order"]); + if(!prev && json["order"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); + return 0; + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); + return 0; + } + + tree.moveChannel(channel, prev, parent); + } + + handleNotifyChannelEdited(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); + return 0; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + + if(this.connection_handler.getClient().currentChannel() === channel) { + //TODO: Playback sound that your channel has been edited + this.connection_handler.update_voice_status(); + } + } + + handleNotifyTextMessage(json) { + json = json[0]; //Only one bulk + + let mode = json["targetmode"]; + if(mode == 1){ + //json["invokerid"], json["invokername"], json["invokeruid"] + const target_client_id = parseInt(json["target"]); + const target_own = target_client_id === this.connection.client.getClientId(); + + if(target_own && target_client_id === json["invokerid"]) { + log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json)); + return; + } + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, + unique_id: target_own ? json["invokeruid"] : undefined, + name: target_own ? json["invokername"] : undefined + }, { + create: target_own, + attach: target_own + }); + if(!conversation) { + log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); + return; + } + + conversation.append_message(json["msg"], { + type: target_own ? "partner" : "self", + name: json["invokername"], + unique_id: json["invokeruid"], + client_id: parseInt(json["invokerid"]) + }); + + if(target_own) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + if(client) /* the client itself might be invisible */ + client.flag_text_unread = conversation.is_unread(); + } else { + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + } + } else if(mode == 2) { + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const own_channel_id = this.connection.client.getClient().currentChannel().channelId; + const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; + const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); + + if(json["invokerid"] == this.connection.client.clientId) + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + else if(channel_id == own_channel_id) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + } + + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(channel_id); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + if(conversation.is_unread() && channel) + channel.flag_text_unread = true; + } else if(mode == 3) { + this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, { + message: json["msg"], + sender: { + client_unique_id: json["invokeruid"], + client_name: json["invokername"], + client_id: parseInt(json["invokerid"]) + } + }); + + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(0); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); + } + } + + notifyClientChatComposing(json) { + json = json[0]; + const conversation_manager = this.connection_handler.side_bar.private_conversations(); const conversation = conversation_manager.find_conversation({ - client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, - unique_id: target_own ? json["invokeruid"] : undefined, - name: target_own ? json["invokername"] : undefined + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined }, { - create: target_own, - attach: target_own + create: false, + attach: false + }); + if(!conversation) + return; + + conversation.trigger_typing(); + } + + handleNotifyClientChatClosed(json) { + json = json[0]; //Only one bulk + + //Chat partner has closed the conversation + + //clid: "6" + //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false }); if(!conversation) { - log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); + log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); + return; + } + conversation.set_state(chat.PrivateConversationState.CLOSED); + } + + handleNotifyClientUpdated(json) { + json = json[0]; //Only one bulk + + let client = this.connection.client.channelTree.findClient(json["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); return; } - conversation.append_message(json["msg"], { - type: target_own ? "partner" : "self", + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key == "clid") continue; + updates.push({key: key, value: json[key]}); + } + client.updateVariables(...updates); + } + + handleNotifyServerEdited(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + } + + handleNotifyServerUpdated(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(true, ...updates); + } + + handleNotifyMusicPlayerInfo(json) { + json = json[0]; + + let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); + if(!bot || !(bot instanceof MusicClientEntry)) { + log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); + return; + } + + bot.handlePlayerInfo(json); + } + + handleNotifyClientPoke(json) { + json = json[0]; + Modals.spawnPoke(this.connection_handler, { + id: parseInt(json["invokerid"]), name: json["invokername"], - unique_id: json["invokeruid"], - client_id: parseInt(json["invokerid"]) - }); + unique_id: json["invokeruid"] + }, json["msg"]); - if(target_own) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - if(client) /* the client itself might be invisible */ - client.flag_text_unread = conversation.is_unread(); + this.connection_handler.sound.play(Sound.USER_POKED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientAdd(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) + this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientRemove(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); } else { - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); } - } else if(mode == 2) { - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const own_channel_id = this.connection.client.getClient().currentChannel().channelId; - const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; - const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); + } - if(json["invokerid"] == this.connection.client.clientId) - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - else if(channel_id == own_channel_id) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + //TODO server chat message + handleNotifyClientChannelGroupChanged(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); + } + } + + handleNotifyChannelSubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = true; + } + } + + handleNotifyChannelUnsubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = false; + for(const client of channel.clients(false)) + this.connection.client.channelTree.deleteClient(client); + } + } + + handleNotifyConversationHistory(json: any[]) { + const conversations = this.connection.client.side_bar.channel_conversations(); + const conversation = conversations.conversation(parseInt(json[0]["cid"])); + if(!conversation) { + log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); + return; } - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(channel_id); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], + for(const entry of json) { + conversation.register_new_message({ + message: entry["msg"], + sender_unique_id: entry["sender_unique_id"], + sender_name: entry["sender_name"], + timestamp: parseInt(entry["timestamp"]), + sender_database_id: parseInt(entry["sender_database_id"]) + }, false); + } - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] + /* now update the boxes */ + /* No update needed because the command which triggers this notify should update the chat box on success + conversation.fix_scroll(true); + conversation.handle.update_chat_box(); + */ + } + + handleNotifyConversationMessageDelete(json: any[]) { + let conversation: Conversation; + const conversations = this.connection.client.side_bar.channel_conversations(); + for(const entry of json) { + if(typeof(entry["cid"]) !== "undefined") + conversation = conversations.conversation(parseInt(entry["cid"]), false); + if(!conversation) + continue; + + conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); + } + } + + handleNotifyMusicStatusUpdate(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + client.events.fire("music_status_update", { + player_replay_index: parseInt(json["player_replay_index"]), + player_buffered_index: parseInt(json["player_buffered_index"]) }); - if(conversation.is_unread() && channel) - channel.flag_text_unread = true; - } else if(mode == 3) { - this.connection_handler.log.log(server.Type.GLOBAL_MESSAGE, { - message: json["msg"], - sender: { - client_unique_id: json["invokeruid"], - client_name: json["invokername"], - client_id: parseInt(json["invokerid"]) + } + + handleMusicPlayerSongChange(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + const song_id = parseInt(json["song_id"]); + let song: SongInfo; + if(song_id) { + song = new SongInfo(); + JSON.map_to(song, json); + } + + client.events.fire("music_song_change", { + song: song + }); + } + + handleNotifyPlaylistSongAdd(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + client.events.fire("playlist_song_add", { + song: { + song_id: parseInt(json["song_id"]), + song_invoker: json["song_invoker"], + song_previous_song_id: parseInt(json["song_previous_song_id"]), + song_url: json["song_url"], + song_url_loader: json["song_url_loader"], + + song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", + song_metadata: json["song_metadata"] } }); + } - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(0); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], + handleNotifyPlaylistSongRemove(json: any[]) { + json = json[0]; - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_remove", { song_id: song_id }); + } + + handleNotifyPlaylistSongReorder(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + const previous_song_id = parseInt(json["song_previous_song_id"]); + client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); + } + + handleNotifyPlaylistSongLoaded(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_loaded", { + song_id: song_id, + success: json["success"] == 1, + error_msg: json["load_error_msg"], + metadata: json["song_metadata"] }); - this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); } } - - notifyClientChatComposing(json) { - json = json[0]; - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) - return; - - conversation.trigger_typing(); - } - - handleNotifyClientChatClosed(json) { - json = json[0]; //Only one bulk - - //Chat partner has closed the conversation - - //clid: "6" - //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) { - log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); - return; - } - conversation.set_state(pchat.PrivateConversationState.CLOSED); - } - - handleNotifyClientUpdated(json) { - json = json[0]; //Only one bulk - - let client = this.connection.client.channelTree.findClient(json["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); - return; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key == "clid") continue; - updates.push({key: key, value: json[key]}); - } - client.updateVariables(...updates); - } - - handleNotifyServerEdited(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - } - - handleNotifyServerUpdated(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(true, ...updates); - } - - handleNotifyMusicPlayerInfo(json) { - json = json[0]; - - let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); - if(!bot || !(bot instanceof MusicClientEntry)) { - log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); - return; - } - - bot.handlePlayerInfo(json); - } - - handleNotifyClientPoke(json) { - json = json[0]; - Modals.spawnPoke(this.connection_handler, { - id: parseInt(json["invokerid"]), - name: json["invokername"], - unique_id: json["invokeruid"] - }, json["msg"]); - - this.connection_handler.sound.play(Sound.USER_POKED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientAdd(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) - this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientRemove(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); - } else { - } - } - - //TODO server chat message - handleNotifyClientChannelGroupChanged(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); - } - } - - handleNotifyChannelSubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = true; - } - } - - handleNotifyChannelUnsubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = false; - for(const client of channel.clients(false)) - this.connection.client.channelTree.deleteClient(client); - } - } - - handleNotifyConversationHistory(json: any[]) { - const conversations = this.connection.client.side_bar.channel_conversations(); - const conversation = conversations.conversation(parseInt(json[0]["cid"])); - if(!conversation) { - log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); - return; - } - - for(const entry of json) { - conversation.register_new_message({ - message: entry["msg"], - sender_unique_id: entry["sender_unique_id"], - sender_name: entry["sender_name"], - timestamp: parseInt(entry["timestamp"]), - sender_database_id: parseInt(entry["sender_database_id"]) - }, false); - } - - /* now update the boxes */ - /* No update needed because the command which triggers this notify should update the chat box on success - conversation.fix_scroll(true); - conversation.handle.update_chat_box(); - */ - } - - handleNotifyConversationMessageDelete(json: any[]) { - let conversation: Conversation; - const conversations = this.connection.client.side_bar.channel_conversations(); - for(const entry of json) { - if(typeof(entry["cid"]) !== "undefined") - conversation = conversations.conversation(parseInt(entry["cid"]), false); - if(!conversation) - continue; - - conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); - } - } - - handleNotifyMusicStatusUpdate(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - client.events.fire("music_status_update", { - player_replay_index: parseInt(json["player_replay_index"]), - player_buffered_index: parseInt(json["player_buffered_index"]) - }); - } - - handleMusicPlayerSongChange(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - const song_id = parseInt(json["song_id"]); - let song: SongInfo; - if(song_id) { - song = new SongInfo(); - JSON.map_to(song, json); - } - - client.events.fire("music_song_change", { - song: song - }); - } - - handleNotifyPlaylistSongAdd(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - client.events.fire("playlist_song_add", { - song: { - song_id: parseInt(json["song_id"]), - song_invoker: json["song_invoker"], - song_previous_song_id: parseInt(json["song_previous_song_id"]), - song_url: json["song_url"], - song_url_loader: json["song_url_loader"], - - song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", - song_metadata: json["song_metadata"] - } - }); - } - - handleNotifyPlaylistSongRemove(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_remove", { song_id: song_id }); - } - - handleNotifyPlaylistSongReorder(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - const previous_song_id = parseInt(json["song_previous_song_id"]); - client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); - } - - handleNotifyPlaylistSongLoaded(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_loaded", { - song_id: song_id, - success: json["success"] == 1, - error_msg: json["load_error_msg"], - metadata: json["song_metadata"] - }); - } } \ No newline at end of file diff --git a/shared/js/connection/CommandHelper.ts b/shared/js/connection/CommandHelper.ts index 21203bb2..9eff210b 100644 --- a/shared/js/connection/CommandHelper.ts +++ b/shared/js/connection/CommandHelper.ts @@ -1,460 +1,448 @@ -import { - ClientNameInfo, - CommandResult, - ErrorID, - Playlist, PlaylistInfo, PlaylistSong, - QueryList, - QueryListEntry, ServerGroupClient -} from "./ServerConnectionDeclaration"; -import {ChannelEntry} from "../channel-tree/channel"; -import {ChatType} from "../ui/frames/chat"; -import {ClientEntry} from "../channel-tree/client"; -import {AbstractCommandHandler, ServerCommand, SingleCommandHandler} from "./ConnectionBase"; -import {log, LogCategory} from "../log"; +namespace connection { + export class CommandHelper extends AbstractCommandHandler { + private _who_am_i: any; + private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; + private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; -export class CommandHelper extends AbstractCommandHandler { - private _who_am_i: any; - private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; - private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; + constructor(connection) { + super(connection); - constructor(connection) { - super(connection); - - this.volatile_handler_boss = false; - this.ignore_consumed = true; - } - - initialize() { - this.connection.command_handler_boss().register_handler(this); - } - - destroy() { - if(this.connection) { - const hboss = this.connection.command_handler_boss(); - hboss && hboss.unregister_handler(this); - } - this._awaiters_unique_ids = undefined; - } - - handle_command(command: ServerCommand): boolean { - if(command.command == "notifyclientnamefromuid") - this.handle_notifyclientnamefromuid(command.arguments); - if(command.command == "notifyclientgetnamefromdbid") - this.handle_notifyclientgetnamefromdbid(command.arguments); - else - return false; - return true; - } - - joinChannel(channel: ChannelEntry, password?: string) : Promise { - return this.connection.send_command("clientmove", { - "clid": this.connection.client.getClientId(), - "cid": channel.getChannelId(), - "cpw": password || "" - }); - } - - sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { - if(type == ChatType.SERVER) - return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); - else if(type == ChatType.CHANNEL) - return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); - else if(type == ChatType.CLIENT) - return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); - } - - updateClient(key: string, value: string) : Promise { - let data = {}; - data[key] = value; - return this.connection.send_command("clientupdate", data); - } - - async info_from_uid(..._unique_ids: string[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_ids = new Set(_unique_ids); - if(!unique_ids.size) return []; - - const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; - - - for(const unique_id of unique_ids) { - request.push({'cluid': unique_id}); - (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) - .push(unique_id_resolvers[unique_id] = info => response.push(info)); + this.volatile_handler_boss = false; + this.ignore_consumed = true; } - try { - await this.connection.send_command("clientgetnamefromuid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; + initialize() { + this.connection.command_handler_boss().register_handler(this); + } + + destroy() { + if(this.connection) { + const hboss = this.connection.command_handler_boss(); + hboss && hboss.unregister_handler(this); } - } finally { - /* cleanup */ - for(const unique_id of Object.keys(unique_id_resolvers)) - (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); + this._awaiters_unique_ids = undefined; } - return response; - } - - private handle_notifyclientgetnamefromdbid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - - const functions = this._awaiters_unique_dbid[info.client_database_id] || []; - delete this._awaiters_unique_dbid[info.client_database_id]; - - for(const fn of functions) - fn(info); - } - } - - async info_from_cldbid(..._cldbid: number[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_cldbid = new Set(_cldbid); - if(!unique_cldbid.size) return []; - - const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; - - - for(const cldbid of unique_cldbid) { - request.push({'cldbid': cldbid}); - (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) - .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); + handle_command(command: connection.ServerCommand): boolean { + if(command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + if(command.command == "notifyclientgetnamefromdbid") + this.handle_notifyclientgetnamefromdbid(command.arguments); + else + return false; + return true; } - try { - await this.connection.send_command("clientgetnamefromdbid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; - } - } finally { - /* cleanup */ - for(const cldbid of Object.keys(unique_cldbid_resolvers)) - (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); + joinChannel(channel: ChannelEntry, password?: string) : Promise { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); } - return response; - } - - private handle_notifyclientnamefromuid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - - const functions = this._awaiters_unique_ids[entry["cluid"]] || []; - delete this._awaiters_unique_ids[entry["cluid"]]; - - for(const fn of functions) - fn(info); + sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { + if(type == ChatType.SERVER) + return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); + else if(type == ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); + else if(type == ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); } - } - - request_query_list(server_id: number = undefined) : Promise { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyquerylist", - function: command => { - const json = command.arguments; - - const result = {} as QueryList; - - result.flag_all = json[0]["flag_all"]; - result.flag_own = json[0]["flag_own"]; - result.queries = []; - - for(const entry of json) { - const rentry = {} as QueryListEntry; - rentry.bounded_server = parseInt(entry["client_bound_server"]); - rentry.username = entry["client_login_name"]; - rentry.unique_id = entry["client_unique_identifier"]; - - result.queries.push(rentry); - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); + updateClient(key: string, value: string) : Promise { let data = {}; - if(server_id !== undefined) - data["server_id"] = server_id; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } - this.connection.send_command("querylist", data).catch(error => { - this.handler_boss.remove_single_handler(single_handler); + async info_from_uid(..._unique_ids: string[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_ids = new Set(_unique_ids); + if(!unique_ids.size) return []; - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve(undefined); - return; - } - } - reject(error); - }); - }); - } + const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; - request_playlist_list() : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistlist", - function: command => { - const json = command.arguments; - const result: Playlist[] = []; - for(const entry of json) { - try { - result.push({ - playlist_id: parseInt(entry["playlist_id"]), - playlist_bot_id: parseInt(entry["playlist_bot_id"]), - playlist_title: entry["playlist_title"], - playlist_type: parseInt(entry["playlist_type"]), - playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), - playlist_owner_name: entry["playlist_owner_name"], + for(const unique_id of unique_ids) { + request.push({'cluid': unique_id}); + (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) + .push(unique_id_resolvers[unique_id] = info => response.push(info)); + } - needed_power_modify: parseInt(entry["needed_power_modify"]), - needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), - needed_power_delete: parseInt(entry["needed_power_delete"]), - needed_power_song_add: parseInt(entry["needed_power_song_add"]), - needed_power_song_move: parseInt(entry["needed_power_song_move"]), - needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistlist").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_songs(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistsonglist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); - return false; - } - - const result: PlaylistSong[] = []; - - for(const entry of json) { - try { - result.push({ - song_id: parseInt(entry["song_id"]), - song_invoker: entry["song_invoker"], - song_previous_song_id: parseInt(entry["song_previous_song_id"]), - song_url: entry["song_url"], - song_url_loader: entry["song_url_loader"], - - song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", - song_metadata: entry["song_metadata"] - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_client_list(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistclientlist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); - return false; - } - - const result: number[] = []; - - for(const entry of json) - result.push(parseInt(entry["cldbid"])); - - resolve(result.filter(e => !isNaN(e))); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); + try { + await this.connection.send_command("clientgetnamefromuid", request); + } catch(error) { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; + /* nothing */ + } else { + throw error; } - reject(error); - }) - }); - } + } finally { + /* cleanup */ + for(const unique_id of Object.keys(unique_id_resolvers)) + (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); + } - async request_clients_by_server_group(group_id: number) : Promise { - //servergroupclientlist sgid=2 - //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyservergroupclientlist", - function: command => { - if (command.arguments[0]["sgid"] != group_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); - return false; - } + return response; + } + + private handle_notifyclientgetnamefromdbid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_dbid[info.client_database_id] || []; + delete this._awaiters_unique_dbid[info.client_database_id]; + + for(const fn of functions) + fn(info); + } + } + + async info_from_cldbid(..._cldbid: number[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_cldbid = new Set(_cldbid); + if(!unique_cldbid.size) return []; + + const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; + + + for(const cldbid of unique_cldbid) { + request.push({'cldbid': cldbid}); + (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) + .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); + } + + try { + await this.connection.send_command("clientgetnamefromdbid", request); + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } else { + throw error; + } + } finally { + /* cleanup */ + for(const cldbid of Object.keys(unique_cldbid_resolvers)) + (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); + } + + return response; + } + + private handle_notifyclientnamefromuid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_ids[entry["cluid"]] || []; + delete this._awaiters_unique_ids[entry["cluid"]]; + + for(const fn of functions) + fn(info); + } + } + + request_query_list(server_id: number = undefined) : Promise { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + + const result = {} as QueryList; + + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + + for(const entry of json) { + const rentry = {} as QueryListEntry; + rentry.bounded_server = parseInt(entry["client_bound_server"]); + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + + result.queries.push(rentry); + } - try { - const result: ServerGroupClient[] = []; - for(const entry of command.arguments) - result.push({ - client_database_id: parseInt(entry["cldbid"]), - client_nickname: entry["client_nickname"], - client_unique_identifier: entry["client_unique_identifier"] - }); resolve(result); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); - reject("failed to parse info"); + return true; } + }; + this.handler_boss.register_single_handler(single_handler); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); + let data = {}; + if(server_id !== undefined) + data["server_id"] = server_id; - this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); - request_playlist_info(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistinfo", - function: command => { - const json = command.arguments[0]; - if (json["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } + } + reject(error); + }); + }); + } + + request_playlist_list() : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result: Playlist[] = []; + + for(const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], + + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); + } + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }) + }); + } + + request_playlist_songs(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; + + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); + return false; + } + + const result: PlaylistSong[] = []; + + for(const entry of json) { + try { + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], + + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); + } + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }) + }); + } + + request_playlist_client_list(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistclientlist", + function: command => { + const json = command.arguments; + + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); + return false; + } + + const result: number[] = []; + + for(const entry of json) + result.push(parseInt(entry["cldbid"])); + + resolve(result.filter(e => !isNaN(e))); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + resolve([]); return; } - - try { - //resolve - resolve({ - playlist_id: parseInt(json["playlist_id"]), - playlist_title: json["playlist_title"], - playlist_description: json["playlist_description"], - playlist_type: parseInt(json["playlist_type"]), - - playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), - playlist_owner_name: json["playlist_owner_name"], - - playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", - playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", - playlist_replay_mode: parseInt(json["playlist_replay_mode"]), - playlist_current_song_id: parseInt(json["playlist_current_song_id"]), - - playlist_max_songs: parseInt(json["playlist_max_songs"]) - }); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); - reject("failed to parse info"); - } - - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } - - /** - * @deprecated - * Its just a workaround for the query management. - * There is no garante that the whoami trick will work forever - */ - current_virtual_server_id() : Promise { - if(this._who_am_i) - return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); - - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - function: command => { - if(command.command != "" && command.command.indexOf("=") == -1) - return false; - - this._who_am_i = command.arguments[0]; - resolve(parseInt(this._who_am_i["virtualserver_id"])); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("whoami").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); + reject(error); + }) }); - }); + } + + async request_clients_by_server_group(group_id: number) : Promise { + //servergroupclientlist sgid=2 + //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyservergroupclientlist", + function: command => { + if (command.arguments[0]["sgid"] != group_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); + return false; + } + + try { + const result: ServerGroupClient[] = []; + for(const entry of command.arguments) + result.push({ + client_database_id: parseInt(entry["cldbid"]), + client_nickname: entry["client_nickname"], + client_unique_identifier: entry["client_unique_identifier"] + }); + resolve(result); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + request_playlist_info(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + return; + } + + try { + //resolve + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + + playlist_max_songs: parseInt(json["playlist_max_songs"]) + }); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() : Promise { + if(this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + function: command => { + if(command.command != "" && command.command.indexOf("=") == -1) + return false; + + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } } } \ No newline at end of file diff --git a/shared/js/connection/ConnectionBase.ts b/shared/js/connection/ConnectionBase.ts index 6e490b17..cf975fdb 100644 --- a/shared/js/connection/ConnectionBase.ts +++ b/shared/js/connection/ConnectionBase.ts @@ -1,222 +1,216 @@ -import {ConnectionHandler, ConnectionState} from "../ConnectionHandler"; -import {ServerAddress} from "../channel-tree/server"; -import {CommandResult} from "./ServerConnectionDeclaration"; -import {RecorderProfile} from "../voice/RecorderProfile"; -import {CommandHelper} from "./CommandHelper"; -import {connection} from "./HandshakeHandler"; -import HandshakeHandler = connection.HandshakeHandler; +namespace connection { + export interface CommandOptions { + flagset?: string[]; /* default: [] */ + process_result?: boolean; /* default: true */ -export interface CommandOptions { - flagset?: string[]; /* default: [] */ - process_result?: boolean; /* default: true */ - - timeout?: number /* default: 1000 */; -} -export const CommandOptionDefaults: CommandOptions = { - flagset: [], - process_result: true, - timeout: 1000 -}; - -export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; -export abstract class AbstractServerConnection { - readonly client: ConnectionHandler; - readonly command_helper: CommandHelper; - - protected constructor(client: ConnectionHandler) { - this.client = client; - - this.command_helper = new CommandHelper(this); + timeout?: number /* default: 1000 */; } - - /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ - abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; - - abstract connected() : boolean; - abstract disconnect(reason?: string) : Promise; - - abstract support_voice() : boolean; - abstract voice_connection() : voice.AbstractVoiceConnection | undefined; - - abstract command_handler_boss() : AbstractCommandHandlerBoss; - abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; - - abstract get onconnectionstatechanged() : ConnectionStateListener; - abstract set onconnectionstatechanged(listener: ConnectionStateListener); - - abstract remote_address() : ServerAddress; /* only valid when connected */ - abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ - - abstract ping() : { - native: number, - javascript?: number + export const CommandOptionDefaults: CommandOptions = { + flagset: [], + process_result: true, + timeout: 1000 }; -} -export namespace voice { - export enum PlayerState { - PREBUFFERING, - PLAYING, - BUFFERING, - STOPPING, - STOPPED + export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; + export abstract class AbstractServerConnection { + readonly client: ConnectionHandler; + readonly command_helper: CommandHelper; + + protected constructor(client: ConnectionHandler) { + this.client = client; + + this.command_helper = new CommandHelper(this); + } + + /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ + abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; + + abstract connected() : boolean; + abstract disconnect(reason?: string) : Promise; + + abstract support_voice() : boolean; + abstract voice_connection() : voice.AbstractVoiceConnection | undefined; + + abstract command_handler_boss() : AbstractCommandHandlerBoss; + abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + + abstract get onconnectionstatechanged() : ConnectionStateListener; + abstract set onconnectionstatechanged(listener: ConnectionStateListener); + + abstract remote_address() : ServerAddress; /* only valid when connected */ + abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ + + abstract ping() : { + native: number, + javascript?: number + }; } - export type LatencySettings = { - min_buffer: number; /* milliseconds */ - max_buffer: number; /* milliseconds */ + export namespace voice { + export enum PlayerState { + PREBUFFERING, + PLAYING, + BUFFERING, + STOPPING, + STOPPED + } + + export type LatencySettings = { + min_buffer: number; /* milliseconds */ + max_buffer: number; /* milliseconds */ + } + + export interface VoiceClient { + client_id: number; + + callback_playback: () => any; + callback_stopped: () => any; + + callback_state_changed: (new_state: PlayerState) => any; + + get_state() : PlayerState; + + get_volume() : number; + set_volume(volume: number) : void; + + abort_replay(); + + support_latency_settings() : boolean; + + reset_latency_settings(); + latency_settings(settings?: LatencySettings) : LatencySettings; + + support_flush() : boolean; + flush(); + } + + export abstract class AbstractVoiceConnection { + readonly connection: AbstractServerConnection; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + abstract connected() : boolean; + abstract encoding_supported(codec: number) : boolean; + abstract decoding_supported(codec: number) : boolean; + + abstract register_client(client_id: number) : VoiceClient; + abstract available_clients() : VoiceClient[]; + abstract unregister_client(client: VoiceClient) : Promise; + + abstract voice_recorder() : RecorderProfile; + abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; + + abstract get_encoder_codec() : number; + abstract set_encoder_codec(codec: number); + } } - export interface VoiceClient { - client_id: number; - - callback_playback: () => any; - callback_stopped: () => any; - - callback_state_changed: (new_state: PlayerState) => any; - - get_state() : PlayerState; - - get_volume() : number; - set_volume(volume: number) : void; - - abort_replay(); - - support_latency_settings() : boolean; - - reset_latency_settings(); - latency_settings(settings?: LatencySettings) : LatencySettings; - - support_flush() : boolean; - flush(); + export class ServerCommand { + command: string; + arguments: any[]; } - export abstract class AbstractVoiceConnection { + export abstract class AbstractCommandHandler { readonly connection: AbstractServerConnection; + handler_boss: AbstractCommandHandlerBoss | undefined; + volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ + + ignore_consumed: boolean = false; + protected constructor(connection: AbstractServerConnection) { this.connection = connection; } - abstract connected() : boolean; - abstract encoding_supported(codec: number) : boolean; - abstract decoding_supported(codec: number) : boolean; - - abstract register_client(client_id: number) : VoiceClient; - abstract available_clients() : VoiceClient[]; - abstract unregister_client(client: VoiceClient) : Promise; - - abstract voice_recorder() : RecorderProfile; - abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; - - abstract get_encoder_codec() : number; - abstract set_encoder_codec(codec: number); - } -} - -export class ServerCommand { - command: string; - arguments: any[]; -} - -export abstract class AbstractCommandHandler { - readonly connection: AbstractServerConnection; - - handler_boss: AbstractCommandHandlerBoss | undefined; - volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ - - ignore_consumed: boolean = false; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; + /** + * @return If the command should be consumed + */ + abstract handle_command(command: ServerCommand) : boolean; } - /** - * @return If the command should be consumed - */ - abstract handle_command(command: ServerCommand) : boolean; -} + export interface SingleCommandHandler { + name?: string; + command?: string; + timeout?: number; -export interface SingleCommandHandler { - name?: string; - command?: string; - timeout?: number; - - /* if the return is true then the command handler will be removed */ - function: (command: ServerCommand) => boolean; -} - -export abstract class AbstractCommandHandlerBoss { - readonly connection: AbstractServerConnection; - protected command_handlers: AbstractCommandHandler[] = []; - /* TODO: Timeout */ - protected single_command_handler: SingleCommandHandler[] = []; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; + /* if the return is true then the command handler will be removed */ + function: (command: ServerCommand) => boolean; } - destroy() { - this.command_handlers = undefined; - this.single_command_handler = undefined; - } + export abstract class AbstractCommandHandlerBoss { + readonly connection: AbstractServerConnection; + protected command_handlers: AbstractCommandHandler[] = []; + /* TODO: Timeout */ + protected single_command_handler: SingleCommandHandler[] = []; - register_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss) - throw "handler already registered"; - - this.command_handlers.remove(handler); /* just to be sure */ - this.command_handlers.push(handler); - handler.handler_boss = this; - } - - unregister_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss !== this) { - console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); - return; + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - this.command_handlers.remove(handler); - handler.handler_boss = undefined; - } + destroy() { + this.command_handlers = undefined; + this.single_command_handler = undefined; + } + register_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; - register_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.push(handler); - } + this.command_handlers.remove(handler); /* just to be sure */ + this.command_handlers.push(handler); + handler.handler_boss = this; + } - remove_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.remove(handler); - } - - handlers() : AbstractCommandHandler[] { - return this.command_handlers; - } - - invoke_handle(command: ServerCommand) : boolean { - let flag_consumed = false; - - for(const handler of this.command_handlers) { - try { - if(!flag_consumed || handler.ignore_consumed) - flag_consumed = flag_consumed || handler.handle_command(command); - } catch(error) { - console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); + unregister_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); + return; } + + this.command_handlers.remove(handler); + handler.handler_boss = undefined; } - for(const handler of [...this.single_command_handler]) { - if(handler.command && handler.command != command.command) - continue; - try { - if(handler.function(command)) - this.single_command_handler.remove(handler); - } catch(error) { - console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); + register_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.push(handler); + } + + remove_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.remove(handler); + } + + handlers() : AbstractCommandHandler[] { + return this.command_handlers; + } + + invoke_handle(command: ServerCommand) : boolean { + let flag_consumed = false; + + for(const handler of this.command_handlers) { + try { + if(!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } catch(error) { + console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); + } } - } - return flag_consumed; + for(const handler of [...this.single_command_handler]) { + if(handler.command && handler.command != command.command) + continue; + + try { + if(handler.function(command)) + this.single_command_handler.remove(handler); + } catch(error) { + console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); + } + } + + return flag_consumed; + } } } \ No newline at end of file diff --git a/shared/js/connection/HandshakeHandler.ts b/shared/js/connection/HandshakeHandler.ts index f4cb14df..de9c4910 100644 --- a/shared/js/connection/HandshakeHandler.ts +++ b/shared/js/connection/HandshakeHandler.ts @@ -1,15 +1,4 @@ -import {AbstractServerConnection} from "./ConnectionBase"; -import {ConnectParameters, DisconnectReason} from "../ConnectionHandler"; -import {profiles} from "../profiles/ConnectionProfile"; -import {profiles as iprofiles} from "../profiles/Identity"; -import {profiles as tiprofiles} from "../profiles/identities/TeamSpeakIdentity"; -import {native_client} from "../main"; -import {settings} from "../settings"; -import {CommandResult} from "./ServerConnectionDeclaration"; - -export namespace connection { - import identities = iprofiles.identities; - +namespace connection { export interface HandshakeIdentityHandler { connection: AbstractServerConnection; @@ -59,7 +48,7 @@ export namespace connection { on_teamspeak() { const type = this.profile.selected_type(); - if(type == identities.IdentitifyType.TEAMSPEAK) + if(type == profiles.identities.IdentitifyType.TEAMSPEAK) this.handshake_finished(); else { @@ -133,8 +122,8 @@ export namespace connection { } /* required to keep compatibility */ - if(this.profile.selected_type() === identities.IdentitifyType.TEAMSPEAK) { - data["client_key_offset"] = (this.profile.selected_identity() as tiprofiles.identities.TeaSpeakIdentity).hash_number; + if(this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) { + data["client_key_offset"] = (this.profile.selected_identity() as profiles.identities.TeaSpeakIdentity).hash_number; } this.connection.send_command("clientinit", data).catch(error => { diff --git a/shared/js/connection/ServerConnectionDeclaration.ts b/shared/js/connection/ServerConnectionDeclaration.ts index 1161c107..c2a71825 100644 --- a/shared/js/connection/ServerConnectionDeclaration.ts +++ b/shared/js/connection/ServerConnectionDeclaration.ts @@ -1,6 +1,4 @@ -import {LaterPromise} from "../utils/helpers"; - -export enum ErrorID { +enum ErrorID { NOT_IMPLEMENTED = 0x2, COMMAND_NOT_FOUND = 0x100, @@ -17,7 +15,7 @@ export enum ErrorID { CONVERSATION_IS_PRIVATE = 0x2202 } -export class CommandResult { +class CommandResult { success: boolean; id: number; message: string; @@ -37,39 +35,39 @@ export class CommandResult { } } -export interface ClientNameInfo { +interface ClientNameInfo { //cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9 client_unique_id: string; client_nickname: string; client_database_id: number; } -export interface ClientNameFromUid { +interface ClientNameFromUid { promise: LaterPromise, keys: string[], response: ClientNameInfo[] } -export interface ServerGroupClient { +interface ServerGroupClient { client_nickname: string; client_unique_identifier: string; client_database_id: number; } -export interface QueryListEntry { +interface QueryListEntry { username: string; unique_id: string; bounded_server: number; } -export interface QueryList { +interface QueryList { flag_own: boolean; flag_all: boolean; queries: QueryListEntry[]; } -export interface Playlist { +interface Playlist { playlist_id: number; playlist_bot_id: number; playlist_title: string; @@ -85,7 +83,7 @@ export interface Playlist { needed_power_song_remove: number; } -export interface PlaylistInfo { +interface PlaylistInfo { playlist_id: number, playlist_title: string, playlist_description: string, @@ -102,7 +100,7 @@ export interface PlaylistInfo { playlist_max_songs: number } -export interface PlaylistSong { +interface PlaylistSong { song_id: number; song_previous_song_id: number; song_invoker: string; diff --git a/shared/js/crypto/asn1.ts b/shared/js/crypto/asn1.ts index a9bf9b44..8184a4aa 100644 --- a/shared/js/crypto/asn1.ts +++ b/shared/js/crypto/asn1.ts @@ -14,7 +14,7 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -export namespace asn1 { +namespace asn1 { declare class Int10 { constructor(value?: any); diff --git a/shared/js/crypto/crc32.ts b/shared/js/crypto/crc32.ts index 52e14979..8122a8a2 100644 --- a/shared/js/crypto/crc32.ts +++ b/shared/js/crypto/crc32.ts @@ -1,4 +1,4 @@ -export class Crc32 { +class Crc32 { private static readonly lookup = [ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, diff --git a/shared/js/crypto/hex.ts b/shared/js/crypto/hex.ts index ca6e319e..9c700341 100644 --- a/shared/js/crypto/hex.ts +++ b/shared/js/crypto/hex.ts @@ -1,4 +1,4 @@ -export namespace hex { +namespace hex { export function encode(buffer) { let hexCodes = []; let view = new DataView(buffer); diff --git a/shared/js/crypto/sha.ts b/shared/js/crypto/sha.ts index d2057b5d..28998cff 100644 --- a/shared/js/crypto/sha.ts +++ b/shared/js/crypto/sha.ts @@ -10,7 +10,7 @@ interface Window { } */ -export namespace sha { +namespace sha { /* * [js-sha1]{@link https://github.com/emn178/js-sha1} * diff --git a/shared/js/crypto/uid.ts b/shared/js/crypto/uid.ts deleted file mode 100644 index 9ade46c7..00000000 --- a/shared/js/crypto/uid.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -} \ No newline at end of file diff --git a/shared/js/dns.ts b/shared/js/dns.ts index af3070e1..866b0daf 100644 --- a/shared/js/dns.ts +++ b/shared/js/dns.ts @@ -1,4 +1,4 @@ -export namespace dns { +namespace dns { export interface AddressTarget { target_ip: string; target_port?: number; diff --git a/shared/js/events.ts b/shared/js/events.ts index 9ce4d73f..348621a8 100644 --- a/shared/js/events.ts +++ b/shared/js/events.ts @@ -1,8 +1,4 @@ -import {guid} from "./crypto/uid"; -import {PlaylistSong} from "./connection/ServerConnectionDeclaration"; -import {MusicClientEntry, SongInfo} from "./channel-tree/client"; - -export namespace events { +namespace events { export interface EventConvert { as() : All[T]; } diff --git a/shared/js/i18n/country.ts b/shared/js/i18n/country.ts index 31c81dd8..ce152e30 100644 --- a/shared/js/i18n/country.ts +++ b/shared/js/i18n/country.ts @@ -1,4 +1,5 @@ -export namespace i18n { + +namespace i18n { interface CountryInfo { name: string; alpha_2: string; diff --git a/shared/js/i18n/localize.ts b/shared/js/i18n/localize.ts index 756930fe..1e469c39 100644 --- a/shared/js/i18n/localize.ts +++ b/shared/js/i18n/localize.ts @@ -1,10 +1,13 @@ -import {guid} from "../crypto/uid"; -import {log, LogCategory} from "../log"; -import {MessageHelper} from "../ui/frames/chat"; -import {StaticSettings} from "../settings"; -import {createErrorModal} from "../ui/elements/modal"; +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} -export namespace i18n { +namespace i18n { export interface TranslationKey { message: string; line?: number; diff --git a/shared/js/log.ts b/shared/js/log.ts index d900686a..4ea9d119 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -1,8 +1,6 @@ //Used by CertAccept popup -import {settings} from "./settings"; - -export enum LogCategory { +enum LogCategory { CHANNEL, CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */ CLIENT, @@ -21,7 +19,7 @@ export enum LogCategory { DNS } -export namespace log { +namespace log { export enum LogType { TRACE, DEBUG, diff --git a/shared/js/main.ts b/shared/js/main.ts index 354cc186..f2e910bc 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -1,18 +1,19 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// + import spawnYesNo = Modals.spawnYesNo; -import {ConnectionHandler} from "./ConnectionHandler"; -import {bipc} from "./BrowserIPC"; -import {log, LogCategory} from "./log"; -import {profiles} from "./profiles/ConnectionProfile"; -import {Modals} from "./ui/modal/ModalConnect"; -import {settings, Settings} from "./settings"; -import {i18n} from "./i18n/localize"; -import {createInfoModal} from "./ui/elements/modal"; -import {MessageHelper} from "./ui/frames/chat"; -export const js_render = window.jsrender || $; -export const native_client = window.require !== undefined; +const js_render = window.jsrender || $; +const native_client = window.require !== undefined; -export function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { +function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) return constraints => navigator.mediaDevices.getUserMedia(constraints); @@ -23,12 +24,11 @@ export function getUserMediaFunctionPromise() : (constraints: MediaStreamConstra return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); } -export interface Window { +interface Window { open_connected_question: () => Promise; } -export declare const nodeRequire: typeof require; -export function setup_close() { +function setup_close() { window.onbeforeunload = event => { if(profiles.requires_save()) profiles.save(); @@ -50,7 +50,7 @@ export function setup_close() { })); const exit = () => { - const {remote} = nodeRequire('electron'); + const {remote} = require('electron'); remote.getCurrentWindow().close(); }; @@ -80,8 +80,8 @@ export function setup_close() { }; } -export declare function moment(...arguments) : any; -export function setup_jsrender() : boolean { +declare function moment(...arguments) : any; +function setup_jsrender() : boolean { if(!js_render) { loader.critical_error("Missing jsrender extension!"); return false; @@ -115,7 +115,7 @@ export function setup_jsrender() : boolean { return true; } -export async function initialize() { +async function initialize() { Settings.initialize(); try { @@ -129,7 +129,7 @@ export async function initialize() { bipc.setup(); } -export async function initialize_app() { +async function initialize_app() { try { //Initialize main template const main = $("#tmpl_main").renderTag({ multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), @@ -180,7 +180,7 @@ export async function initialize_app() { setup_close(); } -export function str2ab8(str) { +function str2ab8(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { @@ -190,7 +190,7 @@ export function str2ab8(str) { } /* FIXME Dont use atob, because it sucks for non UTF-8 tings */ -export function arrayBufferBase64(base64: string) { +function arrayBufferBase64(base64: string) { base64 = atob(base64); const buf = new ArrayBuffer(base64.length); const bufView = new Uint8Array(buf); @@ -200,7 +200,7 @@ export function arrayBufferBase64(base64: string) { return buf; } -export function base64_encode_ab(source: ArrayBufferLike) { +function base64_encode_ab(source: ArrayBufferLike) { const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let base64 = ""; diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index 55e70ad0..c781f468 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -1,24 +1,17 @@ /// -import {LaterPromise} from "../utils/helpers"; -import {PermissionManager, PermissionValue} from "./PermissionManager"; -import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase"; -import {ConnectionHandler} from "../ConnectionHandler"; -import {log, LogCategory} from "../log"; -import {CommandResult} from "../connection/ServerConnectionDeclaration"; - -export enum GroupType { +enum GroupType { QUERY, TEMPLATE, NORMAL } -export enum GroupTarget { +enum GroupTarget { SERVER, CHANNEL } -export class GroupProperties { +class GroupProperties { iconid: number = 0; sortid: number = 0; @@ -26,12 +19,12 @@ export class GroupProperties { namemode: number = 0; } -export class GroupPermissionRequest { +class GroupPermissionRequest { group_id: number; promise: LaterPromise; } -export class Group { +class Group { properties: GroupProperties = new GroupProperties(); readonly handle: GroupManager; @@ -70,7 +63,7 @@ export class Group { } } -export class GroupManager extends AbstractCommandHandler { +class GroupManager extends connection.AbstractCommandHandler { readonly handle: ConnectionHandler; serverGroups: Group[] = []; @@ -90,7 +83,7 @@ export class GroupManager extends AbstractCommandHandler { this.channelGroups = undefined; } - handle_command(command: ServerCommand): boolean { + handle_command(command: connection.ServerCommand): boolean { switch (command.command) { case "notifyservergrouplist": case "notifychannelgrouplist": diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 960ee70c..9b558d1c 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -2,14 +2,7 @@ /// /// -import {ConnectionHandler} from "../ConnectionHandler"; -import {log, LogCategory} from "../log"; -import {LaterPromise} from "../utils/helpers"; -import {AbstractCommandHandler, ServerCommand} from "../connection/ConnectionBase"; -import LogType = log.LogType; -import {CommandResult, ErrorID} from "../connection/ServerConnectionDeclaration"; - -export enum PermissionType { +enum PermissionType { B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view", B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view", @@ -359,7 +352,7 @@ export enum PermissionType { I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client" } -export class PermissionInfo { +class PermissionInfo { name: string; id: number; description: string; @@ -370,21 +363,21 @@ export class PermissionInfo { } } -export class PermissionGroup { +class PermissionGroup { begin: number; end: number; deep: number; name: string; } -export class GroupedPermissions { +class GroupedPermissions { group: PermissionGroup; permissions: PermissionInfo[]; children: GroupedPermissions[]; parent: GroupedPermissions; } -export class PermissionValue { +class PermissionValue { readonly type: PermissionInfo; value: number; flag_skip: boolean; @@ -418,13 +411,13 @@ export class PermissionValue { } } -export class NeededPermissionValue extends PermissionValue { +class NeededPermissionValue extends PermissionValue { constructor(type, value) { super(type, value); } } -export namespace permissions { +namespace permissions { export type PermissionRequestKeys = { client_id?: number; channel_id?: number; @@ -480,13 +473,13 @@ export namespace permissions { } } -export type RequestLists = +type RequestLists = "requests_channel_permissions" | "requests_client_permissions" | "requests_client_channel_permissions" | "requests_playlist_permissions" | "requests_playlist_client_permissions"; -export class PermissionManager extends AbstractCommandHandler { +class PermissionManager extends connection.AbstractCommandHandler { readonly handle: ConnectionHandler; permissionList: PermissionInfo[] = []; @@ -610,7 +603,7 @@ export class PermissionManager extends AbstractCommandHandler { this._cacheNeededPermissions = undefined; } - handle_command(command: ServerCommand): boolean { + handle_command(command: connection.ServerCommand): boolean { switch (command.command) { case "notifyclientneededpermissions": this.onNeededPermissions(command.arguments); diff --git a/shared/js/profiles/ConnectionProfile.ts b/shared/js/profiles/ConnectionProfile.ts index f46266b3..f30cbe54 100644 --- a/shared/js/profiles/ConnectionProfile.ts +++ b/shared/js/profiles/ConnectionProfile.ts @@ -1,260 +1,251 @@ -import {MessageHelper} from "../ui/frames/chat"; -import {createErrorModal} from "../ui/elements/modal"; -import {guid} from "../crypto/uid"; -import {decode_identity, IdentitifyType, Identity} from "./Identity"; -import {static_forum_identity} from "./identities/TeaForumIdentity"; -import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity"; -import {AbstractServerConnection} from "../connection/ConnectionBase"; -import {connection} from "../connection/HandshakeHandler"; +namespace profiles { + export class ConnectionProfile { + id: string; -import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; + profile_name: string; + default_username: string; + default_password: string; -export class ConnectionProfile { - id: string; + selected_identity_type: string = "unset"; + identities: { [key: string]: identities.Identity } = {}; - profile_name: string; - default_username: string; - default_password: string; - - selected_identity_type: string = "unset"; - identities: { [key: string]: Identity } = {}; - - constructor(id: string) { - this.id = id; - } - - connect_username(): string { - if (this.default_username && this.default_username !== "Another TeaSpeak user") - return this.default_username; - - let selected = this.selected_identity(); - let name = selected ? selected.fallback_name() : undefined; - return name || "Another TeaSpeak user"; - } - - selected_identity(current_type?: IdentitifyType): Identity { - if (!current_type) - current_type = this.selected_type(); - - if (current_type === undefined) - return undefined; - - if (current_type == IdentitifyType.TEAFORO) { - return static_forum_identity(); - } else if (current_type == IdentitifyType.TEAMSPEAK || current_type == IdentitifyType.NICKNAME) { - return this.identities[IdentitifyType[current_type].toLowerCase()]; + constructor(id: string) { + this.id = id; } + connect_username(): string { + if (this.default_username && this.default_username !== "Another TeaSpeak user") + return this.default_username; + + let selected = this.selected_identity(); + let name = selected ? selected.fallback_name() : undefined; + return name || "Another TeaSpeak user"; + } + + selected_identity(current_type?: identities.IdentitifyType): identities.Identity { + if (!current_type) + current_type = this.selected_type(); + + if (current_type === undefined) + return undefined; + + if (current_type == identities.IdentitifyType.TEAFORO) { + return identities.static_forum_identity(); + } else if (current_type == identities.IdentitifyType.TEAMSPEAK || current_type == identities.IdentitifyType.NICKNAME) { + return this.identities[identities.IdentitifyType[current_type].toLowerCase()]; + } + + return undefined; + } + + selected_type?(): identities.IdentitifyType { + return this.selected_identity_type ? identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; + } + + set_identity(type: identities.IdentitifyType, identity: identities.Identity) { + this.identities[identities.IdentitifyType[type].toLowerCase()] = identity; + } + + spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { + const identity = this.selected_identity(); + if (!identity) + return undefined; + return identity.spawn_identity_handshake_handler(connection); + } + + encode?(): string { + const identity_data = {}; + for (const key in this.identities) + if (this.identities[key]) + identity_data[key] = this.identities[key].encode(); + + return JSON.stringify({ + version: 1, + username: this.default_username, + password: this.default_password, + profile_name: this.profile_name, + identity_type: this.selected_identity_type, + identity_data: identity_data, + id: this.id + }); + } + + valid(): boolean { + const identity = this.selected_identity(); + if (!identity || !identity.valid()) return false; + + return true; + } + } + + async function decode_profile(data): Promise { + data = JSON.parse(data); + if (data.version !== 1) + return "invalid version"; + + const result: ConnectionProfile = new ConnectionProfile(data.id); + result.default_username = data.username; + result.default_password = data.password; + result.profile_name = data.profile_name; + result.selected_identity_type = (data.identity_type || "").toLowerCase(); + + if (data.identity_data) { + for (const key in data.identity_data) { + const type = identities.IdentitifyType[key.toUpperCase() as string]; + const _data = data.identity_data[key]; + if (type == undefined) continue; + + const identity = await identities.decode_identity(type, _data); + if (identity == undefined) continue; + + result.identities[key.toLowerCase()] = identity; + } + } + + return result; + } + + interface ProfilesData { + version: number; + profiles: string[]; + } + + let available_profiles: ConnectionProfile[] = []; + + export async function load() { + available_profiles = []; + + const profiles_json = localStorage.getItem("profiles"); + let profiles_data: ProfilesData = (() => { + try { + return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; + } catch (error) { + debugger; + console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); + createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); + return {version: 0}; + } + })(); + + if (profiles_data.version === 0) { + profiles_data = { + version: 1, + profiles: [] + }; + } + if (profiles_data.version == 1) { + for (const profile_data of profiles_data.profiles) { + const profile = await decode_profile(profile_data); + if (typeof (profile) === 'string') { + console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); + continue; + } + available_profiles.push(profile); + } + } + + if (!find_profile("default")) { //Create a default profile and teaforo profile + { + const profile = create_new_profile("default", "default"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "Default Profile"; + + /* generate default identity */ + try { + const identity = await identities.TeaSpeakIdentity.generate_new(); + let active = true; + setTimeout(() => { + active = false; + }, 1000); + await identity.improve_level(8, 1, () => active); + profile.set_identity(identities.IdentitifyType.TEAMSPEAK, identity); + profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAMSPEAK]; + } catch (error) { + createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); + } + } + + { /* forum identity (works only when connected to the forum) */ + const profile = create_new_profile("TeaSpeak Forum", "teaforo"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "TeaSpeak Forum profile"; + + profile.set_identity(identities.IdentitifyType.TEAFORO, identities.static_forum_identity()); + profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAFORO]; + } + + save(); + } + } + + export function create_new_profile(name: string, id?: string): ConnectionProfile { + const profile = new ConnectionProfile(id || guid()); + profile.profile_name = name; + profile.default_username = ""; + available_profiles.push(profile); + return profile; + } + + let _requires_save = false; + + export function save() { + const profiles: string[] = []; + for (const profile of available_profiles) + profiles.push(profile.encode()); + + const data = JSON.stringify({ + version: 1, + profiles: profiles + }); + localStorage.setItem("profiles", data); + } + + export function mark_need_save() { + _requires_save = true; + } + + export function requires_save(): boolean { + return _requires_save; + } + + export function profiles(): ConnectionProfile[] { + return available_profiles; + } + + export function find_profile(id: string): ConnectionProfile | undefined { + for (const profile of profiles()) + if (profile.id == id) + return profile; + return undefined; } - selected_type?(): IdentitifyType { - return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; + export function find_profile_by_name(name: string): ConnectionProfile | undefined { + name = name.toLowerCase(); + for (const profile of profiles()) + if ((profile.profile_name || "").toLowerCase() == name) + return profile; + + return undefined; } - set_identity(type: IdentitifyType, identity: Identity) { - this.identities[IdentitifyType[type].toLowerCase()] = identity; + + export function default_profile(): ConnectionProfile { + return find_profile("default"); } - spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler { - const identity = this.selected_identity(); - if (!identity) - return undefined; - return identity.spawn_identity_handshake_handler(connection); - } - - encode?(): string { - const identity_data = {}; - for (const key in this.identities) - if (this.identities[key]) - identity_data[key] = this.identities[key].encode(); - - return JSON.stringify({ - version: 1, - username: this.default_username, - password: this.default_password, - profile_name: this.profile_name, - identity_type: this.selected_identity_type, - identity_data: identity_data, - id: this.id - }); - } - - valid(): boolean { - const identity = this.selected_identity(); - if (!identity || !identity.valid()) return false; - - return true; - } -} - -async function decode_profile(data): Promise { - data = JSON.parse(data); - if (data.version !== 1) - return "invalid version"; - - const result: ConnectionProfile = new ConnectionProfile(data.id); - result.default_username = data.username; - result.default_password = data.password; - result.profile_name = data.profile_name; - result.selected_identity_type = (data.identity_type || "").toLowerCase(); - - if (data.identity_data) { - for (const key in data.identity_data) { - const type = IdentitifyType[key.toUpperCase() as string]; - const _data = data.identity_data[key]; - if (type == undefined) continue; - - const identity = await decode_identity(type, _data); - if (identity == undefined) continue; - - result.identities[key.toLowerCase()] = identity; + export function set_default_profile(profile: ConnectionProfile) { + const old_default = default_profile(); + if (old_default && old_default != profile) { + old_default.id = guid(); } + profile.id = "default"; + return old_default; } - return result; -} - -interface ProfilesData { - version: number; - profiles: string[]; -} - -let available_profiles: ConnectionProfile[] = []; - -export async function load() { - available_profiles = []; - - const profiles_json = localStorage.getItem("profiles"); - let profiles_data: ProfilesData = (() => { - try { - return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; - } catch (error) { - debugger; - console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); - createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); - return {version: 0}; - } - })(); - - if (profiles_data.version === 0) { - profiles_data = { - version: 1, - profiles: [] - }; + export function delete_profile(profile: ConnectionProfile) { + available_profiles.remove(profile); } - if (profiles_data.version == 1) { - for (const profile_data of profiles_data.profiles) { - const profile = await decode_profile(profile_data); - if (typeof (profile) === 'string') { - console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); - continue; - } - available_profiles.push(profile); - } - } - - if (!find_profile("default")) { //Create a default profile and teaforo profile - { - const profile = create_new_profile("default", "default"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "Default Profile"; - - /* generate default identity */ - try { - const identity = await TeaSpeakIdentity.generate_new(); - let active = true; - setTimeout(() => { - active = false; - }, 1000); - await identity.improve_level(8, 1, () => active); - profile.set_identity(IdentitifyType.TEAMSPEAK, identity); - profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAMSPEAK]; - } catch (error) { - createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); - } - } - - { /* forum identity (works only when connected to the forum) */ - const profile = create_new_profile("TeaSpeak Forum", "teaforo"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "TeaSpeak Forum profile"; - - profile.set_identity(IdentitifyType.TEAFORO, static_forum_identity()); - profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO]; - } - - save(); - } -} - -export function create_new_profile(name: string, id?: string): ConnectionProfile { - const profile = new ConnectionProfile(id || guid()); - profile.profile_name = name; - profile.default_username = ""; - available_profiles.push(profile); - return profile; -} - -let _requires_save = false; - -export function save() { - const profiles: string[] = []; - for (const profile of available_profiles) - profiles.push(profile.encode()); - - const data = JSON.stringify({ - version: 1, - profiles: profiles - }); - localStorage.setItem("profiles", data); -} - -export function mark_need_save() { - _requires_save = true; -} - -export function requires_save(): boolean { - return _requires_save; -} - -export function profiles(): ConnectionProfile[] { - return available_profiles; -} - -export function find_profile(id: string): ConnectionProfile | undefined { - for (const profile of profiles()) - if (profile.id == id) - return profile; - - return undefined; -} - -export function find_profile_by_name(name: string): ConnectionProfile | undefined { - name = name.toLowerCase(); - for (const profile of profiles()) - if ((profile.profile_name || "").toLowerCase() == name) - return profile; - - return undefined; -} - - -export function default_profile(): ConnectionProfile { - return find_profile("default"); -} - -export function set_default_profile(profile: ConnectionProfile) { - const old_default = default_profile(); - if (old_default && old_default != profile) { - old_default.id = guid(); - } - profile.id = "default"; - return old_default; -} - -export function delete_profile(profile: ConnectionProfile) { - available_profiles.remove(profile); } \ No newline at end of file diff --git a/shared/js/profiles/Identity.ts b/shared/js/profiles/Identity.ts index 6e4dfbdc..c658f46d 100644 --- a/shared/js/profiles/Identity.ts +++ b/shared/js/profiles/Identity.ts @@ -1,116 +1,110 @@ -import {AbstractCommandHandler, AbstractServerConnection, ServerCommand} from "../connection/ConnectionBase"; -import {connection} from "../connection/HandshakeHandler"; - -import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; -import {NameIdentity} from "./identities/NameIdentity"; -import {TeaForumIdentity} from "./identities/TeaForumIdentity"; -import {TeaSpeakIdentity} from "./identities/TeamSpeakIdentity"; - -export enum IdentitifyType { - TEAFORO, - TEAMSPEAK, - NICKNAME -} - -export interface Identity { - fallback_name(): string | undefined ; - uid() : string; - type() : IdentitifyType; - - valid() : boolean; - - encode?() : string; - decode(data: string) : Promise; - - spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler; -} - -export async function decode_identity(type: IdentitifyType, data: string) : Promise { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - if(!identity) - return undefined; - - try { - await identity.decode(data) - } catch(error) { - /* todo better error handling! */ - console.error(error); - return undefined; +namespace profiles.identities { + export enum IdentitifyType { + TEAFORO, + TEAMSPEAK, + NICKNAME } - return identity; -} + export interface Identity { + fallback_name(): string | undefined ; + uid() : string; + type() : IdentitifyType; -export function create_identity(type: IdentitifyType) { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - return identity; -} + valid() : boolean; -export class HandshakeCommandHandler extends AbstractCommandHandler { - readonly handle: T; + encode?() : string; + decode(data: string) : Promise; - constructor(connection: AbstractServerConnection, handle: T) { - super(connection); - this.handle = handle; + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler; } - - handle_command(command: ServerCommand): boolean { - if($.isFunction(this[command.command])) - this[command.command](command.arguments); - else if(command.command == "error") { - return false; - } else { - console.warn(tr("Received unknown command while handshaking (%o)"), command); + export async function decode_identity(type: IdentitifyType, data: string) : Promise { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeaSpeakIdentity(undefined, undefined); + break; } - return true; - } -} + if(!identity) + return undefined; -export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { - connection: AbstractServerConnection; + try { + await identity.decode(data) + } catch(error) { + /* todo better error handling! */ + console.error(error); + return undefined; + } - protected callbacks: ((success: boolean, message?: string) => any)[] = []; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; + return identity; } - register_callback(callback: (success: boolean, message?: string) => any) { - this.callbacks.push(callback); + export function create_identity(type: IdentitifyType) { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + identity = new NameIdentity(); + break; + case IdentitifyType.TEAFORO: + identity = new TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + identity = new TeaSpeakIdentity(undefined, undefined); + break; + } + return identity; } - abstract start_handshake(); + export class HandshakeCommandHandler extends connection.AbstractCommandHandler { + readonly handle: T; - protected trigger_success() { - for(const callback of this.callbacks) - callback(true); + constructor(connection: connection.AbstractServerConnection, handle: T) { + super(connection); + this.handle = handle; + } + + + handle_command(command: connection.ServerCommand): boolean { + if($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if(command.command == "error") { + return false; + } else { + console.warn(tr("Received unknown command while handshaking (%o)"), command); + } + return true; + } } - protected trigger_fail(message: string) { - for(const callback of this.callbacks) - callback(false, message); + export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { + connection: connection.AbstractServerConnection; + + protected callbacks: ((success: boolean, message?: string) => any)[] = []; + + protected constructor(connection: connection.AbstractServerConnection) { + this.connection = connection; + } + + register_callback(callback: (success: boolean, message?: string) => any) { + this.callbacks.push(callback); + } + + abstract start_handshake(); + + protected trigger_success() { + for(const callback of this.callbacks) + callback(true); + } + + protected trigger_fail(message: string) { + for(const callback of this.callbacks) + callback(false, message); + } } } \ No newline at end of file diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts index 876f6c46..51cbefc7 100644 --- a/shared/js/profiles/identities/NameIdentity.ts +++ b/shared/js/profiles/identities/NameIdentity.ts @@ -1,92 +1,88 @@ -import {CommandResult} from "../../connection/ServerConnectionDeclaration"; -import {log, LogCategory} from "../../log"; -import {AbstractServerConnection} from "../../connection/ConnectionBase"; -import {connection} from "../../connection/HandshakeHandler"; +/// -import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; -import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; +namespace profiles.identities { + class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: NameIdentity; + handler: HandshakeCommandHandler; -class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: NameIdentity; - handler: HandshakeCommandHandler; + constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) { + super(connection); + this.identity = identity; - constructor(connection: AbstractServerConnection, identity: NameIdentity) { - super(connection); - this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); + } - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + client_nickname: this.identity.name() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }).then(() => this.trigger_success()); + } + + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - client_nickname: this.identity.name() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }).then(() => this.trigger_success()); - } + export class NameIdentity implements Identity { + private _name: string; - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } + constructor(name?: string) { + this._name = name; + } - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} + set_name(name: string) { this._name = name; } -export class NameIdentity implements Identity { - private _name: string; + name() : string { return this._name; } - constructor(name?: string) { - this._name = name; - } + fallback_name(): string | undefined { + return this._name; + } - set_name(name: string) { this._name = name; } + uid(): string { + return btoa(this._name); //FIXME hash! + } - name() : string { return this._name; } + type(): IdentitifyType { + return IdentitifyType.NICKNAME; + } - fallback_name(): string | undefined { - return this._name; - } + valid(): boolean { + return this._name != undefined && this._name.length >= 5; + } - uid(): string { - return btoa(this._name); //FIXME hash! - } + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; - type(): IdentitifyType { - return IdentitifyType.NICKNAME; - } + this._name = data["name"]; + return; + } - valid(): boolean { - return this._name != undefined && this._name.length >= 5; - } + encode?() : string { + return JSON.stringify({ + version: 1, + name: this._name + }); + } - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; - - this._name = data["name"]; - return; - } - - encode?() : string { - return JSON.stringify({ - version: 1, - name: this._name - }); - } - - spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { - return new NameHandshakeHandler(connection, this); + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { + return new NameHandshakeHandler(connection, this); + } } } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeaForumIdentity.ts b/shared/js/profiles/identities/TeaForumIdentity.ts index d9224645..2f44e31e 100644 --- a/shared/js/profiles/identities/TeaForumIdentity.ts +++ b/shared/js/profiles/identities/TeaForumIdentity.ts @@ -1,126 +1,122 @@ -import {AbstractServerConnection} from "../../connection/ConnectionBase"; -import {log, LogCategory} from "../../log"; -import {CommandResult} from "../../connection/ServerConnectionDeclaration"; -import {forum} from "./teaspeak-forum"; -import {connection} from "../../connection/HandshakeHandler"; -import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; -import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; +/// -class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: TeaForumIdentity; - handler: HandshakeCommandHandler; +namespace profiles.identities { + class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: TeaForumIdentity; + handler: HandshakeCommandHandler; - constructor(connection: AbstractServerConnection, identity: TeaForumIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + data: this.identity.data().data_json() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + + + private handle_proof(json) { + this.connection.send_command("handshakeindentityproof", { + proof: this.identity.data().data_sign() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + } + + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - data: this.identity.data().data_json() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); + export class TeaForumIdentity implements Identity { + private readonly identity_data: forum.Data; - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); + valid() : boolean { + return !!this.identity_data && !this.identity_data.is_expired(); + } + + constructor(data: forum.Data) { + this.identity_data = data; + } + + data() : forum.Data { + return this.identity_data; + } + + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; + + return; + } + + encode() : string { + return JSON.stringify({ + version: 1 + }); + } + + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { + return new TeaForumHandshakeHandler(connection, this); + } + + fallback_name(): string | undefined { + return this.identity_data ? this.identity_data.name() : undefined; + } + + type(): profiles.identities.IdentitifyType { + return IdentitifyType.TEAFORO; + } + + uid(): string { + //FIXME: Real UID! + return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); + } } + let static_identity: TeaForumIdentity; - private handle_proof(json) { - this.connection.send_command("handshakeindentityproof", { - proof: this.identity.data().data_sign() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); + export function set_static_identity(identity: TeaForumIdentity) { + static_identity = identity; } - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); + export function update_forum() { + if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { + static_identity = new TeaForumIdentity(forum.data()); + } else { + static_identity = undefined; + } } - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} - -export class TeaForumIdentity implements Identity { - private readonly identity_data: forum.Data; - - valid() : boolean { - return !!this.identity_data && !this.identity_data.is_expired(); + export function valid_static_forum_identity() : boolean { + return static_identity && static_identity.valid(); } - constructor(data: forum.Data) { - this.identity_data = data; + export function static_forum_identity() : TeaForumIdentity | undefined { + return static_identity; } - - data() : forum.Data { - return this.identity_data; - } - - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; - - return; - } - - encode() : string { - return JSON.stringify({ - version: 1 - }); - } - - spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { - return new TeaForumHandshakeHandler(connection, this); - } - - fallback_name(): string | undefined { - return this.identity_data ? this.identity_data.name() : undefined; - } - - type(): IdentitifyType { - return IdentitifyType.TEAFORO; - } - - uid(): string { - //FIXME: Real UID! - return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); - } -} - -let static_identity: TeaForumIdentity; - -export function set_static_identity(identity: TeaForumIdentity) { - static_identity = identity; -} - -export function update_forum() { - if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { - static_identity = new TeaForumIdentity(forum.data()); - } else { - static_identity = undefined; - } -} - -export function valid_static_forum_identity() : boolean { - return static_identity && static_identity.valid(); -} - -export function static_forum_identity() : TeaForumIdentity | undefined { - return static_identity; } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts index 287adb02..b7c5be9c 100644 --- a/shared/js/profiles/identities/TeamSpeakIdentity.ts +++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts @@ -1,115 +1,88 @@ -import {arrayBufferBase64, base64_encode_ab, str2ab8} from "../../main"; -import {sha} from "../../crypto/sha"; -import {asn1} from "../../crypto/asn1"; -import {AbstractServerConnection} from "../../connection/ConnectionBase"; -import {log, LogCategory} from "../../log"; -import {CommandResult} from "../../connection/ServerConnectionDeclaration"; -import {settings} from "../../settings"; -import {connection} from "../../connection/HandshakeHandler"; -import HandshakeIdentityHandler = connection.HandshakeIdentityHandler; -import {AbstractHandshakeIdentityHandler, HandshakeCommandHandler, IdentitifyType, Identity} from "../Identity"; +/// -export namespace CryptoHelper { - export function base64_url_encode(str){ - return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); - } +namespace profiles.identities { + export namespace CryptoHelper { + export function base64_url_encode(str){ + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); + } - export function base64_url_decode(str: string, pad?: boolean){ - if(typeof(pad) === 'undefined' || pad) - str = (str + '===').slice(0, str.length + (str.length % 4)); - return str.replace(/-/g, '+').replace(/_/g, '/'); - } + export function base64_url_decode(str: string, pad?: boolean){ + if(typeof(pad) === 'undefined' || pad) + str = (str + '===').slice(0, str.length + (str.length % 4)); + return str.replace(/-/g, '+').replace(/_/g, '/'); + } - export function arraybuffer_to_string(buf) { - return String.fromCharCode.apply(null, new Uint16Array(buf)); - } + export function arraybuffer_to_string(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } - export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { - /* - Tomcrypt public key export: - if (type == PK_PRIVATE) { - flags[0] = 1; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_INTEGER, 1UL, key->k, - LTC_ASN1_EOL, 0UL, NULL); - } else { - flags[0] = 0; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_EOL, 0UL, NULL); + export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { + /* + Tomcrypt public key export: + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + */ + + const key_data = await crypto.subtle.exportKey("jwk", crypto_key); + + let index = 0; + const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ + const buffer = new Uint8Array(length); /* fixed ASN1 length */ + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ } - - */ - - const key_data = await crypto.subtle.exportKey("jwk", crypto_key); - - let index = 0; - const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ - const buffer = new Uint8Array(length); /* fixed ASN1 length */ - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* the flags bit string */ - buffer[index++] = 0x03; /* type */ - buffer[index++] = 0x02; /* length */ - buffer[index++] = 0x07; /* data */ - buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ - } - { /* key size (const 32 for P-256) */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x01; /* length */ - buffer[index++] = 0x20; - } - try { /* Public kex X */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.x, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; + { /* the flags bit string */ + buffer[index++] = 0x03; /* type */ + buffer[index++] = 0x02; /* length */ + buffer[index++] = 0x07; /* data */ + buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse x coordinate (invalid base64)"; - throw error; - } - - try { /* Public kex Y */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.y, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; + { /* key size (const 32 for P-256) */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x01; /* length */ + buffer[index++] = 0x20; } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse y coordinate (invalid base64)"; - throw error; - } - - if(!public_key) { - try { /* Public kex K */ + try { /* Public kex X */ buffer[index++] = 0x02; /* type */ buffer[index++] = 0x20; /* length */ - const raw = atob(base64_url_decode(key_data.d, false)); + const raw = atob(base64_url_decode(key_data.x, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse x coordinate (invalid base64)"; + throw error; + } + + try { /* Public kex Y */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.y, false)); if(raw.charCodeAt(0) > 0x7F) { buffer[index - 1] += 1; buffer[index++] = 0; @@ -122,284 +95,223 @@ export namespace CryptoHelper { throw "failed to parse y coordinate (invalid base64)"; throw error; } + + if(!public_key) { + try { /* Public kex K */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.d, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; + } + } + + buffer[1] = index - 2; /* set the final sequence length */ + + return base64_encode_ab(buffer.buffer.slice(0, index)); } - buffer[1] = index - 2; /* set the final sequence length */ - - return base64_encode_ab(buffer.buffer.slice(0, index)); - } - - const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; - function c_strlen(buffer: Uint8Array, offset: number) : number { - let index = 0; - while(index + offset < buffer.length && buffer[index + offset] != 0) - index++; - return index; - } - - export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { - /* buffer could contains a zero! */ - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - return arraybuffer_to_string(buffer); - } - - export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - return base64_encode_ab(buffer); - } - - /** - * @param buffer base64 encoded ASN.1 string - */ - export function decode_tomcrypt_key(buffer: string) { - let decoded; - - try { - decoded = asn1.decode(atob(buffer)); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse key buffer (invalid base64)"; - throw error; + const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; + function c_strlen(buffer: Uint8Array, offset: number) : number { + let index = 0; + while(index + offset < buffer.length && buffer[index + offset] != 0) + index++; + return index; } - let {x, y, k} = { - x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), - y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), - k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) - }; + export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { + /* buffer could contains a zero! */ + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; - if(x.length > 32) { - if(x.charCodeAt(0) != 0) - throw "Invalid X coordinate! (Too long)"; - x = x.substr(1); + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + return arraybuffer_to_string(buffer); } - if(y.length > 32) { - if(y.charCodeAt(0) != 0) - throw "Invalid Y coordinate! (Too long)"; - y = y.substr(1); + export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + + return base64_encode_ab(buffer); } - if(k.length > 32) { - if(k.charCodeAt(0) != 0) - throw "Invalid private coordinate! (Too long)"; - k = k.substr(1); + /** + * @param buffer base64 encoded ASN.1 string + */ + export function decode_tomcrypt_key(buffer: string) { + let decoded; + + try { + decoded = asn1.decode(atob(buffer)); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse key buffer (invalid base64)"; + throw error; + } + + let {x, y, k} = { + x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), + y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), + k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) + }; + + if(x.length > 32) { + if(x.charCodeAt(0) != 0) + throw "Invalid X coordinate! (Too long)"; + x = x.substr(1); + } + + if(y.length > 32) { + if(y.charCodeAt(0) != 0) + throw "Invalid Y coordinate! (Too long)"; + y = y.substr(1); + } + + if(k.length > 32) { + if(k.charCodeAt(0) != 0) + throw "Invalid private coordinate! (Too long)"; + k = k.substr(1); + } + + /* + console.log("Key x: %s (%d)", btoa(x), x.length); + console.log("Key y: %s (%d)", btoa(y), y.length); + console.log("Key k: %s (%d)", btoa(k), k.length); + */ + return { + crv: "P-256", + d: base64_url_encode(btoa(k)), + x: base64_url_encode(btoa(x)), + y: base64_url_encode(btoa(y)), + + ext: true, + key_ops:["deriveKey", "sign"], + kty:"EC", + }; } - - /* - console.log("Key x: %s (%d)", btoa(x), x.length); - console.log("Key y: %s (%d)", btoa(y), y.length); - console.log("Key k: %s (%d)", btoa(k), k.length); - */ - return { - crv: "P-256", - d: base64_url_encode(btoa(k)), - x: base64_url_encode(btoa(x)), - y: base64_url_encode(btoa(y)), - - ext: true, - key_ops:["deriveKey", "sign"], - kty:"EC", - }; - } -} - -class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { - identity: TeaSpeakIdentity; - handler: HandshakeCommandHandler; - - constructor(connection: AbstractServerConnection, identity: TeaSpeakIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - publicKey: this.identity.public_key - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { + identity: TeaSpeakIdentity; + handler: HandshakeCommandHandler; - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - - private handle_proof(json) { - if(!json[0]["digest"]) { - this.trigger_fail("server too old"); - return; + constructor(connection: connection.AbstractServerConnection, identity: TeaSpeakIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } - this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { - this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + publicKey: this.identity.public_key + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); if(error instanceof CommandResult) error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - }).catch(error => { - this.trigger_fail("failed to sign message"); - }); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} - -class IdentityPOWWorker { - private _worker: Worker; - private _current_hash: string; - private _best_level: number; - - async initialize(key: string) { - this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - - /* initialize */ - await new Promise((resolve, reject) => { - const timeout_id = setTimeout(() => reject("timeout"), 1000); - - this._worker.onmessage = event => { - clearTimeout(timeout_id); - - if(!event.data) { - reject("invalid data"); - return; - } - - if(!event.data.success) { - reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - this._worker.onerror = event => { - log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); - clearTimeout(timeout_id); - reject("Failed to load worker (" + event.message + ")"); - }; - }); - - /* set data */ - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "set_data", - private_key: key, - code: "set_data" + this.trigger_fail("failed to execute begin (" + error + ")"); }); + } - const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); + private handle_proof(json) { + if(!json[0]["digest"]) { + this.trigger_fail("server too old"); + return; + } - this._worker.onmessage = event => { - clearTimeout(timeout_id); + this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { + this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - }); - } - - async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { - this._current_hash = hash; - if(target < this._best_level) - return true; - - return await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "mine", - hash: this._current_hash, - iterations: iterations, - target: target, - code: "mine" + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + }).catch(error => { + this.trigger_fail("failed to sign message"); }); + } - const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - if(event.data.result) { - this._best_level = event.data.level; - this._current_hash = event.data.hash; - resolve(true); - } else { - resolve(false); /* no result */ - } - }; - }); + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } - current_hash() : string { - return this._current_hash; - } + class IdentityPOWWorker { + private _worker: Worker; + private _current_hash: string; + private _best_level: number; - current_level() : number { - return this._best_level; - } + async initialize(key: string) { + this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - async finalize(timeout?: number) { - try { + /* initialize */ await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "finalize", - code: "finalize" - }); - - const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + const timeout_id = setTimeout(() => reject("timeout"), 1000); this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if(!event.data) { + reject("invalid data"); + return; + } + + if(!event.data.success) { + reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + this._worker.onerror = event => { + log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); + clearTimeout(timeout_id); + reject("Failed to load worker (" + event.message + ")"); + }; + }); + + /* set data */ + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "set_data", + private_key: key, + code: "set_data" + }); + + const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); + + this._worker.onmessage = event => { clearTimeout(timeout_id); if (!event.data) { @@ -408,471 +320,552 @@ class IdentityPOWWorker { } if (!event.data.success) { - reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); return; } + this._worker.onmessage = event => this.handle_message(event.data); resolve(); }; }); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); } - this._worker.terminate(); - this._worker = undefined; - } + async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { + this._current_hash = hash; + if(target < this._best_level) + return true; - private handle_message(message: any) { - log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); - } -} + return await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "mine", + hash: this._current_hash, + iterations: iterations, + target: target, + code: "mine" + }); -export class TeaSpeakIdentity implements Identity { - static async generate_new() : Promise { - let key: CryptoKeyPair; - try { - key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(e) { - log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); - throw "Failed to generate keypair"; - } - const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); + const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); - await identity.initialize(); - return identity; - } + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); - static async import_ts(ts_string: string, ini?: boolean) : Promise { - const parse_string = string => { - /* parsing without INI structure */ - const V_index = string.indexOf('V'); - if(V_index == -1) throw "invalid input (missing V)"; + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } - return { - hash: string.substr(0, V_index), - data: string.substr(V_index + 1), - name: "TeaSpeak user" - } - }; + if (!event.data.success) { + reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } - const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { - /* parsing with INI structure */ - let identity: string, name: string; - - for(const line of ts_string.split("\n")) { - if(line.startsWith("identity=")) - identity = line.substr(9); - else if(line.startsWith("nickname=")) - name = line.substr(9); - } - - if(!identity) throw "missing identity keyword"; - identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; - if(!identity) throw "invalid identity key value"; - - const result = parse_string(identity); - result.name = name || result.name; - return result; - })(); - - if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - - let buffer; - try { - buffer = new Uint8Array(arrayBufferBase64(data)); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); - throw "failed to base data (base64 decode failed)"; - } - const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - - const identity = new TeaSpeakIdentity(key64, hash, name, false); - await identity.initialize(); - return identity; - } - - hash_number: string; /* hash suffix for the private key */ - private_key: string; /* base64 representation of the private key */ - _name: string; - - public_key: string; /* only set when initialized */ - - private _initialized: boolean; - private _crypto_key: CryptoKey; - private _crypto_key_sign: CryptoKey; - - private _unique_id: string; - - constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { - this.private_key = private_key; - this.hash_number = hash || "0"; - this._name = name; - - if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { - this.initialize().catch(error => { - log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); - this._initialized = false; + if(event.data.result) { + this._best_level = event.data.level; + this._current_hash = event.data.hash; + resolve(true); + } else { + resolve(false); /* no result */ + } + }; }); } + + current_hash() : string { + return this._current_hash; + } + + current_level() : number { + return this._best_level; + } + + async finalize(timeout?: number) { + try { + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + + clearTimeout(timeout_id); + + if (!event.data) { + reject("invalid data"); + return; + } + + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + resolve(); + }; + }); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } + + this._worker.terminate(); + this._worker = undefined; + } + + private handle_message(message: any) { + log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); + } } - fallback_name(): string | undefined { - return this._name; - } + export class TeaSpeakIdentity implements Identity { + static async generate_new() : Promise { + let key: CryptoKeyPair; + try { + key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(e) { + log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); + throw "Failed to generate keypair"; + } + const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); - uid(): string { - return this._unique_id; - } + const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); + await identity.initialize(); + return identity; + } - type(): IdentitifyType { - return IdentitifyType.TEAMSPEAK; - } + static async import_ts(ts_string: string, ini?: boolean) : Promise { + const parse_string = string => { + /* parsing without INI structure */ + const V_index = string.indexOf('V'); + if(V_index == -1) throw "invalid input (missing V)"; - valid(): boolean { - return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; - } + return { + hash: string.substr(0, V_index), + data: string.substr(V_index + 1), + name: "TeaSpeak user" + } + }; - async decode(data: string) : Promise { - const json = JSON.parse(data); - if(!json) throw "invalid json"; + const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { + /* parsing with INI structure */ + let identity: string, name: string; - if(json.version == 2) { - this.private_key = json.key; - this.hash_number = json.hash; - this._name = json.name; - } else if(json.version == 1) { - const key = json.key; - this._name = json.name; + for(const line of ts_string.split("\n")) { + if(line.startsWith("identity=")) + identity = line.substr(9); + else if(line.startsWith("nickname=")) + name = line.substr(9); + } - const clone = await TeaSpeakIdentity.import_ts(key, false); - this.private_key = clone.private_key; - this.hash_number = clone.hash_number; - } else - throw "invalid version"; + if(!identity) throw "missing identity keyword"; + identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; + if(!identity) throw "invalid identity key value"; - await this.initialize(); - } + const result = parse_string(identity); + result.name = name || result.name; + return result; + })(); - encode?() : string { - return JSON.stringify({ - key: this.private_key, - hash: this.hash_number, - name: this._name, - version: 2 - }); - } + if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - async level() : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; + let buffer; + try { + buffer = new Uint8Array(arrayBufferBase64(data)); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); + throw "failed to base data (base64 decode failed)"; + } + const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + const identity = new TeaSpeakIdentity(key64, hash, name, false); + await identity.initialize(); + return identity; + } - let level = 0; - while(level < hash.byteLength && hash[level] == 0) - level++; + hash_number: string; /* hash suffix for the private key */ + private_key: string; /* base64 representation of the private key */ + _name: string; - if(level >= hash.byteLength) { - level = 256; - } else { - let byte = hash[level]; - level <<= 3; - while((byte & 0x1) == 0) { + public_key: string; /* only set when initialized */ + + private _initialized: boolean; + private _crypto_key: CryptoKey; + private _crypto_key_sign: CryptoKey; + + private _unique_id: string; + + constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { + this.private_key = private_key; + this.hash_number = hash || "0"; + this._name = name; + + if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { + this.initialize().catch(error => { + log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); + this._initialized = false; + }); + } + } + + fallback_name(): string | undefined { + return this._name; + } + + uid(): string { + return this._unique_id; + } + + type(): IdentitifyType { + return IdentitifyType.TEAMSPEAK; + } + + valid(): boolean { + return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; + } + + async decode(data: string) : Promise { + const json = JSON.parse(data); + if(!json) throw "invalid json"; + + if(json.version == 2) { + this.private_key = json.key; + this.hash_number = json.hash; + this._name = json.name; + } else if(json.version == 1) { + const key = json.key; + this._name = json.name; + + const clone = await TeaSpeakIdentity.import_ts(key, false); + this.private_key = clone.private_key; + this.hash_number = clone.hash_number; + } else + throw "invalid version"; + + await this.initialize(); + } + + encode?() : string { + return JSON.stringify({ + key: this.private_key, + hash: this.hash_number, + name: this._name, + version: 2 + }); + } + + async level() : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + + const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + + let level = 0; + while(level < hash.byteLength && hash[level] == 0) level++; - byte >>= 1; - } - } - return level; - } - - /** - * @param {string} a - * @param {string} b - * @description b must be smaller (in bytes) then a - */ - private string_add(a: string, b: string) { - const char_result: number[] = []; - const char_a = [...a].reverse().map(e => e.charCodeAt(0)); - const char_b = [...b].reverse().map(e => e.charCodeAt(0)); - - let carry = false; - while(char_b.length > 0) { - let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } - - while(char_a.length > 0) { - let result = char_a.pop_front() + (carry ? 1 : 0); - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } - - if(carry) - char_result.push(49); - - return String.fromCharCode.apply(null, char_result.slice().reverse()); - } - - - async improve_level_for(time: number, threads: number) : Promise { - let active = true; - setTimeout(() => active = false, time); - - return await this.improve_level(-1, threads, () => active); - } - - async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; - if(target == -1) /* get the highest level possible */ - target = 0; - else if(target <= await this.level()) - return true; - - const workers: IdentityPOWWorker[] = []; - - const iterations = 100000; - let current_hash; - const next_hash = () => { - if(!current_hash) - return (current_hash = this.hash_number); - - if(current_hash.length < iterations.toString().length) { - current_hash = this.string_add(iterations.toString(), current_hash); + if(level >= hash.byteLength) { + level = 256; } else { - current_hash = this.string_add(current_hash, iterations.toString()); + let byte = hash[level]; + level <<= 3; + while((byte & 0x1) == 0) { + level++; + byte >>= 1; + } } - return current_hash; - }; - { /* init */ - const initialize_promise: Promise[] = []; - for(let index = 0; index < threads; index++) { - const worker = new IdentityPOWWorker(); - workers.push(worker); - initialize_promise.push(worker.initialize(this.public_key)); + return level; + } + + /** + * @param {string} a + * @param {string} b + * @description b must be smaller (in bytes) then a + */ + private string_add(a: string, b: string) { + const char_result: number[] = []; + const char_a = [...a].reverse().map(e => e.charCodeAt(0)); + const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + + let carry = false; + while(char_b.length > 0) { + let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; + if((carry = result > 57)) + result -= 10; + char_result.push(result); } + while(char_a.length > 0) { + let result = char_a.pop_front() + (carry ? 1 : 0); + if((carry = result > 57)) + result -= 10; + char_result.push(result); + } + + if(carry) + char_result.push(49); + + return String.fromCharCode.apply(null, char_result.slice().reverse()); + } + + + async improve_level_for(time: number, threads: number) : Promise { + let active = true; + setTimeout(() => active = false, time); + + return await this.improve_level(-1, threads, () => active); + } + + async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + if(target == -1) /* get the highest level possible */ + target = 0; + else if(target <= await this.level()) + return true; + + const workers: IdentityPOWWorker[] = []; + + const iterations = 100000; + let current_hash; + const next_hash = () => { + if(!current_hash) + return (current_hash = this.hash_number); + + if(current_hash.length < iterations.toString().length) { + current_hash = this.string_add(iterations.toString(), current_hash); + } else { + current_hash = this.string_add(current_hash, iterations.toString()); + } + return current_hash; + }; + + { /* init */ + const initialize_promise: Promise[] = []; + for(let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } + + try { + await Promise.all(initialize_promise); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } + } + + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : await this.level() + 1; + + const worker_promise: Promise[] = []; + + const hash_timestamps: number[] = []; + let last_hashrate_update: number = 0; + + const update_hashrate = () => { + if(!callback_status) return; + const now = Date.now(); + hash_timestamps.push(now); + + if(last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + + const timeout = now - 10 * 1000; /* 10s */ + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) + } + }; + try { - await Promise.all(initialize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to initialize"; - } - } + result = await new Promise((resolve, reject) => { + let active = true; - let result = false; - let best_level = 0; - let target_level = target > 0 ? target : await this.level() + 1; - - const worker_promise: Promise[] = []; - - const hash_timestamps: number[] = []; - let last_hashrate_update: number = 0; - - const update_hashrate = () => { - if(!callback_status) return; - const now = Date.now(); - hash_timestamps.push(now); - - if(last_hashrate_update + 1000 < now) { - last_hashrate_update = now; - - const timeout = now - 10 * 1000; /* 10s */ - const rounds = hash_timestamps.filter(e => e > timeout); - callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) - } - }; - - try { - result = await new Promise((resolve, reject) => { - let active = true; - - const exit = () => { - const timeout = setTimeout(() => resolve(true), 1000); - Promise.all(worker_promise).then(result => { - clearTimeout(timeout); - resolve(true); - }).catch(error => resolve(true)); - active = false; - }; - - for(const worker of workers) { - const worker_mine = () => { - if(!active) return; - - const promise = worker.mine(next_hash(), iterations, target_level); - const p = promise.then(result => { - update_hashrate(); - - worker_promise.remove(p); - - if(result.valueOf()) { - if(worker.current_level() > best_level) { - this.hash_number = worker.current_hash(); - - log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); - best_level = worker.current_level(); - if(callback_level) - callback_level(best_level); - } - - if(active) { - if(target > 0) - exit(); - else - target_level = best_level + 1; - } - } - - if(active && (active = active_callback())) - setTimeout(() => worker_mine(), 0); - else { - exit(); - } - - return Promise.resolve(); - }).catch(error => { - worker_promise.remove(p); - - log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); - reject(error); - - return Promise.resolve(); - }); - - worker_promise.push(p); + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; }; - worker_mine(); + for(const worker of workers) { + const worker_mine = () => { + if(!active) return; + + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + + worker_promise.remove(p); + + if(result.valueOf()) { + if(worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + + log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if(callback_level) + callback_level(best_level); + } + + if(active) { + if(target > 0) + exit(); + else + target_level = best_level + 1; + } + } + + if(active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + + log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + + return Promise.resolve(); + }); + + worker_promise.push(p); + }; + + worker_mine(); + } + }); + } catch(error) { + //error already printed before reject had been called + } + + { /* shutdown */ + const finalize_promise: Promise[] = []; + for(const worker of workers) + finalize_promise.push(worker.finalize(250)); + + try { + await Promise.all(finalize_promise); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to finalize"; } - }); - } catch(error) { - //error already printed before reject had been called + } + + + return result; } - { /* shutdown */ - const finalize_promise: Promise[] = []; - for(const worker of workers) - finalize_promise.push(worker.finalize(250)); + private async initialize() { + if(!this.private_key) + throw "Invalid private key"; + + let jwk: any; + try { + jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); + if(!jwk) + throw "result undefined"; + } catch(error) { + throw "failed to parse key (" + error + ")"; + } try { - await Promise.all(finalize_promise); + this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); } catch(error) { log.error(LogCategory.IDENTITIES, error); - throw "failed to finalize"; - } - } - - - return result; - } - - private async initialize() { - if(!this.private_key) - throw "Invalid private key"; - - let jwk: any; - try { - jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); - if(!jwk) - throw "result undefined"; - } catch(error) { - throw "failed to parse key (" + error + ")"; - } - - try { - this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto sign key"; - } - - try { - this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto key"; - } - - try { - this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); - this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to calculate unique id"; - } - - this._initialized = true; - //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); - } - - async export_ts(ini?: boolean) : Promise { - if(!this.private_key) - throw "Invalid private key"; - - const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); - if(!ini) return identity; - - return "[Identity]\n" + - "id=TeaWeb-Exported\n" + - "identity=\"" + identity + "\"\n" + - "nickname=\"" + this.fallback_name() + "\"\n" + - "phonetic_nickname="; - } - - async sign_message(message: string, hash: string = "SHA-256") : Promise { - /* bring this to libtomcrypt format */ - const sign_buffer = await crypto.subtle.sign({ - name: "ECDSA", - hash: hash - }, this._crypto_key_sign, str2ab8(message)); - const sign = new Uint8Array(sign_buffer); - /* first 32 r bits | last 32 s bits */ - - const buffer = new Uint8Array(72); - let index = 0; - - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* integer r */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[0] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; + throw "failed to create crypto sign key"; } - for(let i = 0; i < 32; i++) - buffer[index++] = sign[i]; - } - { /* integer s */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[32] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; + try { + this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto key"; } - for(let i = 0; i < 32; i++) - buffer[index++] = sign[32 + i]; + try { + this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); + this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to calculate unique id"; + } + + this._initialized = true; + //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); } - buffer[1] = index - 2; - return base64_encode_ab(buffer.subarray(0, index)); - } + async export_ts(ini?: boolean) : Promise { + if(!this.private_key) + throw "Invalid private key"; - spawn_identity_handshake_handler(connection: AbstractServerConnection): HandshakeIdentityHandler { - return new TeaSpeakHandshakeHandler(connection, this); + const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); + if(!ini) return identity; + + return "[Identity]\n" + + "id=TeaWeb-Exported\n" + + "identity=\"" + identity + "\"\n" + + "nickname=\"" + this.fallback_name() + "\"\n" + + "phonetic_nickname="; + } + + async sign_message(message: string, hash: string = "SHA-256") : Promise { + /* bring this to libtomcrypt format */ + const sign_buffer = await crypto.subtle.sign({ + name: "ECDSA", + hash: hash + }, this._crypto_key_sign, str2ab8(message)); + const sign = new Uint8Array(sign_buffer); + /* first 32 r bits | last 32 s bits */ + + const buffer = new Uint8Array(72); + let index = 0; + + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* integer r */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[0] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[i]; + } + { /* integer s */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[32] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[32 + i]; + } + buffer[1] = index - 2; + + return base64_encode_ab(buffer.subarray(0, index)); + } + + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { + return new TeaSpeakHandshakeHandler(connection, this); + } } } \ No newline at end of file diff --git a/shared/js/profiles/identities/teaspeak-forum.ts b/shared/js/profiles/identities/teaspeak-forum.ts index 802cb87f..1ab6cd53 100644 --- a/shared/js/profiles/identities/teaspeak-forum.ts +++ b/shared/js/profiles/identities/teaspeak-forum.ts @@ -1,11 +1,8 @@ -import {Settings, settings} from "../../settings"; -import {update_forum} from "./TeaForumIdentity"; - -declare interface Window { +interface Window { grecaptcha: GReCaptcha; } -export interface GReCaptcha { +interface GReCaptcha { render(container: string | HTMLElement, parameters: { sitekey: string; theme?: "dark" | "light"; @@ -21,10 +18,10 @@ export interface GReCaptcha { reset(widget_id?: string); } -export namespace forum { +namespace forum { export namespace gcaptcha { export async function initialize() { - if(typeof((window as any).grecaptcha) === "undefined") { + if(typeof(window.grecaptcha) === "undefined") { let script = document.createElement("script"); script.async = true; @@ -53,7 +50,7 @@ export namespace forum { } } - if(typeof((window as any).grecaptcha) === "undefined") + if(typeof(window.grecaptcha) === "undefined") throw tr("failed to load recaptcha"); } @@ -65,9 +62,9 @@ export namespace forum { throw tr("initialisation failed"); } if(container.attr("captcha-uuid")) - (window as any).grecaptcha.reset(container.attr("captcha-uuid")); + window.grecaptcha.reset(container.attr("captcha-uuid")); else { - container.attr("captcha-uuid", (window as any).grecaptcha.render(container[0], { + container.attr("captcha-uuid", window.grecaptcha.render(container[0], { "sitekey": key, callback: callback_data })); @@ -209,7 +206,7 @@ export namespace forum { localStorage.setItem("teaspeak-forum-data", response["data"]); localStorage.setItem("teaspeak-forum-sign", response["sign"]); localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); - update_forum(); + profiles.identities.update_forum(); } catch(error) { console.error(tr("Failed to parse forum given data: %o"), error); return { @@ -269,7 +266,7 @@ export namespace forum { _data = new Data(_data.auth_key, response["data"], response["sign"]); localStorage.setItem("teaspeak-forum-data", response["data"]); localStorage.setItem("teaspeak-forum-sign", response["sign"]); - update_forum(); + profiles.identities.update_forum(); } catch(error) { console.error(tr("Failed to parse forum given data: %o"), error); throw tr("failed to parse data"); @@ -323,7 +320,7 @@ export namespace forum { localStorage.removeItem("teaspeak-forum-data"); localStorage.removeItem("teaspeak-forum-sign"); localStorage.removeItem("teaspeak-forum-auth"); - update_forum(); + profiles.identities.update_forum(); } loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { diff --git a/shared/js/proto.ts b/shared/js/proto.ts index 37ee32e9..18a162a7 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -1,3 +1,5 @@ +//Used by CertAccept popup + interface Array { remove(elem?: T): boolean; last?(): T; diff --git a/shared/js/settings.ts b/shared/js/settings.ts index d90dc807..59a9c8eb 100644 --- a/shared/js/settings.ts +++ b/shared/js/settings.ts @@ -1,9 +1,6 @@ /// //Used by CertAccept popup -import {log, LogCategory} from "./log"; -import {createErrorModal} from "./ui/elements/modal"; - if(typeof(customElements) !== "undefined") { try { class X_Properties extends HTMLElement {} @@ -17,7 +14,7 @@ if(typeof(customElements) !== "undefined") { } /* T = value type */ -export interface SettingsKey { +interface SettingsKey { key: string; fallback_keys?: string | string[]; @@ -28,7 +25,7 @@ export interface SettingsKey { require_restart?: boolean; } -export class SettingsBase { +class SettingsBase { protected static readonly UPDATE_DIRECT: boolean = true; protected static transformStO?(input?: string, _default?: T, default_type?: string) : T { @@ -80,7 +77,7 @@ export class SettingsBase { } } -export class StaticSettings extends SettingsBase { +class StaticSettings extends SettingsBase { private static _instance: StaticSettings; static get instance() : StaticSettings { if(!this._instance) @@ -142,7 +139,7 @@ export class StaticSettings extends SettingsBase { } } -export class Settings extends StaticSettings { +class Settings extends StaticSettings { static readonly KEY_USER_IS_NEW: SettingsKey = { key: 'user_is_new_user', default_value: true @@ -436,7 +433,7 @@ export class Settings extends StaticSettings { } } -export class ServerSettings extends SettingsBase { +class ServerSettings extends SettingsBase { private cacheServer = {}; private _server_unique_id: string; private _server_save_worker: NodeJS.Timer; @@ -514,4 +511,4 @@ export class ServerSettings extends SettingsBase { } } -export let settings: Settings; \ No newline at end of file +let settings: Settings; \ No newline at end of file diff --git a/shared/js/sound/Sounds.ts b/shared/js/sound/Sounds.ts index f2992312..cbb75329 100644 --- a/shared/js/sound/Sounds.ts +++ b/shared/js/sound/Sounds.ts @@ -1,8 +1,4 @@ -import {settings} from "../settings"; -import {log, LogCategory} from "../log"; -import {ConnectionHandler} from "../ConnectionHandler"; - -export enum Sound { +enum Sound { SOUND_TEST = "sound.test", SOUND_EGG = "sound.egg", @@ -65,7 +61,7 @@ export enum Sound { GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self" } -export namespace sound { +namespace sound { export interface SoundHandle { key: string; filename: string; diff --git a/shared/js/stats.ts b/shared/js/stats.ts index cefdfff4..ce9ca84b 100644 --- a/shared/js/stats.ts +++ b/shared/js/stats.ts @@ -1,6 +1,4 @@ -import {log, LogCategory} from "./log"; - -export namespace stats { +namespace stats { const LOG_PREFIX = "[Statistics] "; export enum CloseCodes { diff --git a/shared/js/ui/channel-tree/channel.css b/shared/js/ui/channel-tree/channel.css deleted file mode 100644 index 49a762ab..00000000 --- a/shared/js/ui/channel-tree/channel.css +++ /dev/null @@ -1,81 +0,0 @@ -.channel-container { - display: flex; - flex-direction: column; -} -.channel-container .container-channel { - position: relative; - display: flex; - flex-direction: row; - justify-content: stretch; - width: 100%; - min-height: 16px; - align-items: center; - cursor: pointer; -} -.channel-container .container-channel .channel-type { - flex-grow: 0; - flex-shrink: 0; - margin-right: 2px; -} -.channel-container .container-channel .container-channel-name { - display: flex; - flex-direction: row; - flex-grow: 1; - flex-shrink: 1; - justify-content: left; - max-width: 100%; - /* important for the repetitive channel name! */ - overflow-x: hidden; - height: 16px; -} -.channel-container .container-channel .container-channel-name.align-right { - justify-content: right; -} -.channel-container .container-channel .container-channel-name.align-center, .channel-container .container-channel .container-channel-name.align-repetitive { - justify-content: center; -} -.channel-container .container-channel .container-channel-name .channel-name { - align-self: center; - color: #828282; - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.channel-container .container-channel .container-channel-name.align-repetitive .channel-name { - text-overflow: clip; -} -.channel-container .container-channel .icons { - display: flex; - flex-direction: row; - flex-grow: 0; - flex-shrink: 0; -} -.channel-container .container-channel.move-selected { - border-bottom: 1px solid black; -} -.channel-container .container-channel .show-channel-normal-only { - display: none; -} -.channel-container .container-channel .show-channel-normal-only.channel-normal { - display: block; -} -.channel-container .container-channel .icon_no_sound { - position: relative; - display: flex; -} -.channel-container .container-channel .icon_no_sound .background { - width: 10px; - height: 14px; - background: red; - position: absolute; - top: 1px; - left: 3px; - z-index: -1; -} -.channel-container .container-clients { - display: flex; - flex-direction: column; -} - -/*# sourceMappingURL=channel.css.map */ diff --git a/shared/js/ui/channel-tree/channel.css.map b/shared/js/ui/channel-tree/channel.css.map deleted file mode 100644 index 5c2199e9..00000000 --- a/shared/js/ui/channel-tree/channel.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["channel.scss","colors.scss"],"names":[],"mappings":"AAEA;EACI;EACA;;AAEA;EACI;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;;AAEA;EACI;EACA;EAEA;;AAGJ;EACI;EACA;EAEA;EACA;EAEA;EAEA;AAAiB;EACjB;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;EACA,OC/CW;EDiDX;EACA;EACA;EACA;;AAIA;EACI;;AAKZ;EACI;EACA;EAEA;EACA;;AAGJ;EACI;;AAGJ;EACI;;AAEA;EACI;;AAIR;EACI;EACA;;AAEA;EACI;EACA;EAEA;EACA;EACA;EACA;EACA;;AAKZ;EACI;EACA","file":"channel.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/channel.scss b/shared/js/ui/channel-tree/channel.scss deleted file mode 100644 index 6fa7ebbc..00000000 --- a/shared/js/ui/channel-tree/channel.scss +++ /dev/null @@ -1,106 +0,0 @@ -@import "colors"; - -.channel-container { - display: flex; - flex-direction: column; - - .container-channel { - position: relative; - - display: flex; - flex-direction: row; - justify-content: stretch; - - width: 100%; - min-height: 16px; - - align-items: center; - cursor: pointer; - - .channel-type { - flex-grow: 0; - flex-shrink: 0; - - margin-right: 2px; - } - - .container-channel-name { - display: flex; - flex-direction: row; - - flex-grow: 1; - flex-shrink: 1; - - justify-content: left; - - max-width: 100%; /* important for the repetitive channel name! */ - overflow-x: hidden; - height: 16px; - - &.align-right { - justify-content: right; - } - - &.align-center, &.align-repetitive { - justify-content: center; - } - - .channel-name { - align-self: center; - color: $channel-tree-entry-color; - - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &.align-repetitive { - .channel-name { - text-overflow: clip; - } - } - } - - .icons { - display: flex; - flex-direction: row; - - flex-grow: 0; - flex-shrink: 0; - } - - &.move-selected { - border-bottom: 1px solid black; - } - - .show-channel-normal-only { - display: none; - - &.channel-normal { - display: block; - } - } - - .icon_no_sound { - position: relative; - display: flex; - - .background { - width: 10px; - height: 14px; - - background: red; - position: absolute; - top: 1px; - left: 3px; - z-index: -1; - } - } - } - - .container-clients { - display: flex; - flex-direction: column; - } -} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/channel.tsx b/shared/js/ui/channel-tree/channel.tsx deleted file mode 100644 index 3e24be3b..00000000 --- a/shared/js/ui/channel-tree/channel.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import * as React from "react"; -import {ChannelEntry} from "../../channel-tree/channel"; - -const cssChannel = require("./channel.scss"); -const cssTree = require("./tree.scss"); - -class ChannelIcon extends React.Component<{ channel: ChannelEntry }, {}> { - channel_icon_classes() { - const class_list = []; - const channel = this.props.channel; - - if(channel.formattedChannelName() !== channel.channelName()) - return "channel-spacer"; - - let icon_color; - if(channel.properties.channel_flag_password && !channel.cached_password()) - icon_color = "yellow"; - else if(channel.properties.channel_flag_maxclients_unlimited && channel.clients().length >= channel.properties.channel_maxclients) - icon_color = "red"; - else if(channel.properties.channel_flag_maxfamilyclients_unlimited && channel.clients(true).length >= channel.properties.channel_maxfamilyclients) - icon_color = "red"; - else - icon_color = "green"; - - - return "channel-normal client-channel_" + icon_color + (channel.flag_subscribed ? "_subscribed" : ""); - } - - render() { - return
; - } -} - -class ChannelIcons extends React.Component<{channel: ChannelEntry}, {}> { - render_icon(target: HTMLDivElement) { - const props = this.props.channel.properties; - if(!props.channel_icon_id) return; - - const tag = this.props.channel.channelTree.client.fileManager.icons.generateTag(props.channel_icon_id); - tag.appendTo($(target)); - } - - render() { - const icons = []; - - const props = this.props.channel.properties; - if(props.channel_flag_default) { - icons.push(
); - } - if(props.channel_flag_password) { - icons.push(
); - } - if(props.channel_codec_quality > 4) { - icons.push(
); - } - if(props.channel_needed_talk_power > 0) { - icons.push(
); - } - if(props.channel_icon_id != 0) { - icons.push(
this.render_icon(e) }/>) - } - if(!audio.codec.supported(props.channel_codec)) { - icons.push(
-
-
-
) - } - - return (
{icons}
); - } -} - -class ChannelLine extends React.Component<{ channel: ChannelEntry }, {}> { - - render() { - let depth = 1; - let parent = this.props.channel; - while((parent = parent.parent)) - depth++; - - //TODO: On handle spacer alignments in channel name! - return ( -
-
- - ); - } -} - -class ChannelClientsView extends React.Component<{}, {}> { -} - -class SubChannelView extends React.Component<{ channels: ChannelEntry[] }, {}> { - - render() { - return this.props.channels.map(e => ); - } -} - -export class Channel extends React.Component<{ channel: ChannelEntry }, {}> { - children: ChannelEntry[]; - - channel_entry() : ChannelEntry { return this.props.channel; } - - render() { - return ( -
- - - -
- ); - } -} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/colors.css b/shared/js/ui/channel-tree/colors.css deleted file mode 100644 index f48e716e..00000000 --- a/shared/js/ui/channel-tree/colors.css +++ /dev/null @@ -1,3 +0,0 @@ - - -/*# sourceMappingURL=colors.css.map */ diff --git a/shared/js/ui/channel-tree/colors.css.map b/shared/js/ui/channel-tree/colors.css.map deleted file mode 100644 index 6ee84d1b..00000000 --- a/shared/js/ui/channel-tree/colors.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"colors.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/colors.scss b/shared/js/ui/channel-tree/colors.scss deleted file mode 100644 index b12d3a2e..00000000 --- a/shared/js/ui/channel-tree/colors.scss +++ /dev/null @@ -1,3 +0,0 @@ - -$channel-tree-new-message-color: #a814147F; -$channel-tree-entry-color: #828282; \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.css b/shared/js/ui/channel-tree/tree.css deleted file mode 100644 index 3f5f7faf..00000000 --- a/shared/js/ui/channel-tree/tree.css +++ /dev/null @@ -1,38 +0,0 @@ -/* Some general browser helpers */ -.tree-container .tree .entry .depth-filler { - font-size: 16px; - flex-shrink: 0; - flex-grow: 0; -} -.tree-container .marker-text-unread { - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 1px; - background-color: #a814147F; - opacity: 1; - -moz-transition: opacity 0.25s; - -o-transition: opacity 0.25s; - -webkit-transition: opacity 0.25s; - transition: opacity 0.25s; -} -.tree-container .marker-text-unread:before { - content: ""; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 24px; - background: -moz-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); - /* FF3.6-15 */ - background: -webkit-linear-gradient(left, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); - /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(to right, rgba(168, 20, 20, 0.18) 0%, rgba(168, 20, 20, 0) 100%); - /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ -} -.tree-container .marker-text-unread.hidden { - opacity: 0; -} - -/*# sourceMappingURL=tree.css.map */ diff --git a/shared/js/ui/channel-tree/tree.css.map b/shared/js/ui/channel-tree/tree.css.map deleted file mode 100644 index ab305210..00000000 --- a/shared/js/ui/channel-tree/tree.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["../../../css/static/mixin.scss","tree.scss"],"names":[],"mappings":"AAAA;ACQY;EACI;EAEA;EACA;;AAKZ;EACI;EACA;EACA;EACA;EAEA;EACA;EAEA;EDvBP,iBC4CO;ED3CP,eC2CO;ED1CP,oBC0CO;EDzCP,YCyCO;;AAnBA;EACI;EACA;EAEA;EACA;EACA;EAEA;EAEA;AAAyF;EACzF;AAA2F;EAC3F;AAAuF;;AAG3F;EACI","file":"tree.css"} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.scss b/shared/js/ui/channel-tree/tree.scss deleted file mode 100644 index b54c059a..00000000 --- a/shared/js/ui/channel-tree/tree.scss +++ /dev/null @@ -1,50 +0,0 @@ -@import "../../../css/static/mixin"; -@import "../../../css/static/properties"; - -.tree-container { - .tree { - - .entry { - - .depth-filler { - font-size: 16px; - - flex-shrink: 0; - flex-grow: 0; - } - } - } - - .marker-text-unread { - position: absolute; - left: 0; - top: 0; - bottom: 0; - - width: 1px; - background-color: #a814147F; - - opacity: 1; - - &:before { - content: ''; - position: absolute; - - left: 0; - top: 0; - bottom: 0; - - width: 24px; - - background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */ - background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ - } - - &.hidden { - opacity: 0; - } - - @include transition(opacity $button_hover_animation_time); - } -} \ No newline at end of file diff --git a/shared/js/ui/channel-tree/tree.tsx b/shared/js/ui/channel-tree/tree.tsx deleted file mode 100644 index ea705ddf..00000000 --- a/shared/js/ui/channel-tree/tree.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import * as React from "react"; - -export class TreeView { - -} \ No newline at end of file diff --git a/shared/js/channel-tree/channel.ts b/shared/js/ui/channel.ts similarity index 99% rename from shared/js/channel-tree/channel.ts rename to shared/js/ui/channel.ts index 52da4cee..1a510f05 100644 --- a/shared/js/channel-tree/channel.ts +++ b/shared/js/ui/channel.ts @@ -1,15 +1,12 @@ /// /// -import {ChannelTree} from "./view"; -import {ClientEntry} from "./client"; - -export enum ChannelType { +enum ChannelType { PERMANENT, SEMI_PERMANENT, TEMPORARY } -export namespace ChannelType { +namespace ChannelType { export function normalize(mode: ChannelType) { let value: string = ChannelType[mode]; value = value.toLowerCase(); @@ -17,13 +14,13 @@ export namespace ChannelType { } } -export enum ChannelSubscribeMode { +enum ChannelSubscribeMode { SUBSCRIBED, UNSUBSCRIBED, INHERITED } -export class ChannelProperties { +class ChannelProperties { channel_order: number = 0; channel_name: string = ""; channel_name_phonetic: string = ""; @@ -58,7 +55,7 @@ export class ChannelProperties { channel_conversation_history_length: number = -1; } -export class ChannelEntry { +class ChannelEntry { channelTree: ChannelTree; channelId: number; parent?: ChannelEntry; diff --git a/shared/js/channel-tree/client.ts b/shared/js/ui/client.ts similarity index 99% rename from shared/js/channel-tree/client.ts rename to shared/js/ui/client.ts index 7010a7a5..d898f8f7 100644 --- a/shared/js/channel-tree/client.ts +++ b/shared/js/ui/client.ts @@ -1,11 +1,8 @@ /// -/// -/// +/// +/// -import {ChannelEntry} from "./channel"; -import {ChannelTree} from "./view"; - -export enum ClientType { +enum ClientType { CLIENT_VOICE, CLIENT_QUERY, CLIENT_INTERNAL, @@ -14,7 +11,7 @@ export enum ClientType { CLIENT_UNDEFINED } -export class ClientProperties { +class ClientProperties { client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type client_type_exact: ClientType = ClientType.CLIENT_VOICE; @@ -60,7 +57,7 @@ export class ClientProperties { client_is_priority_speaker: boolean = false; } -export class ClientConnectionInfo { +class ClientConnectionInfo { connection_bandwidth_received_last_minute_control: number = -1; connection_bandwidth_received_last_minute_keepalive: number = -1; connection_bandwidth_received_last_minute_speech: number = -1; @@ -112,7 +109,7 @@ export class ClientConnectionInfo { connection_client_port: number = -1; } -export class ClientEntry { +class ClientEntry { readonly events: events.Registry; protected _clientId: number; @@ -1126,7 +1123,7 @@ export class ClientEntry { } } -export class LocalClientEntry extends ClientEntry { +class LocalClientEntry extends ClientEntry { handle: ConnectionHandler; private renaming: boolean; @@ -1235,7 +1232,7 @@ export class LocalClientEntry extends ClientEntry { } } -export class MusicClientProperties extends ClientProperties { +class MusicClientProperties extends ClientProperties { player_state: number = 0; player_volume: number = 0; @@ -1267,7 +1264,7 @@ export class MusicClientProperties extends ClientProperties { } */ -export class SongInfo { +class SongInfo { song_id: number = 0; song_url: string = ""; song_invoker: number = 0; @@ -1280,7 +1277,7 @@ export class SongInfo { song_length: number = 0; } -export class MusicClientPlayerInfo extends SongInfo { +class MusicClientPlayerInfo extends SongInfo { bot_id: number = 0; player_state: number = 0; @@ -1293,7 +1290,7 @@ export class MusicClientPlayerInfo extends SongInfo { player_description: string = ""; } -export class MusicClientEntry extends ClientEntry { +class MusicClientEntry extends ClientEntry { private _info_promise: Promise; private _info_promise_age: number = 0; private _info_promise_resolve: any; diff --git a/shared/js/ui/client_move.ts b/shared/js/ui/client_move.ts index c99f7c61..5ccfa8a1 100644 --- a/shared/js/ui/client_move.ts +++ b/shared/js/ui/client_move.ts @@ -1,4 +1,6 @@ -export class ClientMover { +/// + +class ClientMover { static readonly listener_root = $(document); static readonly move_element = $("#mouse-move"); readonly channel_tree: ChannelTree; diff --git a/shared/js/ui/elements/context_divider.ts b/shared/js/ui/elements/context_divider.ts index c0426898..3ec65814 100644 --- a/shared/js/ui/elements/context_divider.ts +++ b/shared/js/ui/elements/context_divider.ts @@ -1,14 +1,10 @@ -import {settings} from "../../settings"; -import {log, LogCategory} from "../../log"; - -declare const $: any; interface JQuery { dividerfy() : this; } if(!$.fn.dividerfy) { $.fn.dividerfy = function(this: JQuery) : JQuery { - (this as any).find(".container-seperator").each(function (this: T) { + this.find(".container-seperator").each(function (this: T) { if(!this.previousElementSibling) return; if(!this.nextElementSibling) return; diff --git a/shared/js/ui/elements/context_menu.ts b/shared/js/ui/elements/context_menu.ts index c3543e2e..f56eb023 100644 --- a/shared/js/ui/elements/context_menu.ts +++ b/shared/js/ui/elements/context_menu.ts @@ -1,4 +1,4 @@ -export namespace contextmenu { +namespace contextmenu { export interface MenuEntry { callback?: () => void; type: MenuEntryType; diff --git a/shared/js/ui/elements/modal.ts b/shared/js/ui/elements/modal.ts index 68838f63..a0030309 100644 --- a/shared/js/ui/elements/modal.ts +++ b/shared/js/ui/elements/modal.ts @@ -1,13 +1,13 @@ -import {KeyCode} from "../../PPTListener"; +/// -export enum ElementType { +enum ElementType { HEADER, BODY, FOOTER } -export type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; -export const ModalFunctions = { +type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; +const ModalFunctions = { divify: function (val: JQuery) { if(val.length > 1) return $.spawn("div").append(val); @@ -54,7 +54,7 @@ export const ModalFunctions = { } }; -export class ModalProperties { +class ModalProperties { template?: string; header: BodyCreator = () => "HEADER"; body: BodyCreator = () => "BODY"; @@ -89,7 +89,7 @@ export class ModalProperties { full_size?: boolean = false; } -export namespace modal { +namespace modal { export function initialize_modals() { register_global_events(); } @@ -184,7 +184,7 @@ let _global_modal_count = 0; let _global_modal_last: HTMLElement; let _global_modal_last_time: number; -export class Modal { +class Modal { private _htmlTag: JQuery; properties: ModalProperties; shown: boolean; @@ -296,11 +296,11 @@ export class Modal { } } -export function createModal(data: ModalProperties | any) : Modal { +function createModal(data: ModalProperties | any) : Modal { return new Modal(ModalFunctions.warpProperties(data)); } -export class InputModalProperties extends ModalProperties { +class InputModalProperties extends ModalProperties { maxLength?: number; field_title?: string; @@ -310,7 +310,7 @@ export class InputModalProperties extends ModalProperties { error_message?: string; } -export function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { +function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { props = ModalFunctions.warpProperties(props); props.template_properties || (props.template_properties = {}); props.template_properties.field_title = props.field_title; @@ -370,7 +370,7 @@ export function createInputModal(headMessage: BodyCreator, question: BodyCreator return modal; } -export function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; @@ -382,7 +382,7 @@ export function createErrorModal(header: BodyCreator, message: BodyCreator, prop return modal; } -export function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; @@ -393,3 +393,73 @@ export function createInfoModal(header: BodyCreator, message: BodyCreator, props modal.htmlTag.find(".modal-body").addClass("modal-info"); return modal; } + +/* extend jquery */ + +interface ModalElements { + header?: BodyCreator; + body?: BodyCreator; + footer?: BodyCreator; +} + +interface JQuery { + modalize(entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal; +} + +$.fn.modalize = function (this: JQuery, entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal { + properties = properties || {} as ModalProperties; + entry_callback = entry_callback || ((a,b,c) => undefined); + + let tag_modal = this[0].tagName.toLowerCase() == "modal" ? this : undefined; /* TODO may throw exception? */ + + let tag_head = tag_modal ? tag_modal.find("modal-header") : ModalFunctions.jqueriefy(properties.header); + let tag_body = tag_modal ? tag_modal.find("modal-body") : this; + let tag_footer = tag_modal ? tag_modal.find("modal-footer") : ModalFunctions.jqueriefy(properties.footer); + + const result = entry_callback(tag_head as any, tag_body, tag_footer as any) || {}; + properties.header = result.header || tag_head; + properties.body = result.body || tag_body; + properties.footer = result.footer || tag_footer; + return createModal(properties); +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shared/js/ui/elements/net_graph.ts b/shared/js/ui/elements/net_graph.ts index a605d1cc..5e440872 100644 --- a/shared/js/ui/elements/net_graph.ts +++ b/shared/js/ui/elements/net_graph.ts @@ -1,4 +1,4 @@ -export namespace net.graph { +namespace net.graph { export type Entry = { timestamp: number; diff --git a/shared/js/ui/elements/slider.ts b/shared/js/ui/elements/slider.ts index 83c2f77c..0e956585 100644 --- a/shared/js/ui/elements/slider.ts +++ b/shared/js/ui/elements/slider.ts @@ -1,4 +1,4 @@ -export interface SliderOptions { +interface SliderOptions { min_value?: number; max_value?: number; initial_value?: number; @@ -8,11 +8,11 @@ export interface SliderOptions { value_field?: JQuery | JQuery[]; } -export interface Slider { +interface Slider { value(value?: number) : number; } -export function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { +function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { options = Object.assign( { initial_value: 0, min_value: 0, diff --git a/shared/js/ui/elements/tab.ts b/shared/js/ui/elements/tab.ts new file mode 100644 index 00000000..fb34577c --- /dev/null +++ b/shared/js/ui/elements/tab.ts @@ -0,0 +1,161 @@ +/// + +interface JQuery { + asTabWidget(copy?: boolean) : JQuery; + tabify(copy?: boolean) : this; + + changeElementType(type: string) : JQuery; +} + + +if(typeof (customElements) !== "undefined") { + try { + class X_Tab extends HTMLElement {} + class X_Entry extends HTMLElement {} + class X_Tag extends HTMLElement {} + class X_Content extends HTMLElement {} + + customElements.define('x-tab', X_Tab, { extends: 'div' }); + customElements.define('x-entry', X_Entry, { extends: 'div' }); + customElements.define('x-tag', X_Tag, { extends: 'div' }); + customElements.define('x-content', X_Content, { extends: 'div' }); + } catch(error) { + console.warn("failed to define costum elements"); + } +} else { + console.warn(tr("Could not defied tab customElements!")); +} + +var TabFunctions = { + tabify(template: JQuery, copy: boolean = true) : JQuery { + console.log("Tabify: copy=" + copy); + console.log(template); + + let tag = $.spawn("div"); + tag.addClass("tab"); + + let header = $.spawn("div"); + header.addClass("tab-header"); + + let content = $.spawn("div"); + content.addClass("tab-content"); + + content.append($.spawn("div").addClass("height-watcher")); + + let silentContent = $.spawn("div"); + silentContent.addClass("tab-content-invisible"); + + /* add some kind of min height */ + const update_height = () => { + const height_watcher = tag.find("> .tab-content .height-watcher"); + const entries: JQuery = tag.find("> .tab-content-invisible x-content, > .tab-content x-content"); + console.error(entries); + let max_height = 0; + + entries.each((_, _e) => { + const entry = $(_e); + const height = entry.visible_height(); + if(height > max_height) + max_height = height; + }); + + height_watcher.css('min-height', max_height + "px"); + tag.find(".window-resize-listener").trigger('resize'); + }; + + template.find("x-entry").each( (_, _entry) => { + const entry = $(_entry); + + const tag_header = $.spawn("div").addClass("entry"); + const tag_content = copy ? entry.find("x-content").clone(true, true) : entry.find("x-content"); + + { + const header_tag = entry.find("x-tag"); + const header_data = copy ? header_tag.contents().clone(true, true) : header_tag.contents(); + + if(header_tag.attr("x-entry-class")) + tag_header.addClass(header_tag.attr("x-entry-class")); + if(header_tag.attr("x-entry-id")) + tag_header.attr("x-id", header_tag.attr("x-entry-id")); + + tag_header.append(header_data); + + /* listener if the tab might got removed */ + tag_header.addClass("window-resize-listener"); + tag_header.on('resize', event => { + if(!tag_header.is(':visible') && tag_header.hasClass('selected')) { + let element = tag_header.next('.entry:visible'); + if(element.length == 0) + element = tag_header.prev('.entry:visible'); + if(element.length == 0) { + tag_header.removeClass("selected"); + tag_content.hide(); + } else { + element.first().trigger('click'); + } + console.log("Next: %o", tag_header.next('.entry:visible')); + console.log("prev: %o", tag_header.prev('.entry:visible')); + } + }); + } + + content.append(tag_content.hide()); + + tag_header.on("click", () => { + if(tag_header.hasClass("selected")) return; + + tag.find(".tab-header .selected").removeClass("selected"); + tag_header.addClass("selected"); + + content.find("> x-content").hide(); + /* don't show many nodes at once */ + let entries = tag_content.find(".tab-show-partitional"); + entries.hide(); + const show_next = index => { + console.log("Show " + index); + if(index >= entries.length) return; + entries.eq(index).show(); + + setTimeout(show_next.bind(undefined, index + 1), 0); + }; + show_next(0); + + tag_content.trigger('show'); + tag_content.show(); + }); + + console.log(this); + header.append(tag_header); + }); + + setTimeout(() => header.find(".entry").first().trigger("click"), 0); + + tag.append(header); + tag.append(content); + tag.append(silentContent); + + tag.on('tab.resize', update_height); + return tag; + } +}; + +if(!$.fn.asTabWidget) { + $.fn.asTabWidget = function (copy?: boolean) : JQuery { + if($(this).prop("tagName") == "X-TAB") + return TabFunctions.tabify($(this), typeof(copy) === "boolean" ? copy : true); + else { + throw "Invalid tag! " + $(this).prop("tagName"); + } + } +} + +if(!$.fn.tabify) { + $.fn.tabify = function (this: JQuery, copy?: boolean) { + const wrapped_tag = $.spawn("div").append(this); + wrapped_tag.find("x-tab").each((_, _element) => { + const element = $(_element); + element.replaceWith(element.asTabWidget(copy)); + }); + return wrapped_tag.children(); + } +} \ No newline at end of file diff --git a/shared/js/ui/elements/tooltip.ts b/shared/js/ui/elements/tooltip.ts index 89627bbe..b64f96b5 100644 --- a/shared/js/ui/elements/tooltip.ts +++ b/shared/js/ui/elements/tooltip.ts @@ -1,8 +1,8 @@ -export function tooltip(entry: JQuery) { +function tooltip(entry: JQuery) { return tooltip.initialize(entry); } -export namespace tooltip { +namespace tooltip { let _global_tooltip: JQuery; export type Handle = { show(); diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index ff205531..b2d6dad3 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -13,27 +13,12 @@ client_away_message Value: '' */ -import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler"; -import {top_menu} from "./MenuBar"; -import {Settings, settings} from "../../settings"; -import {createErrorModal, createInfoModal, createInputModal} from "../elements/modal"; -import {default_recorder} from "../../voice/RecorderProfile"; -import {sound, Sound} from "../../sound/Sounds"; -import {Modals} from "../modal/ModalConnect"; -import {Modal as ModalsS} from "../modal/ModalSettings"; -import {log} from "./server_log"; -import {MessageHelper} from "./chat"; -import {CommandResult} from "../../connection/ServerConnectionDeclaration"; -import {PermissionType} from "../../permission/PermissionManager"; -import {bookmarks} from "../../bookmarks"; -import {contextmenu} from "../elements/context_menu"; +let control_bar: ControlBar; /* global variable to access the control bar */ -export let control_bar: ControlBar; /* global variable to access the control bar */ - -export type MicrophoneState = "disabled" | "muted" | "enabled"; -export type HeadphoneState = "muted" | "enabled"; -export type AwayState = "away-global" | "away" | "online"; -export class ControlBar { +type MicrophoneState = "disabled" | "muted" | "enabled"; +type HeadphoneState = "muted" | "enabled"; +type AwayState = "away-global" | "away" | "online"; +class ControlBar { private _button_away_active: AwayState; private _button_microphone: MicrophoneState; private _button_speakers: HeadphoneState; @@ -436,7 +421,7 @@ export class ControlBar { } private on_open_settings() { - ModalsS.spawnSettingsModal(); + Modals.spawnSettingsModal(); } private on_open_connect() { diff --git a/shared/js/ui/frames/MenuBar.ts b/shared/js/ui/frames/MenuBar.ts index 10fb83cb..04885afa 100644 --- a/shared/js/ui/frames/MenuBar.ts +++ b/shared/js/ui/frames/MenuBar.ts @@ -1,12 +1,4 @@ -import {bookmarks} from "../../bookmarks"; -import {Modals} from "../modal/ModalBookmarks"; -import {DisconnectReason} from "../../ConnectionHandler"; -import {control_bar} from "./ControlBar"; -import {createErrorModal} from "../elements/modal"; -import {PermissionType} from "../../permission/PermissionManager"; -import {Sound} from "../../sound/Sounds"; - -export namespace top_menu { +namespace top_menu { export interface HRItem { } export interface MenuItem { diff --git a/shared/js/ui/frames/chat.ts b/shared/js/ui/frames/chat.ts index c0f38ee0..9de0d55b 100644 --- a/shared/js/ui/frames/chat.ts +++ b/shared/js/ui/frames/chat.ts @@ -1,6 +1,4 @@ -import {log, LogCategory} from "../../log"; - -export enum ChatType { +enum ChatType { GENERAL, SERVER, CHANNEL, @@ -8,7 +6,7 @@ export enum ChatType { } declare const xbbcode: any; -export namespace MessageHelper { +namespace MessageHelper { export function htmlEscape(message: string) : string[] { const div = document.createElement('div'); div.innerText = message; diff --git a/shared/js/ui/frames/chat_frame.ts b/shared/js/ui/frames/chat_frame.ts index 6921f9b4..b871ce95 100644 --- a/shared/js/ui/frames/chat_frame.ts +++ b/shared/js/ui/frames/chat_frame.ts @@ -1,11 +1,5 @@ /* the bar on the right with the chats (Channel & Client) */ -import {ChannelEntry} from "../../channel-tree/channel"; -import {Modals} from "../modal/ModalMusicManage"; -import {MessageHelper} from "./chat"; -import {ServerEntry} from "../../channel-tree/server"; -import {ConnectionHandler} from "../../ConnectionHandler"; - -export namespace chat { +namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/connection_handlers.ts b/shared/js/ui/frames/connection_handlers.ts index be3347f2..4d9fb936 100644 --- a/shared/js/ui/frames/connection_handlers.ts +++ b/shared/js/ui/frames/connection_handlers.ts @@ -1,11 +1,7 @@ -import {ConnectionHandler, DisconnectReason} from "../../ConnectionHandler"; -import {Settings, settings} from "../../settings"; -import {control_bar} from "./ControlBar"; -import {top_menu} from "./MenuBar"; -export let server_connections: ServerConnectionManager; +let server_connections: ServerConnectionManager; -export class ServerConnectionManager { +class ServerConnectionManager { private connection_handlers: ConnectionHandler[] = []; private active_handler: ConnectionHandler | undefined; diff --git a/shared/js/ui/frames/hostbanner.ts b/shared/js/ui/frames/hostbanner.ts index 6dffddb5..2b0466d1 100644 --- a/shared/js/ui/frames/hostbanner.ts +++ b/shared/js/ui/frames/hostbanner.ts @@ -1,8 +1,4 @@ -import {ConnectionHandler} from "../../ConnectionHandler"; -import {Settings, settings} from "../../settings"; -import {log, LogCategory} from "../../log"; - -export class Hostbanner { +class Hostbanner { readonly html_tag: JQuery; readonly client: ConnectionHandler; diff --git a/shared/js/ui/frames/image_preview.ts b/shared/js/ui/frames/image_preview.ts index ee69ca3f..af46412f 100644 --- a/shared/js/ui/frames/image_preview.ts +++ b/shared/js/ui/frames/image_preview.ts @@ -1,4 +1,4 @@ -export namespace image_preview { +namespace image_preview { let preview_overlay: JQuery; let container_image: JQuery; let button_open_in_browser: JQuery; diff --git a/shared/js/ui/frames/server_log.ts b/shared/js/ui/frames/server_log.ts index 8107611a..d6e29f22 100644 --- a/shared/js/ui/frames/server_log.ts +++ b/shared/js/ui/frames/server_log.ts @@ -1,571 +1,566 @@ -import {ConnectionHandler, ViewReasonId} from "../../ConnectionHandler"; -import {PermissionInfo} from "../../permission/PermissionManager"; -import {htmltags} from "../htmltags"; -import {MessageHelper} from "./chat"; -import {i18n} from "../../i18n/localize"; +namespace log { + export namespace server { + export enum Type { + CONNECTION_BEGIN = "connection_begin", + CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", + CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", + CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", + CONNECTION_LOGIN = "connection_login", + CONNECTION_CONNECTED = "connection_connected", + CONNECTION_FAILED = "connection_failed", -export namespace server { - export enum Type { - CONNECTION_BEGIN = "connection_begin", - CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", - CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", - CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", - CONNECTION_LOGIN = "connection_login", - CONNECTION_CONNECTED = "connection_connected", - CONNECTION_FAILED = "connection_failed", + DISCONNECTED = "disconnected", - DISCONNECTED = "disconnected", + CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", + CONNECTION_COMMAND_ERROR = "connection_command_error", - CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", - CONNECTION_COMMAND_ERROR = "connection_command_error", + GLOBAL_MESSAGE = "global_message", - GLOBAL_MESSAGE = "global_message", + SERVER_WELCOME_MESSAGE = "server_welcome_message", + SERVER_HOST_MESSAGE = "server_host_message", + SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", - SERVER_WELCOME_MESSAGE = "server_welcome_message", - SERVER_HOST_MESSAGE = "server_host_message", - SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", + SERVER_CLOSED = "server_closed", + SERVER_BANNED = "server_banned", + SERVER_REQUIRES_PASSWORD = "server_requires_password", - SERVER_CLOSED = "server_closed", - SERVER_BANNED = "server_banned", - SERVER_REQUIRES_PASSWORD = "server_requires_password", + CLIENT_VIEW_ENTER = "client_view_enter", + CLIENT_VIEW_LEAVE = "client_view_leave", + CLIENT_VIEW_MOVE = "client_view_move", - CLIENT_VIEW_ENTER = "client_view_enter", - CLIENT_VIEW_LEAVE = "client_view_leave", - CLIENT_VIEW_MOVE = "client_view_move", + CLIENT_NICKNAME_CHANGED = "client_nickname_changed", + CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", - CLIENT_NICKNAME_CHANGED = "client_nickname_changed", - CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", + CLIENT_SERVER_GROUP_ADD = "client_server_group_add", + CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", + CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", - CLIENT_SERVER_GROUP_ADD = "client_server_group_add", - CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", - CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", + CHANNEL_CREATE = "channel_create", + CHANNEL_DELETE = "channel_delete", - CHANNEL_CREATE = "channel_create", - CHANNEL_DELETE = "channel_delete", + ERROR_CUSTOM = "error_custom", + ERROR_PERMISSION = "error_permission", - ERROR_CUSTOM = "error_custom", - ERROR_PERMISSION = "error_permission", - - RECONNECT_SCHEDULED = "reconnect_scheduled", - RECONNECT_EXECUTE = "reconnect_execute", - RECONNECT_CANCELED = "reconnect_canceled" - } - - export namespace base { - export type Client = { - client_unique_id: string; - client_name: string; - client_id: number; - } - export type Channel = { - channel_id: number; - channel_name: string; - } - export type Server = { - server_name: string; - server_unique_id: string; - } - export type ServerAddress = { - server_hostname: string; - server_port: number; - } - } - - export namespace event { - export type GlobalMessage = { - sender: base.Client; - message: string; - } - export type ConnectBegin = { - address: base.ServerAddress; - client_nickname: string; - } - export type ErrorCustom = { - message: string; + RECONNECT_SCHEDULED = "reconnect_scheduled", + RECONNECT_EXECUTE = "reconnect_execute", + RECONNECT_CANCELED = "reconnect_canceled" } - export type ReconnectScheduled = { - timeout: number; + export namespace base { + export type Client = { + client_unique_id: string; + client_name: string; + client_id: number; + } + export type Channel = { + channel_id: number; + channel_name: string; + } + export type Server = { + server_name: string; + server_unique_id: string; + } + export type ServerAddress = { + server_hostname: string; + server_port: number; + } } - export type ReconnectCanceled = { } - export type ReconnectExecute = { } - - export type ErrorPermission = { - permission: PermissionInfo; - } - - export type WelcomeMessage = { - message: string; - } - - export type HostMessageDisconnect = { - message: string; - } - - export type ClientMove = { - channel_from?: base.Channel; - channel_from_own: boolean; - - channel_to?: base.Channel; - channel_to_own: boolean; - - client: base.Client; - client_own: boolean; - - invoker?: base.Client; - - message?: string; - reason: ViewReasonId; - } - - export type ClientEnter = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ClientLeave = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ChannelCreate = { - creator: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ChannelDelete = { - deleter: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ConnectionConnected = { - own_client: base.Client; - } - export type ConnectionFailed = {}; - export type ConnectionLogin = {} - export type ConnectionHostnameResolve = {}; - export type ConnectionHostnameResolved = { - address: base.ServerAddress; - } - export type ConnectionHostnameResolveError = { - message: string; - } - - export type ConnectionVoiceSetupFailed = { - reason: string; - reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ - } - - export type ConnectionCommandError = { - error: any; - } - - export type ClientNicknameChanged = { - own_client: boolean; - - client: base.Client; - - old_name: string; - new_name: string; - } - - export type ClientNicknameChangeFailed = { - reason: string; - } - - export type ServerClosed = { - message: string; - } - - export type ServerRequiresPassword = {} - - export type ServerBanned = { - message: string; - time: number; - - invoker: base.Client; - } - } - - export type LogMessage = { - type: Type; - timestamp: number; - data: any; - } - - export interface TypeInfo { - "connection_begin" : event.ConnectBegin; - "global_message": event.GlobalMessage; - - "error_custom": event.ErrorCustom; - "error_permission": event.ErrorPermission; - - "connection_hostname_resolved": event.ConnectionHostnameResolved; - "connection_hostname_resolve": event.ConnectionHostnameResolve; - "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; - "connection_failed": event.ConnectionFailed; - "connection_login": event.ConnectionLogin; - "connection_connected": event.ConnectionConnected; - "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; - "connection_command_error": event.ConnectionCommandError; - - "reconnect_scheduled": event.ReconnectScheduled; - "reconnect_canceled": event.ReconnectCanceled; - "reconnect_execute": event.ReconnectExecute; - - "server_welcome_message": event.WelcomeMessage; - "server_host_message": event.WelcomeMessage; - "server_host_message_disconnect": event.HostMessageDisconnect; - - "server_closed": event.ServerClosed; - "server_requires_password": event.ServerRequiresPassword; - "server_banned": event.ServerBanned; - - "client_view_enter": event.ClientEnter; - "client_view_move": event.ClientMove; - "client_view_leave": event.ClientLeave; - - "client_nickname_change_failed": event.ClientNicknameChangeFailed, - "client_nickname_changed": event.ClientNicknameChanged, - - "channel_create": event.ChannelCreate; - "channel_delete": event.ChannelDelete; - - "disconnected": any; - } - - export type MessageBuilderOptions = {}; - export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; - - export const MessageBuilders: {[key: string]: MessageBuilder} = { - "error_custom": (data: event.ErrorCustom, options) => { - return [$.spawn("div").addClass("log-error").text(data.message)] - } - }; -} - -export class ServerLog { - private readonly handle: ConnectionHandler; - private history_length: number = 100; - - private _log: server.LogMessage[] = []; - private _html_tag: JQuery; - private _log_container: JQuery; - private auto_follow: boolean; /* automatic scroll to bottom */ - private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ - - constructor(handle: ConnectionHandler) { - this.handle = handle; - this.auto_follow = true; - - this._html_tag = $.spawn("div").addClass("container-log"); - this._log_container = $.spawn("div").addClass("container-messages"); - this._log_container.appendTo(this._html_tag); - - this._html_tag.on('scroll', event => { - if(Date.now() - this._ignore_event < 100) { - this._ignore_event = 0; - return; + export namespace event { + export type GlobalMessage = { + sender: base.Client; + message: string; + } + export type ConnectBegin = { + address: base.ServerAddress; + client_nickname: string; + } + export type ErrorCustom = { + message: string; } - this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; - }); - } + export type ReconnectScheduled = { + timeout: number; + } - log(type: T, data: server.TypeInfo[T]) { - const event = { - data: data, - timestamp: Date.now(), - type: type as any + export type ReconnectCanceled = { } + export type ReconnectExecute = { } + + export type ErrorPermission = { + permission: PermissionInfo; + } + + export type WelcomeMessage = { + message: string; + } + + export type HostMessageDisconnect = { + message: string; + } + + export type ClientMove = { + channel_from?: base.Channel; + channel_from_own: boolean; + + channel_to?: base.Channel; + channel_to_own: boolean; + + client: base.Client; + client_own: boolean; + + invoker?: base.Client; + + message?: string; + reason: ViewReasonId; + } + + export type ClientEnter = { + channel_from?: base.Channel; + channel_to?: base.Channel; + + client: base.Client; + invoker?: base.Client; + + message?: string; + own_channel: boolean; + + reason: ViewReasonId; + ban_time?: number; + } + + export type ClientLeave = { + channel_from?: base.Channel; + channel_to?: base.Channel; + + client: base.Client; + invoker?: base.Client; + + message?: string; + own_channel: boolean; + + reason: ViewReasonId; + ban_time?: number; + } + + export type ChannelCreate = { + creator: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ChannelDelete = { + deleter: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ConnectionConnected = { + own_client: base.Client; + } + export type ConnectionFailed = {}; + export type ConnectionLogin = {} + export type ConnectionHostnameResolve = {}; + export type ConnectionHostnameResolved = { + address: base.ServerAddress; + } + export type ConnectionHostnameResolveError = { + message: string; + } + + export type ConnectionVoiceSetupFailed = { + reason: string; + reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ + } + + export type ConnectionCommandError = { + error: any; + } + + export type ClientNicknameChanged = { + own_client: boolean; + + client: base.Client; + + old_name: string; + new_name: string; + } + + export type ClientNicknameChangeFailed = { + reason: string; + } + + export type ServerClosed = { + message: string; + } + + export type ServerRequiresPassword = {} + + export type ServerBanned = { + message: string; + time: number; + + invoker: base.Client; + } + } + + export type LogMessage = { + type: Type; + timestamp: number; + data: any; + } + + export interface TypeInfo { + "connection_begin" : event.ConnectBegin; + "global_message": event.GlobalMessage; + + "error_custom": event.ErrorCustom; + "error_permission": event.ErrorPermission; + + "connection_hostname_resolved": event.ConnectionHostnameResolved; + "connection_hostname_resolve": event.ConnectionHostnameResolve; + "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; + "connection_failed": event.ConnectionFailed; + "connection_login": event.ConnectionLogin; + "connection_connected": event.ConnectionConnected; + "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; + "connection_command_error": event.ConnectionCommandError; + + "reconnect_scheduled": event.ReconnectScheduled; + "reconnect_canceled": event.ReconnectCanceled; + "reconnect_execute": event.ReconnectExecute; + + "server_welcome_message": event.WelcomeMessage; + "server_host_message": event.WelcomeMessage; + "server_host_message_disconnect": event.HostMessageDisconnect; + + "server_closed": event.ServerClosed; + "server_requires_password": event.ServerRequiresPassword; + "server_banned": event.ServerBanned; + + "client_view_enter": event.ClientEnter; + "client_view_move": event.ClientMove; + "client_view_leave": event.ClientLeave; + + "client_nickname_change_failed": event.ClientNicknameChangeFailed, + "client_nickname_changed": event.ClientNicknameChanged, + + "channel_create": event.ChannelCreate; + "channel_delete": event.ChannelDelete; + + "disconnected": any; + } + + export type MessageBuilderOptions = {}; + export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; + + export const MessageBuilders: {[key: string]: MessageBuilder} = { + "error_custom": (data: event.ErrorCustom, options) => { + return [$.spawn("div").addClass("log-error").text(data.message)] + } }; - - this._log.push(event); - while(this._log.length > this.history_length) - this._log.pop_front(); - - this.append_log(event); } - html_tag() : JQuery { - return this._html_tag; - } + export class ServerLog { + private readonly handle: ConnectionHandler; + private history_length: number = 100; - destroy() { - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._log_container = undefined; + private _log: server.LogMessage[] = []; + private _html_tag: JQuery; + private _log_container: JQuery; + private auto_follow: boolean; /* automatic scroll to bottom */ + private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ - this._log = undefined; - } + constructor(handle: ConnectionHandler) { + this.handle = handle; + this.auto_follow = true; - private _scroll_task: number; + this._html_tag = $.spawn("div").addClass("container-log"); + this._log_container = $.spawn("div").addClass("container-messages"); + this._log_container.appendTo(this._html_tag); - private append_log(message: server.LogMessage) { - let container = $.spawn("div").addClass("log-message"); + this._html_tag.on('scroll', event => { + if(Date.now() - this._ignore_event < 100) { + this._ignore_event = 0; + return; + } - /* build timestamp */ - { - const num = number => ('00' + number).substr(-2); - const date = new Date(message.timestamp); - $.spawn("div") - .addClass("timestamp") - .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") - .appendTo(container); + this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; + }); } - /* build message data */ - { - const builder = server.MessageBuilders[message.type]; - if(!builder) { - MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); - } else { - const elements = builder(message.data, {}); - if(!elements || elements.length == 0) - return; /* discard message */ - container.append(...elements); + log(type: T, data: server.TypeInfo[T]) { + const event = { + data: data, + timestamp: Date.now(), + type: type as any + }; + + this._log.push(event); + while(this._log.length > this.history_length) + this._log.pop_front(); + + this.append_log(event); + } + + html_tag() : JQuery { + return this._html_tag; + } + + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._log_container = undefined; + + this._log = undefined; + } + + private _scroll_task: number; + + private append_log(message: server.LogMessage) { + let container = $.spawn("div").addClass("log-message"); + + /* build timestamp */ + { + const num = number => ('00' + number).substr(-2); + const date = new Date(message.timestamp); + $.spawn("div") + .addClass("timestamp") + .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") + .appendTo(container); } - } - this._ignore_event = Date.now(); - this._log_container.append(container); - /* max history messages! */ - const messages = this._log_container.children(); - let index = 0; - while(messages.length - index > this.history_length) - index++; - const hide_elements = messages.filter(idx => idx < index); - hide_elements.hide(250, () => hide_elements.remove()); + /* build message data */ + { + const builder = server.MessageBuilders[message.type]; + if(!builder) { + MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); + } else { + const elements = builder(message.data, {}); + if(!elements || elements.length == 0) + return; /* discard message */ + container.append(...elements); + } + } + this._ignore_event = Date.now(); + this._log_container.append(container); - if(this.auto_follow) { - clearTimeout(this._scroll_task); + /* max history messages! */ + const messages = this._log_container.children(); + let index = 0; + while(messages.length - index > this.history_length) + index++; + const hide_elements = messages.filter(idx => idx < index); + hide_elements.hide(250, () => hide_elements.remove()); - /* do not enforce a recalculate style here */ - this._scroll_task = setTimeout(() => { - this._html_tag.scrollTop(this._html_tag[0].scrollHeight); - this._scroll_task = 0; - }, 5) as any; + if(this.auto_follow) { + clearTimeout(this._scroll_task); + + /* do not enforce a recalculate style here */ + this._scroll_task = setTimeout(() => { + this._html_tag.scrollTop(this._html_tag[0].scrollHeight); + this._scroll_task = 0; + }, 5) as any; + } } } } /* impl of the parsers */ -export namespace server { - namespace impl { - import MessageBuilders = server.MessageBuilders; - import base = server.base; - import tra = i18n.tra; - const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ - client_unique_id: client.client_unique_id, - client_id: client.client_id, - client_name: client.client_name, - add_braces: braces - }); - const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ - channel_display_name: channel.channel_name, - channel_name: channel.channel_name, - channel_id: channel.channel_id, - add_braces: braces - }); +namespace log { + export namespace server { + namespace impl { + const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ + client_unique_id: client.client_unique_id, + client_id: client.client_id, + client_name: client.client_name, + add_braces: braces + }); + const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ + channel_display_name: channel.channel_name, + channel_name: channel.channel_name, + channel_id: channel.channel_id, + add_braces: braces + }); - MessageBuilders["connection_begin"] = (data: server.event.ConnectBegin, options) => { - return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); - }; + MessageBuilders["connection_begin"] = (data: event.ConnectBegin, options) => { + return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); + }; - MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname")); - MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); - MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); + MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname")); + MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); + MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); - MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in...")); - MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed.")); - MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); + MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in...")); + MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed.")); + MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); - MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { - return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); - }; + MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { + return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); + }; - MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { - return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); - }; + MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { + return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); + }; - MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { - if(data.reason == ViewReasonId.VREASON_SYSTEM) { - return undefined; - } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - /* client appeared */ - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); + MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { + if(data.reason == ViewReasonId.VREASON_SYSTEM) { + return undefined; + } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + /* client appeared */ + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); + } + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), + client_tag(data.client), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + if(data.channel_from) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), + client_tag(data.client), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } } - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), + return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; + }; + + MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { + if(data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker) ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), + } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), client_tag(data.client), - channel_tag(data.channel_to), - client_tag(data.invoker) + channel_tag(data.channel_from), + channel_tag(data.channel_to) ); - } - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "" ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), + } + return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; + }; + + MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { + if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { + return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { + return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), client_tag(data.client), - channel_tag(data.channel_to), + channel_tag(data.channel_from), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : "" ); + } else if(data.reason == ViewReasonId.VREASON_BAN) { + let duration = "permanently"; + if(data.ban_time) + duration = "for " + formatDate(data.ban_time); + return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"), + client_tag(data.client), + duration, + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { + return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); } - } - return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; - }; - MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { - if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to) - ); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } - return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; - }; + return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; + }; - MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { - if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { - return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { - return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), - client_tag(data.client), - channel_tag(data.channel_from), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else if(data.reason == ViewReasonId.VREASON_BAN) { - let duration = "permanently"; - if(data.ban_time) - duration = "for " + formatDate(data.ban_time); - return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"), - client_tag(data.client), - duration, - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { - return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); - } + MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; - return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; - }; + MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { + return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); + }; - MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; + MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { + if(data.own_client) { + return MessageHelper.formatMessage(tr("Nickname successfully changed.")); + } else { + return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); + } + }; - MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; + MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { + return []; /* we do not show global messages within log */ + }; - MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { - if(data.own_client) { - return MessageHelper.formatMessage(tr("Nickname successfully changed.")); - } else { - return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); - } - }; + MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server")); - MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { - return []; /* we do not show global messages within log */ - }; + MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { + return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now"))) + }; - MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server")); + MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { + return tra("Canceled reconnect.") + }; - MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { - return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now"))) - }; + MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { + return tra("Reconnecting...") + }; - MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { - return tra("Canceled reconnect.") - }; + MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { + let result: JQuery[]; - MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { - return tra("Reconnecting...") - }; + const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second")); + if(data.invoker.client_id > 0) { + if(data.message) + result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); + else + result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); + } else { + if(data.message) + result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); + else + result = tra("You've been banned from the server for {0}.", time); + } - MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { - let result: JQuery[]; - - const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second")); - if(data.invoker.client_id > 0) { - if(data.message) - result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); - else - result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); - } else { - if(data.message) - result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); - else - result = tra("You've been banned from the server for {0}.", time); - } - - return result.map(e => e.addClass("log-error")); - }; + return result.map(e => e.addClass("log-error")); + }; + } } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/chat_box.ts b/shared/js/ui/frames/side/chat_box.ts index 12474fcf..863f9335 100644 --- a/shared/js/ui/frames/side/chat_box.ts +++ b/shared/js/ui/frames/side/chat_box.ts @@ -1,4 +1,4 @@ -export namespace chat { +namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/chat_helper.ts b/shared/js/ui/frames/side/chat_helper.ts index 79e3bf55..c3e7396f 100644 --- a/shared/js/ui/frames/side/chat_helper.ts +++ b/shared/js/ui/frames/side/chat_helper.ts @@ -1,4 +1,4 @@ -export namespace chat { +namespace chat { export namespace helpers { //https://regex101.com/r/YQbfcX/2 //static readonly URL_REGEX = /^(?([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?(?:[^\s?]+)?)(?:\?(?\S+))?)?$/gm; diff --git a/shared/js/ui/frames/side/client_info.ts b/shared/js/ui/frames/side/client_info.ts index cacbed19..9847b42b 100644 --- a/shared/js/ui/frames/side/client_info.ts +++ b/shared/js/ui/frames/side/client_info.ts @@ -1,11 +1,4 @@ -import {ClientEntry, LocalClientEntry} from "../../../channel-tree/client"; -import {Modals} from "../../modal/ModalClientInfo"; -import {htmltags} from "../../htmltags"; -import {image_preview} from "../image_preview"; -import {i18n} from "../../../i18n/country"; -import {GroupManager} from "../../../permission/GroupManager"; - -export namespace chat { +namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/conversations.ts b/shared/js/ui/frames/side/conversations.ts index 5351a673..3c45863a 100644 --- a/shared/js/ui/frames/side/conversations.ts +++ b/shared/js/ui/frames/side/conversations.ts @@ -1,10 +1,4 @@ -import {htmltags} from "../../htmltags"; -import {MessageHelper} from "../chat"; -import {CommandResult, ErrorID} from "../../../connection/ServerConnectionDeclaration"; -import {log, LogCategory} from "../../../log"; -import {createErrorModal} from "../../elements/modal"; - -export namespace chat { +namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/music_info.ts b/shared/js/ui/frames/side/music_info.ts index 3b79e309..41cbebbc 100644 --- a/shared/js/ui/frames/side/music_info.ts +++ b/shared/js/ui/frames/side/music_info.ts @@ -1,7 +1,6 @@ -import {MusicClientEntry} from "../../../channel-tree/client"; -import {image_preview} from "../image_preview"; +namespace chat { + import PlayerState = connection.voice.PlayerState; -export namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/frames/side/private_conversations.ts b/shared/js/ui/frames/side/private_conversations.ts index 08edd3ba..f85ffe7c 100644 --- a/shared/js/ui/frames/side/private_conversations.ts +++ b/shared/js/ui/frames/side/private_conversations.ts @@ -1,9 +1,5 @@ /* the bar on the right with the chats (Channel & Client) */ -import {log, LogCategory} from "../../../log"; -import {htmltags} from "../../htmltags"; -import {MessageHelper} from "../chat"; - -export namespace chat { +namespace chat { declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; diff --git a/shared/js/ui/htmltags.ts b/shared/js/ui/htmltags.ts index 76c98159..b208043f 100644 --- a/shared/js/ui/htmltags.ts +++ b/shared/js/ui/htmltags.ts @@ -1,4 +1,4 @@ -export namespace htmltags { +namespace htmltags { let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0}; function initialize() { diff --git a/shared/js/ui/modal/ModalAbout.ts b/shared/js/ui/modal/ModalAbout.ts index 518e5a3a..39207f1c 100644 --- a/shared/js/ui/modal/ModalAbout.ts +++ b/shared/js/ui/modal/ModalAbout.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { function format_date(date: number) { const d = new Date(date); diff --git a/shared/js/ui/modal/ModalAvatar.ts b/shared/js/ui/modal/ModalAvatar.ts index e877f45f..c5fcac24 100644 --- a/shared/js/ui/modal/ModalAvatar.ts +++ b/shared/js/ui/modal/ModalAvatar.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { //TODO: Test if we could render this image and not only the browser by knowing the type. export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) { const modal = createModal({ diff --git a/shared/js/ui/modal/ModalAvatarList.ts b/shared/js/ui/modal/ModalAvatarList.ts index beca0f7e..021430f2 100644 --- a/shared/js/ui/modal/ModalAvatarList.ts +++ b/shared/js/ui/modal/ModalAvatarList.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { const avatar_to_uid = (id: string) => { const buffer = new Uint8Array(id.length / 2); for(let index = 0; index < id.length; index += 2) { diff --git a/shared/js/ui/modal/ModalBanClient.ts b/shared/js/ui/modal/ModalBanClient.ts index 24a47d6b..bb7b0627 100644 --- a/shared/js/ui/modal/ModalBanClient.ts +++ b/shared/js/ui/modal/ModalBanClient.ts @@ -1,3 +1,8 @@ +/// +/// +/// + +namespace Modals { export type BanEntry = { name?: string; unique_id: string; diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index dc6344de..e5460964 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -1,4 +1,9 @@ -export namespace Modals { +/// +/// +/// +/// + +namespace Modals { export function openBanList(client: ConnectionHandler) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalBookmarks.ts b/shared/js/ui/modal/ModalBookmarks.ts index 875a34f2..950b3658 100644 --- a/shared/js/ui/modal/ModalBookmarks.ts +++ b/shared/js/ui/modal/ModalBookmarks.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { export function spawnBookmarkModal() { let modal: Modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalChangeLatency.ts b/shared/js/ui/modal/ModalChangeLatency.ts index 7690dcd3..3b8dca95 100644 --- a/shared/js/ui/modal/ModalChangeLatency.ts +++ b/shared/js/ui/modal/ModalChangeLatency.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { let modal: Modal; export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any, callback_flush?: () => any) { if(modal) modal.close(); diff --git a/shared/js/ui/modal/ModalChangeVolume.ts b/shared/js/ui/modal/ModalChangeVolume.ts index 691f61ab..b9b0778a 100644 --- a/shared/js/ui/modal/ModalChangeVolume.ts +++ b/shared/js/ui/modal/ModalChangeVolume.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { //TODO: Use the max limit! let modal: Modal; diff --git a/shared/js/ui/modal/ModalChannelInfo.ts b/shared/js/ui/modal/ModalChannelInfo.ts index ace6027b..db91989f 100644 --- a/shared/js/ui/modal/ModalChannelInfo.ts +++ b/shared/js/ui/modal/ModalChannelInfo.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function openChannelInfo(channel: ChannelEntry) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalClientInfo.ts b/shared/js/ui/modal/ModalClientInfo.ts index 67953f79..482a42d5 100644 --- a/shared/js/ui/modal/ModalClientInfo.ts +++ b/shared/js/ui/modal/ModalClientInfo.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { type InfoUpdateCallback = (info: ClientConnectionInfo) => any; export function openClientInfo(client: ClientEntry) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalConnect.ts b/shared/js/ui/modal/ModalConnect.ts index 9c9be5a4..cefdf403 100644 --- a/shared/js/ui/modal/ModalConnect.ts +++ b/shared/js/ui/modal/ModalConnect.ts @@ -1,5 +1,7 @@ +/// + //FIXME: Move this shit out of this file! -export namespace connection_log { +namespace connection_log { //TODO: Save password data export type ConnectionData = { name: string; @@ -89,7 +91,7 @@ export namespace connection_log { }); } -export namespace Modals { +namespace Modals { export function spawnConnectModal(options: { default_connect_new_tab?: boolean /* default false */ }, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) { diff --git a/shared/js/ui/modal/ModalCreateChannel.ts b/shared/js/ui/modal/ModalCreateChannel.ts index 5a5ba4d6..5f7be3bc 100644 --- a/shared/js/ui/modal/ModalCreateChannel.ts +++ b/shared/js/ui/modal/ModalCreateChannel.ts @@ -1,4 +1,6 @@ -export namespace Modals { +/// + +namespace Modals { export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) { let properties: ChannelProperties = { } as ChannelProperties; //The changes properties const modal = createModal({ diff --git a/shared/js/ui/modal/ModalGroupAssignment.ts b/shared/js/ui/modal/ModalGroupAssignment.ts index 9d2e05d1..52deb24d 100644 --- a/shared/js/ui/modal/ModalGroupAssignment.ts +++ b/shared/js/ui/modal/ModalGroupAssignment.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { let current_modal: Modal; export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise) { if(current_modal) diff --git a/shared/js/ui/modal/ModalIconSelect.ts b/shared/js/ui/modal/ModalIconSelect.ts index 65f915d8..42911e61 100644 --- a/shared/js/ui/modal/ModalIconSelect.ts +++ b/shared/js/ui/modal/ModalIconSelect.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) { selected_icon = selected_icon || 0; let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1); diff --git a/shared/js/ui/modal/ModalIdentity.ts b/shared/js/ui/modal/ModalIdentity.ts index 12aec88b..1c5f9296 100644 --- a/shared/js/ui/modal/ModalIdentity.ts +++ b/shared/js/ui/modal/ModalIdentity.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity, name: string): Modal { let modal: Modal; let elapsed_timer: NodeJS.Timer; diff --git a/shared/js/ui/modal/ModalInvite.ts b/shared/js/ui/modal/ModalInvite.ts index 77865c63..45a7acaa 100644 --- a/shared/js/ui/modal/ModalInvite.ts +++ b/shared/js/ui/modal/ModalInvite.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { type URLGeneratorSettings = { flag_direct: boolean, flag_resolved: boolean diff --git a/shared/js/ui/modal/ModalKeySelect.ts b/shared/js/ui/modal/ModalKeySelect.ts index 194f925b..8ad60b2d 100644 --- a/shared/js/ui/modal/ModalKeySelect.ts +++ b/shared/js/ui/modal/ModalKeySelect.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function spawnKeySelect(callback: (key?: ppt.KeyEvent) => void) { let modal = createModal({ header: tr("Select a key"), diff --git a/shared/js/ui/modal/ModalMusicManage.ts b/shared/js/ui/modal/ModalMusicManage.ts index ce84ee24..1736ca05 100644 --- a/shared/js/ui/modal/ModalMusicManage.ts +++ b/shared/js/ui/modal/ModalMusicManage.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) { const ev_registry = new events.Registry(); ev_registry.enable_debug("music-manage"); diff --git a/shared/js/ui/modal/ModalNewcomer.ts b/shared/js/ui/modal/ModalNewcomer.ts index 6a8d350d..20f414a8 100644 --- a/shared/js/ui/modal/ModalNewcomer.ts +++ b/shared/js/ui/modal/ModalNewcomer.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { const next_step: {[key: string]:string} = { "welcome": "microphone", //"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */ diff --git a/shared/js/ui/modal/ModalPlaylistEdit.ts b/shared/js/ui/modal/ModalPlaylistEdit.ts index e6a4e48b..69ec39c5 100644 --- a/shared/js/ui/modal/ModalPlaylistEdit.ts +++ b/shared/js/ui/modal/ModalPlaylistEdit.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { export function spawnPlaylistSongInfo(song: PlaylistSong) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalPlaylistList.ts b/shared/js/ui/modal/ModalPlaylistList.ts index 887a65f4..58c7451c 100644 --- a/shared/js/ui/modal/ModalPlaylistList.ts +++ b/shared/js/ui/modal/ModalPlaylistList.ts @@ -1,4 +1,9 @@ -export namespace Modals { +/// +/// +/// +/// + +namespace Modals { export function spawnPlaylistManage(client: ConnectionHandler) { { createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open(); diff --git a/shared/js/ui/modal/ModalPoke.ts b/shared/js/ui/modal/ModalPoke.ts index 744bf155..d4769a2c 100644 --- a/shared/js/ui/modal/ModalPoke.ts +++ b/shared/js/ui/modal/ModalPoke.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { let global_modal: PokeModal; interface ServerEntry { diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts index 0fc337d1..af3e8560 100644 --- a/shared/js/ui/modal/ModalQuery.ts +++ b/shared/js/ui/modal/ModalQuery.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) { let modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalQueryManage.ts b/shared/js/ui/modal/ModalQueryManage.ts index da9cd35a..c856ab83 100644 --- a/shared/js/ui/modal/ModalQueryManage.ts +++ b/shared/js/ui/modal/ModalQueryManage.ts @@ -1,4 +1,8 @@ -export namespace Modals { +/// +/// +/// + +namespace Modals { /* export function spawnQueryManage(client: ConnectionHandler) { let modal: Modal; diff --git a/shared/js/ui/modal/ModalServerEdit.ts b/shared/js/ui/modal/ModalServerEdit.ts index cf94122e..089f1522 100644 --- a/shared/js/ui/modal/ModalServerEdit.ts +++ b/shared/js/ui/modal/ModalServerEdit.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise) { const properties = Object.assign({}, server.properties); diff --git a/shared/js/ui/modal/ModalServerInfo.ts b/shared/js/ui/modal/ModalServerInfo.ts index d470f174..08fe0c71 100644 --- a/shared/js/ui/modal/ModalServerInfo.ts +++ b/shared/js/ui/modal/ModalServerInfo.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function openServerInfo(server: ServerEntry) { let modal: Modal; let update_callbacks: ServerBandwidthInfoUpdateCallback[] = []; diff --git a/shared/js/ui/modal/ModalServerInfoBandwidth.ts b/shared/js/ui/modal/ModalServerInfoBandwidth.ts index 4965b8f8..8bb6fdc8 100644 --- a/shared/js/ui/modal/ModalServerInfoBandwidth.ts +++ b/shared/js/ui/modal/ModalServerInfoBandwidth.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export enum RequestInfoStatus { SUCCESS, UNKNOWN, diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts index 523b28e1..155e3076 100644 --- a/shared/js/ui/modal/ModalSettings.ts +++ b/shared/js/ui/modal/ModalSettings.ts @@ -1,4 +1,4 @@ -export namespace Modals { +namespace Modals { export function spawnSettingsModal(default_page?: string) : Modal { let modal: Modal; modal = createModal({ diff --git a/shared/js/ui/modal/ModalYesNo.ts b/shared/js/ui/modal/ModalYesNo.ts index 8a9af6b1..4562d4ac 100644 --- a/shared/js/ui/modal/ModalYesNo.ts +++ b/shared/js/ui/modal/ModalYesNo.ts @@ -1,6 +1,6 @@ -import {BodyCreator, ModalFunctions} from "../elements/modal"; +/// -export namespace Modals { +namespace Modals { export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: { text_yes?: string, text_no?: string, diff --git a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts index edf6b296..f2c5ecfd 100644 --- a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts +++ b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts @@ -1,5 +1,7 @@ +/// /* first needs the AbstractPermissionEdit */ + /* Canvas Permission Editor */ -export namespace pe { +namespace pe { namespace ui { export namespace scheme { export interface CheckBox { diff --git a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts index 32a13d0b..f1400da9 100644 --- a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts +++ b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts @@ -1,4 +1,6 @@ -export namespace pe { +/// /* first needs the AbstractPermissionEdit */ + +namespace pe { class HTMLPermission { readonly handle: HTMLPermissionEditor; readonly group: HTMLPermissionGroup; @@ -121,7 +123,7 @@ export namespace pe { this._tag_value_input.on('change', event => { const str_value = this._tag_value_input.val() as string; const value = parseInt(str_value); - if(!HTMLPermission.number_filter_re.test(str_value) || value == NaN) { + if(!HTMLPermission.number_filter_re.test(str_value) || isNaN(value)) { console.warn(tr("Failed to parse given permission value string: %s"), this._tag_value_input.val()); this._reset_value(); return; diff --git a/shared/js/ui/modal/permission/ModalPermissionEdit.ts b/shared/js/ui/modal/permission/ModalPermissionEdit.ts index fcb85f8a..f405dd9c 100644 --- a/shared/js/ui/modal/permission/ModalPermissionEdit.ts +++ b/shared/js/ui/modal/permission/ModalPermissionEdit.ts @@ -1,8 +1,12 @@ +/// +/// +/// + interface JQuery { dropdown: any; } -export namespace Modals { +namespace Modals { export namespace PermissionEditor { export interface PermissionEntry { tag: JQuery; diff --git a/shared/js/ui/modal/permission/SenselessPermissions.ts b/shared/js/ui/modal/permission/SenselessPermissions.ts index 1ff6524b..06ceed6a 100644 --- a/shared/js/ui/modal/permission/SenselessPermissions.ts +++ b/shared/js/ui/modal/permission/SenselessPermissions.ts @@ -1,6 +1,6 @@ -import {PermissionType} from "../../../permission/PermissionManager"; +/// -export namespace permissions { +namespace permissions { export const senseless_server_group_permissions: PermissionType[] = [ PermissionType.B_CHANNEL_GROUP_INHERITANCE_END ]; diff --git a/shared/js/channel-tree/server.ts b/shared/js/ui/server.ts similarity index 98% rename from shared/js/channel-tree/server.ts rename to shared/js/ui/server.ts index 2c02ae8a..598f9d22 100644 --- a/shared/js/channel-tree/server.ts +++ b/shared/js/ui/server.ts @@ -1,9 +1,7 @@ /// -/// +/// -import {ChannelTree} from "./view"; - -export class ServerProperties { +class ServerProperties { virtualserver_host: string = ""; virtualserver_port: number = 0; @@ -80,7 +78,7 @@ export class ServerProperties { virtualserver_total_bytes_uploaded: number = 0; } -export interface ServerConnectionInfo { +interface ServerConnectionInfo { connection_filetransfer_bandwidth_sent: number; connection_filetransfer_bandwidth_received: number; @@ -105,12 +103,12 @@ export interface ServerConnectionInfo { connection_ping: number; } -export interface ServerAddress { +interface ServerAddress { host: string; port: number; } -export class ServerEntry { +class ServerEntry { remote_address: ServerAddress; channelTree: ChannelTree; properties: ServerProperties; diff --git a/shared/js/channel-tree/view.ts b/shared/js/ui/view.ts similarity index 99% rename from shared/js/channel-tree/view.ts rename to shared/js/ui/view.ts index 5f1e8108..4530e28e 100644 --- a/shared/js/channel-tree/view.ts +++ b/shared/js/ui/view.ts @@ -4,12 +4,12 @@ /// /// /// -/// -/// +/// +/// /// -export class ChannelTree { +class ChannelTree { client: ConnectionHandler; server: ServerEntry; diff --git a/shared/js/utils/helpers.ts b/shared/js/utils/helpers.ts index f3104911..1b8cc2d9 100644 --- a/shared/js/utils/helpers.ts +++ b/shared/js/utils/helpers.ts @@ -1,6 +1,6 @@ -import {sha} from "../crypto/sha"; +/// -export namespace helpers { +namespace helpers { export function hashPassword(password: string) : Promise { return new Promise((resolve, reject) => { sha.sha1(password).then(result => { @@ -10,7 +10,7 @@ export namespace helpers { } } -export class LaterPromise extends Promise { +class LaterPromise extends Promise { private _handle: Promise; private _resolve: ($: T) => any; private _reject: ($: any) => any; diff --git a/shared/js/voice/RecorderBase.ts b/shared/js/voice/RecorderBase.ts index 3fb11f55..ba32fb38 100644 --- a/shared/js/voice/RecorderBase.ts +++ b/shared/js/voice/RecorderBase.ts @@ -1,4 +1,4 @@ -export namespace audio { +namespace audio { export namespace recorder { export interface InputDevice { unique_id: string; diff --git a/shared/js/voice/RecorderProfile.ts b/shared/js/voice/RecorderProfile.ts index d149a6ac..ec5ea0dc 100644 --- a/shared/js/voice/RecorderProfile.ts +++ b/shared/js/voice/RecorderProfile.ts @@ -1,7 +1,7 @@ -import {ConnectionHandler} from "../ConnectionHandler"; +/// -export type VadType = "threshold" | "push_to_talk" | "active"; -export interface RecorderProfileConfig { +type VadType = "threshold" | "push_to_talk" | "active"; +interface RecorderProfileConfig { version: number; /* devices unique id */ @@ -25,8 +25,8 @@ export interface RecorderProfileConfig { } } -export let default_recorder: RecorderProfile; /* needs initialize */ -export class RecorderProfile { +let default_recorder: RecorderProfile; /* needs initialize */ +class RecorderProfile { readonly name; readonly volatile; /* not saving profile */ diff --git a/tsconfig.json b/tsconfig.json index 95a8baf6..fdf294a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,9 @@ "jsx": "react", "baseUrl": ".", "paths": { - "*": ["shared/declarations/*"] + "*": ["shared/declarations/*"], + "tc-shared/*": ["shared/js/*"], + "tc-backend/*": ["web/js/*"] } }, "exclude": [ diff --git a/web/js/audio/AudioPlayer.ts b/web/js/audio/AudioPlayer.ts index a0c6ea34..25538997 100644 --- a/web/js/audio/AudioPlayer.ts +++ b/web/js/audio/AudioPlayer.ts @@ -1,5 +1,3 @@ -import {log, LogCategory} from "shared-app/log"; - namespace audio.player { let _globalContext: AudioContext; let _global_destination: GainNode; diff --git a/webpack.config.js b/webpack.config.js index 9c9a692f..ca37ea76 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -51,6 +51,10 @@ module.exports = { }, resolve: { extensions: ['.tsx', '.ts', '.js', ".scss"], + alias: { + "tc-shared": path.resolve(__dirname, "shared/js"), + "tc-backend": path.resolve(__dirname, "web/js") + } }, output: { filename: 'bundle.js', From 0e3a415983cff8fc1d05f30a4cded77b3a489aaf Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 30 Mar 2020 13:44:18 +0200 Subject: [PATCH 06/23] Refactored the app to use webpack --- file.ts | 169 + {shared/loader => loader/app}/app.ts | 300 +- {shared/loader => loader/app}/certaccept.ts | 53 +- loader/app/index.ts | 5 + loader/app/loader.ts | 712 +++ loader/exports/loader.d.ts | 85 + loader/webpack.config.js | 62 + package-lock.json | 5 + package.json | 5 +- shared/backend.d/audio/player.d.ts | 19 + shared/backend.d/audio/recorder.d.ts | 9 + shared/backend.d/audio/sounds.d.ts | 3 + shared/backend.d/connection.d.ts | 5 + shared/backend.d/dns.d.ts | 5 + shared/backend.d/ppt.d.ts | 12 + shared/{backend => backend.d}/readme.md | 0 shared/backend/audio.d.ts | 35 - shared/backend/connection.d.ts | 4 - shared/backend/dns.d.ts | 4 - shared/backend/ppt.d.ts | 12 - shared/css/static/channel-tree.scss | 123 + shared/html/index.php | 4 +- shared/html/templates.html | 4 +- shared/js/BrowserIPC.ts | 1291 +++-- shared/js/ConnectionHandler.ts | 153 +- shared/js/FileManager.ts | 176 +- shared/js/MessageFormatter.ts | 506 +- shared/js/PPTListener.ts | 88 +- shared/js/audio/audio.ts | 10 - shared/js/audio/player.ts | 6 + shared/js/bookmarks.ts | 468 +- .../js/connection/AbstractCommandHandler.ts | 98 + shared/js/connection/CommandHandler.ts | 2207 ++++---- shared/js/connection/CommandHelper.ts | 823 +-- shared/js/connection/ConnectionBase.ts | 311 +- shared/js/connection/HandshakeHandler.ts | 267 +- .../connection/ServerConnectionDeclaration.ts | 22 +- shared/js/crypto/asn1.ts | 968 ++-- shared/js/crypto/crc32.ts | 2 +- shared/js/crypto/hex.ts | 34 +- shared/js/crypto/sha.ts | 710 ++- shared/js/crypto/uid.ts | 10 + shared/js/dns.ts | 40 +- shared/js/events.ts | 1111 ++-- shared/js/i18n/country.ts | 3003 ++++++----- shared/js/i18n/localize.ts | 553 +- shared/js/log.ts | 455 +- shared/js/main.ts | 188 +- shared/js/permission/GroupManager.ts | 25 +- shared/js/permission/PermissionManager.ts | 510 +- shared/js/permission/PermissionType.ts | 350 ++ shared/js/profiles/ConnectionProfile.ts | 438 +- shared/js/profiles/Identity.ts | 191 +- shared/js/profiles/identities/NameIdentity.ts | 149 +- .../profiles/identities/TeaForumIdentity.ts | 223 +- .../profiles/identities/TeamSpeakIdentity.ts | 1567 +++--- .../js/profiles/identities/teaspeak-forum.ts | 616 +-- shared/js/proto.ts | 229 +- shared/js/settings.ts | 20 +- shared/js/sound/Sounds.ts | 374 +- shared/js/stats.ts | 421 +- shared/js/ui/channel.ts | 38 +- shared/js/ui/client.ts | 96 +- shared/js/ui/client_move.ts | 8 +- .../{context_divider.ts => ContextDivider.ts} | 20 +- .../{context_menu.ts => ContextMenu.ts} | 155 +- shared/js/ui/elements/{modal.ts => Modal.ts} | 68 +- shared/js/ui/elements/NetGraph.ts | 433 ++ .../js/ui/elements/{slider.ts => Slider.ts} | 10 +- shared/js/ui/elements/{tab.ts => Tab.ts} | 15 +- shared/js/ui/elements/Tooltip.ts | 79 + shared/js/ui/elements/net_graph.ts | 435 -- shared/js/ui/elements/tooltip.ts | 85 - shared/js/ui/frames/ControlBar.ts | 98 +- shared/js/ui/frames/MenuBar.ts | 1029 ++-- shared/js/ui/frames/chat.ts | 420 +- shared/js/ui/frames/chat_frame.ts | 779 +-- shared/js/ui/frames/connection_handlers.ts | 12 +- shared/js/ui/frames/hostbanner.ts | 7 +- shared/js/ui/frames/image_preview.ts | 154 +- shared/js/ui/frames/server_log.ts | 1031 ++-- shared/js/ui/frames/side/chat_box.ts | 455 +- shared/js/ui/frames/side/chat_helper.ts | 711 +-- shared/js/ui/frames/side/client_info.ts | 503 +- shared/js/ui/frames/side/conversations.ts | 1150 ++-- shared/js/ui/frames/side/music_info.ts | 1557 +++--- .../ui/frames/side/private_conversations.ts | 1672 +++--- shared/js/ui/htmltags.ts | 477 +- shared/js/ui/modal/ModalAbout.ts | 85 +- shared/js/ui/modal/ModalAvatar.ts | 132 +- shared/js/ui/modal/ModalAvatarList.ts | 304 +- shared/js/ui/modal/ModalBanClient.ts | 294 +- shared/js/ui/modal/ModalBanList.ts | 1795 +++---- shared/js/ui/modal/ModalBookmarks.ts | 699 +-- shared/js/ui/modal/ModalChangeLatency.ts | 189 +- shared/js/ui/modal/ModalChangeVolume.ts | 123 +- shared/js/ui/modal/ModalChannelInfo.ts | 267 +- shared/js/ui/modal/ModalClientInfo.ts | 955 ++-- shared/js/ui/modal/ModalConnect.ts | 437 +- shared/js/ui/modal/ModalCreateChannel.ts | 1333 ++--- shared/js/ui/modal/ModalGroupAssignment.ts | 128 +- shared/js/ui/modal/ModalIconSelect.ts | 1240 ++--- shared/js/ui/modal/ModalIdentity.ts | 431 +- shared/js/ui/modal/ModalInvite.ts | 387 +- shared/js/ui/modal/ModalKeySelect.ts | 83 +- shared/js/ui/modal/ModalMusicManage.ts | 3573 ++++++------- shared/js/ui/modal/ModalNewcomer.ts | 789 +-- shared/js/ui/modal/ModalPlaylistEdit.ts | 668 +-- shared/js/ui/modal/ModalPlaylistList.ts | 344 +- shared/js/ui/modal/ModalPoke.ts | 166 +- shared/js/ui/modal/ModalQuery.ts | 151 +- shared/js/ui/modal/ModalQueryManage.ts | 718 +-- shared/js/ui/modal/ModalServerEdit.ts | 1373 ++--- shared/js/ui/modal/ModalServerInfo.ts | 444 +- .../js/ui/modal/ModalServerInfoBandwidth.ts | 325 +- shared/js/ui/modal/ModalSettings.ts | 4606 +++++++++-------- shared/js/ui/modal/ModalYesNo.ts | 74 +- .../permission/AbstractPermissionEditor.ts | 61 + .../permission/CanvasPermissionEditor.ts | 3142 +++++------ .../modal/permission/HTMLPermissionEditor.ts | 1717 +++--- .../modal/permission/ModalPermissionEdit.ts | 2757 +++++----- .../modal/permission/SenselessPermissions.ts | 108 +- shared/js/ui/server.ts | 36 +- shared/js/ui/view.ts | 44 +- shared/js/utils/LaterPromise.ts | 49 + shared/js/utils/buffers.ts | 71 + shared/js/utils/helpers.ts | 66 +- shared/js/voice/RecorderBase.ts | 228 +- shared/js/voice/RecorderProfile.ts | 73 +- shared/loader/loader.ts | 762 --- shared/popup/certaccept/js/main.ts | 26 + tsconfig.json | 3 +- web/js/WebPPTListener.ts | 152 - web/js/audio/AudioPlayer.ts | 103 - web/js/audio/WebCodec.ts | 11 - web/js/audio/player.ts | 128 + web/js/audio/recorder.ts | 873 ++++ web/js/audio/sounds.ts | 233 +- web/js/codec/BasicCodec.ts | 12 +- web/js/codec/Codec.ts | 10 +- web/js/codec/CodecWrapperRaw.ts | 4 +- web/js/codec/CodecWrapperWorker.ts | 8 +- web/js/connection.ts | 4 + web/js/connection/ServerConnection.ts | 928 ++-- web/js/dns.ts | 481 ++ web/js/dns_impl.ts | 478 -- web/js/index.ts | 1 + web/js/ppt.ts | 151 + web/js/voice/AudioResampler.ts | 5 +- web/js/voice/JavascriptRecorder.ts | 852 --- web/js/voice/VoiceClient.ts | 409 +- web/js/voice/VoiceHandler.ts | 1265 ++--- webpack.config.js | 26 +- 153 files changed, 34926 insertions(+), 34209 deletions(-) rename {shared/loader => loader/app}/app.ts (58%) rename {shared/loader => loader/app}/certaccept.ts (63%) create mode 100644 loader/app/index.ts create mode 100644 loader/app/loader.ts create mode 100644 loader/exports/loader.d.ts create mode 100644 loader/webpack.config.js create mode 100644 shared/backend.d/audio/player.d.ts create mode 100644 shared/backend.d/audio/recorder.d.ts create mode 100644 shared/backend.d/audio/sounds.d.ts create mode 100644 shared/backend.d/connection.d.ts create mode 100644 shared/backend.d/dns.d.ts create mode 100644 shared/backend.d/ppt.d.ts rename shared/{backend => backend.d}/readme.md (100%) delete mode 100644 shared/backend/audio.d.ts delete mode 100644 shared/backend/connection.d.ts delete mode 100644 shared/backend/dns.d.ts delete mode 100644 shared/backend/ppt.d.ts delete mode 100644 shared/js/audio/audio.ts create mode 100644 shared/js/audio/player.ts create mode 100644 shared/js/connection/AbstractCommandHandler.ts create mode 100644 shared/js/crypto/uid.ts create mode 100644 shared/js/permission/PermissionType.ts rename shared/js/ui/elements/{context_divider.ts => ContextDivider.ts} (94%) rename shared/js/ui/elements/{context_menu.ts => ContextMenu.ts} (68%) rename shared/js/ui/elements/{modal.ts => Modal.ts} (85%) create mode 100644 shared/js/ui/elements/NetGraph.ts rename shared/js/ui/elements/{slider.ts => Slider.ts} (92%) rename shared/js/ui/elements/{tab.ts => Tab.ts} (95%) create mode 100644 shared/js/ui/elements/Tooltip.ts delete mode 100644 shared/js/ui/elements/net_graph.ts delete mode 100644 shared/js/ui/elements/tooltip.ts create mode 100644 shared/js/ui/modal/permission/AbstractPermissionEditor.ts create mode 100644 shared/js/utils/LaterPromise.ts create mode 100644 shared/js/utils/buffers.ts delete mode 100644 shared/loader/loader.ts delete mode 100644 web/js/WebPPTListener.ts delete mode 100644 web/js/audio/AudioPlayer.ts delete mode 100644 web/js/audio/WebCodec.ts create mode 100644 web/js/audio/player.ts create mode 100644 web/js/audio/recorder.ts create mode 100644 web/js/connection.ts create mode 100644 web/js/dns.ts delete mode 100644 web/js/dns_impl.ts create mode 100644 web/js/ppt.ts delete mode 100644 web/js/voice/JavascriptRecorder.ts diff --git a/file.ts b/file.ts index 1fa20f8f..249d9c91 100644 --- a/file.ts +++ b/file.ts @@ -444,6 +444,7 @@ const CLIENT_APP_FILE_LIST = [ ...APP_FILE_LIST_CLIENT_SOURCE ]; +/* const WEB_APP_FILE_LIST = [ ...APP_FILE_LIST_SHARED_SOURCE, ...APP_FILE_LIST_SHARED_VENDORS, @@ -451,6 +452,174 @@ const WEB_APP_FILE_LIST = [ ...APP_FILE_LIST_WEB_TEASPEAK, ...CERTACCEPT_FILE_LIST, ]; +*/ +const WEB_APP_FILE_LIST = [ + ...APP_FILE_LIST_SHARED_VENDORS, + { /* shared html and php files */ + "type": "html", + "search-pattern": /^.*([a-zA-Z]+)\.(html|php|json)$/, + "build-target": "dev|rel", + + "path": "./", + "local-path": "./shared/html/" + }, + { /* javascript loader for releases */ + "type": "js", + "search-pattern": /.*$/, + "build-target": "dev|rel", + + "path": "js/", + "local-path": "./dist/" + }, + + { /* shared javascript files (WebRTC adapter) */ + "type": "js", + "search-pattern": /.*\.js$/, + "build-target": "dev|rel", + + "path": "adapter/", + "local-path": "./shared/adapter/" + }, + + { /* shared generated worker codec */ + "type": "js", + "search-pattern": /(WorkerPOW.js)$/, + "build-target": "dev|rel", + + "path": "js/workers/", + "local-path": "./shared/js/workers/" + }, + { /* shared developer single css files */ + "type": "css", + "search-pattern": /.*\.css$/, + "build-target": "dev", + + "path": "css/", + "local-path": "./shared/css/" + }, + { /* shared css mapping files (development mode only) */ + "type": "css", + "search-pattern": /.*\.(css.map|scss)$/, + "build-target": "dev", + + "path": "css/", + "local-path": "./shared/css/", + "req-parm": ["--mappings"] + }, + { /* shared release css files */ + "type": "css", + "search-pattern": /.*\.css$/, + "build-target": "rel", + + "path": "css/", + "local-path": "./shared/generated/" + }, + { /* shared release css files */ + "type": "css", + "search-pattern": /.*\.css$/, + "build-target": "rel", + + "path": "css/loader/", + "local-path": "./shared/css/loader/" + }, + { /* shared release css files */ + "type": "css", + "search-pattern": /.*\.css$/, + "build-target": "dev|rel", + + "path": "css/theme/", + "local-path": "./shared/css/theme/" + }, + { /* shared sound files */ + "type": "wav", + "search-pattern": /.*\.wav$/, + "build-target": "dev|rel", + + "path": "audio/", + "local-path": "./shared/audio/" + }, + { /* shared data sound files */ + "type": "json", + "search-pattern": /.*\.json/, + "build-target": "dev|rel", + + "path": "audio/", + "local-path": "./shared/audio/" + }, + { /* shared image files */ + "type": "img", + "search-pattern": /.*\.(svg|png)/, + "build-target": "dev|rel", + + "path": "img/", + "local-path": "./shared/img/" + }, + { /* own webassembly files */ + "type": "wasm", + "search-pattern": /.*\.(wasm)/, + "build-target": "dev|rel", + + "path": "wat/", + "local-path": "./shared/wat/" + }, + + + /* web specific */ + { /* generated assembly files */ + "web-only": true, + "type": "wasm", + "search-pattern": /.*\.(wasm)/, + "build-target": "dev|rel", + + "path": "wasm/", + "local-path": "./asm/generated/" + }, + { /* generated assembly javascript files */ + "web-only": true, + "type": "js", + "search-pattern": /.*\.(js)/, + "build-target": "dev|rel", + + "path": "wasm/", + "local-path": "./asm/generated/" + }, + { /* web generated worker codec */ + "web-only": true, + "type": "js", + "search-pattern": /(WorkerCodec.js)$/, + "build-target": "dev|rel", + + "path": "js/workers/", + "local-path": "./web/js/workers/" + }, + { /* web css files */ + "web-only": true, + "type": "css", + "search-pattern": /.*\.css$/, + "build-target": "dev|rel", + + "path": "css/", + "local-path": "./web/css/" + }, + { /* web html files */ + "web-only": true, + "type": "html", + "search-pattern": /.*\.(php|html)/, + "build-target": "dev|rel", + + "path": "./", + "local-path": "./web/html/" + }, + { /* translations */ + "web-only": true, /* Only required for the web client */ + "type": "i18n", + "search-pattern": /.*\.(translation|json)/, + "build-target": "dev|rel", + + "path": "i18n/", + "local-path": "./shared/i18n/" + } +] as any; //@ts-ignore declare module "fs-extra" { diff --git a/shared/loader/app.ts b/loader/app/app.ts similarity index 58% rename from shared/loader/app.ts rename to loader/app/app.ts index 4542f478..b1c5d540 100644 --- a/shared/loader/app.ts +++ b/loader/app/app.ts @@ -1,41 +1,39 @@ -/// +import * as loader from "./loader"; -interface Window { - $: JQuery; +declare global { + interface Window { + native_client: boolean; + } } -namespace app { - export enum Type { - UNKNOWN, - CLIENT_RELEASE, - CLIENT_DEBUG, - WEB_DEBUG, - WEB_RELEASE - } - export let type: Type = Type.UNKNOWN; - - export function is_web() { - return type == Type.WEB_RELEASE || type == Type.WEB_DEBUG; - } - - let _ui_version; - export function ui_version() { - if(typeof(_ui_version) !== "string") { - const version_node = document.getElementById("app_version"); - if(!version_node) return undefined; - - const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; - if(!version) return undefined; - - return (_ui_version = version); - } - return _ui_version; +const node_require: typeof require = window.require; + +let _ui_version; +export function ui_version() { + if(typeof(_ui_version) !== "string") { + const version_node = document.getElementById("app_version"); + if(!version_node) return undefined; + + const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; + if(!version) return undefined; + + return (_ui_version = version); } + return _ui_version; } /* all javascript loaders */ const loader_javascript = { detect_type: async () => { + //TODO: Detect real version! + loader.set_version({ + backend: "-", + ui: ui_version(), + debug_mode: true, + type: "web" + }); + window.native_client = false; + return; if(window.require) { const request = new Request("js/proto.js"); let file_path = request.url; @@ -43,11 +41,11 @@ const loader_javascript = { throw "Invalid file path (" + file_path + ")"; file_path = file_path.substring(process.platform === "win32" ? 8 : 7); - const fs = require('fs'); + const fs = node_require('fs'); if(fs.existsSync(file_path)) { - app.type = app.Type.CLIENT_DEBUG; + //type = Type.CLIENT_DEBUG; } else { - app.type = app.Type.CLIENT_RELEASE; + //type = Type.CLIENT_RELEASE; } } else { /* test if js/proto.js is available. If so we're in debug mode */ @@ -58,9 +56,9 @@ const loader_javascript = { request.onreadystatechange = () => { if (request.readyState === 4){ if (request.status === 404) { - app.type = app.Type.WEB_RELEASE; + //type = Type.WEB_RELEASE; } else { - app.type = app.Type.WEB_DEBUG; + //type = Type.WEB_DEBUG; } resolve(); } @@ -101,7 +99,7 @@ const loader_javascript = { ["vendor/emoji-picker/src/jquery.lsxemojipicker.js"] ]); - if(app.type == app.Type.WEB_RELEASE || app.type == app.Type.CLIENT_RELEASE) { + if(!loader.version().debug_mode) { loader.register_task(loader.Stage.JAVASCRIPT, { name: "scripts release", priority: 20, @@ -116,176 +114,8 @@ const loader_javascript = { } }, load_scripts_debug: async () => { - /* test if we're loading as TeaClient or WebClient */ - if(!window.require) { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "javascript web", - priority: 10, - function: loader_javascript.load_scripts_debug_web - }); - } else { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "javascript client", - priority: 10, - function: loader_javascript.load_scripts_debug_client - }); - } - - /* load some extends classes */ - await loader.load_scripts([ - ["js/connection/ConnectionBase.js"] - ]); - - /* load the main app */ - await loader.load_scripts([ - //Load general API's - "js/proto.js", - "js/i18n/localize.js", - "js/i18n/country.js", - "js/log.js", - "js/events.js", - - "js/sound/Sounds.js", - - "js/utils/helpers.js", - - "js/crypto/sha.js", - "js/crypto/hex.js", - "js/crypto/asn1.js", - "js/crypto/crc32.js", - - //load the profiles - "js/profiles/ConnectionProfile.js", - "js/profiles/Identity.js", - "js/profiles/identities/teaspeak-forum.js", - - //Basic UI elements - "js/ui/elements/context_divider.js", - "js/ui/elements/context_menu.js", - "js/ui/elements/modal.js", - "js/ui/elements/tab.js", - "js/ui/elements/slider.js", - "js/ui/elements/tooltip.js", - "js/ui/elements/net_graph.js", - - //Load permissions - "js/permission/PermissionManager.js", - "js/permission/GroupManager.js", - - //Load UI - "js/ui/modal/ModalAbout.js", - "js/ui/modal/ModalAvatar.js", - "js/ui/modal/ModalAvatarList.js", - "js/ui/modal/ModalClientInfo.js", - "js/ui/modal/ModalChannelInfo.js", - "js/ui/modal/ModalServerInfo.js", - "js/ui/modal/ModalServerInfoBandwidth.js", - "js/ui/modal/ModalQuery.js", - "js/ui/modal/ModalQueryManage.js", - "js/ui/modal/ModalPlaylistList.js", - "js/ui/modal/ModalPlaylistEdit.js", - "js/ui/modal/ModalBookmarks.js", - "js/ui/modal/ModalConnect.js", - "js/ui/modal/ModalSettings.js", - "js/ui/modal/ModalNewcomer.js", - "js/ui/modal/ModalCreateChannel.js", - "js/ui/modal/ModalServerEdit.js", - "js/ui/modal/ModalChangeVolume.js", - "js/ui/modal/ModalChangeLatency.js", - "js/ui/modal/ModalBanClient.js", - "js/ui/modal/ModalIconSelect.js", - "js/ui/modal/ModalInvite.js", - "js/ui/modal/ModalIdentity.js", - "js/ui/modal/ModalBanList.js", - "js/ui/modal/ModalMusicManage.js", - "js/ui/modal/ModalYesNo.js", - "js/ui/modal/ModalPoke.js", - "js/ui/modal/ModalKeySelect.js", - "js/ui/modal/ModalGroupAssignment.js", - "js/ui/modal/permission/ModalPermissionEdit.js", - {url: "js/ui/modal/permission/SenselessPermissions.js", depends: ["js/permission/PermissionManager.js"]}, - {url: "js/ui/modal/permission/CanvasPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]}, - {url: "js/ui/modal/permission/HTMLPermissionEditor.js", depends: ["js/ui/modal/permission/ModalPermissionEdit.js"]}, - - "js/channel-tree/channel.js", - "js/channel-tree/client.js", - "js/channel-tree/server.js", - "js/channel-tree/view.js", - "js/ui/client_move.js", - "js/ui/htmltags.js", - - - "js/ui/frames/side/chat_helper.js", - "js/ui/frames/side/chat_box.js", - "js/ui/frames/side/client_info.js", - "js/ui/frames/side/music_info.js", - "js/ui/frames/side/conversations.js", - "js/ui/frames/side/private_conversations.js", - - "js/ui/frames/ControlBar.js", - "js/ui/frames/chat.js", - "js/ui/frames/chat_frame.js", - "js/ui/frames/connection_handlers.js", - "js/ui/frames/server_log.js", - "js/ui/frames/hostbanner.js", - "js/ui/frames/MenuBar.js", - "js/ui/frames/image_preview.js", - - //Load audio - "js/voice/RecorderBase.js", - "js/voice/RecorderProfile.js", - - //Load general stuff - "js/settings.js", - "js/bookmarks.js", - "js/FileManager.js", - "js/ConnectionHandler.js", - "js/BrowserIPC.js", - "js/MessageFormatter.js", - - //Connection - "js/connection/CommandHandler.js", - "js/connection/CommandHelper.js", - "js/connection/HandshakeHandler.js", - "js/connection/ServerConnectionDeclaration.js", - - "js/stats.js", - "js/PPTListener.js", - - "js/profiles/identities/NameIdentity.js", //Depends on Identity - "js/profiles/identities/TeaForumIdentity.js", //Depends on Identity - "js/profiles/identities/TeamSpeakIdentity.js", //Depends on Identity - ]); - - await loader.load_script("js/main.js"); + await loader.load_scripts(["js/shared-app.js"]) }, - load_scripts_debug_web: async () => { - await loader.load_scripts([ - "js/audio/AudioPlayer.js", - "js/audio/sounds.js", - "js/audio/WebCodec.js", - "js/WebPPTListener.js", - - "js/voice/AudioResampler.js", - "js/voice/JavascriptRecorder.js", - "js/voice/VoiceHandler.js", - "js/voice/VoiceClient.js", - - //Connection - "js/connection/ServerConnection.js", - "js/dns_impl.js", - - //Load codec - "js/codec/Codec.js", - "js/codec/BasicCodec.js", - {url: "js/codec/CodecWrapperWorker.js", depends: ["js/codec/BasicCodec.js"]}, - ]); - }, - load_scripts_debug_client: async () => { - await loader.load_scripts([ - ]); - }, - load_release: async () => { console.log("Load for release!"); @@ -329,7 +159,7 @@ const loader_style = { ["vendor/highlight/styles/darcula.css", ""], /* empty string means not required */ ]); - if(app.type == app.Type.WEB_DEBUG || app.type == app.Type.CLIENT_DEBUG) { + if(loader.version().debug_mode) { await loader_style.load_style_debug(); } else { await loader_style.load_style_release(); @@ -494,23 +324,10 @@ loader.register_task(loader.Stage.TEMPLATES, { loader.register_task(loader.Stage.LOADED, { name: "loaded handler", - function: async () => { - fadeoutLoader(); - }, + function: async () => loader.hide_overlay(), priority: 5 }); -loader.register_task(loader.Stage.LOADED, { - name: "error task", - function: async () => { - if(Settings.instance.static(Settings.KEY_LOAD_DUMMY_ERROR, false)) { - loader.critical_error("The tea is cold!", "Argh, this is evil! Cold tea dosn't taste good."); - throw "The tea is cold!"; - } - }, - priority: 20 -}); - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { name: "lsx emoji picker setup", function: async () => await (window as any).setup_lsx_emoji_picker({twemoji: typeof(window.twemoji) !== "undefined"}), @@ -576,31 +393,26 @@ loader.register_task(loader.Stage.SETUP, { priority: 100 }); -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "log enabled initialisation", - function: async () => log.initialize(app.type === app.Type.CLIENT_DEBUG || app.type === app.Type.WEB_DEBUG ? log.LogType.TRACE : log.LogType.INFO), - priority: 150 -}); +export function run() { + window["Module"] = (window["Module"] || {}) as any; + /* TeaClient */ + if(node_require) { + const path = node_require("path"); + const remote = node_require('electron').remote; + module.paths.push(path.join(remote.app.getAppPath(), "/modules")); + module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js")); -window["Module"] = (window["Module"] || {}) as any; -/* TeaClient */ -if(window.require) { - const path = require("path"); - const remote = require('electron').remote; - module.paths.push(path.join(remote.app.getAppPath(), "/modules")); - module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js")); + //TODO: HERE! + const connector = node_require("renderer"); + loader.register_task(loader.Stage.INITIALIZING, { + name: "teaclient initialize", + function: connector.initialize, + priority: 40 + }); + } - const connector = require("renderer"); - console.log(connector); - - loader.register_task(loader.Stage.INITIALIZING, { - name: "teaclient initialize", - function: connector.initialize, - priority: 40 - }); -} - -if(!loader.running()) { - /* we know that we want to load the app */ - loader.execute_managed(); + if(!loader.running()) { + /* we know that we want to load the app */ + loader.execute_managed(); + } } \ No newline at end of file diff --git a/shared/loader/certaccept.ts b/loader/app/certaccept.ts similarity index 63% rename from shared/loader/certaccept.ts rename to loader/app/certaccept.ts index b95c1a26..7c12bac7 100644 --- a/shared/loader/certaccept.ts +++ b/loader/app/certaccept.ts @@ -1,4 +1,4 @@ -/// +import * as loader from "./loader"; let is_debug = false; @@ -25,34 +25,8 @@ const loader_javascript = { load_scripts: async () => { await loader.load_script(["vendor/jquery/jquery.min.js"]); - - if(!is_debug) { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "scripts release", - priority: 20, - function: loader_javascript.load_release - }); - } else { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "scripts debug", - priority: 20, - function: loader_javascript.load_scripts_debug - }); - } - }, - load_scripts_debug: async () => { await loader.load_scripts([ - ["js/proto.js"], - ["js/log.js"], - ["js/BrowserIPC.js"], - ["js/settings.js"], - ["js/main.js"] - ]); - }, - - load_release: async () => { - await loader.load_scripts([ - ["js/certaccept.min.js", "js/certaccept.js"] + ["dist/certificate-popup.js"], ]); } }; @@ -99,9 +73,7 @@ loader.register_task(loader.Stage.STYLE, { loader.register_task(loader.Stage.LOADED, { name: "loaded handler", - function: async () => { - fadeoutLoader(); - }, + function: async () => loader.hide_overlay(), priority: 0 }); @@ -123,25 +95,6 @@ loader.register_task(loader.Stage.INITIALIZING, { priority: 50 }); -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "settings initialisation", - function: async () => Settings.initialize(), - priority: 200 -}); - -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "bipc initialisation", - function: async () => bipc.setup(), - priority: 100 -}); - - -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "log enabled initialisation", - function: async () => log.initialize(is_debug ? log.LogType.TRACE : log.LogType.INFO), - priority: 150 -}); - if(!loader.running()) { /* we know that we want to load the app */ loader.execute_managed(); diff --git a/loader/app/index.ts b/loader/app/index.ts new file mode 100644 index 00000000..536f7ee7 --- /dev/null +++ b/loader/app/index.ts @@ -0,0 +1,5 @@ +import * as loader from "./app"; +import * as loader_base from "./loader"; + +export = loader_base; +loader.run(); \ No newline at end of file diff --git a/loader/app/loader.ts b/loader/app/loader.ts new file mode 100644 index 00000000..2df47c4f --- /dev/null +++ b/loader/app/loader.ts @@ -0,0 +1,712 @@ +import {AppVersion} from "../exports/loader"; +import {type} from "os"; + +declare global { + interface Window { + tr(message: string) : string; + tra(message: string, ...args: any[]); + + log: any; + StaticSettings: any; + } +} + +export interface Config { + loader_groups: boolean; + verbose: boolean; + error: boolean; +} + +export let config: Config = { + loader_groups: false, + verbose: false, + error: true +}; + +export type Task = { + name: string, + priority: number, /* tasks with the same priority will be executed in sync */ + function: () => Promise +}; + +export enum Stage { + /* + loading loader required files (incl this) + */ + INITIALIZING, + /* + setting up the loading process + */ + SETUP, + /* + loading all style sheet files + */ + STYLE, + /* + loading all javascript files + */ + JAVASCRIPT, + /* + loading all template files + */ + TEMPLATES, + /* + initializing static/global stuff + */ + JAVASCRIPT_INITIALIZING, + /* + finalizing load process + */ + FINALIZING, + /* + invoking main task + */ + LOADED, + + DONE +} + +let cache_tag: string | undefined; +let current_stage: Stage = undefined; +const tasks: {[key:number]:Task[]} = {}; + +/* test if all files shall be load from cache or fetch again */ +function loader_cache_tag() { + const app_version = (() => { + const version_node = document.getElementById("app_version"); + if(!version_node) return undefined; + + const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; + if(!version) return undefined; + + if(!version || version == "unknown" || version.replace(/0+/, "").length == 0) + return undefined; + + return version; + })(); + if(config.verbose) console.log("Found current app version: %o", app_version); + + if(!app_version) { + /* TODO add warning */ + cache_tag = "?_ts=" + Date.now(); + return; + } + const cached_version = localStorage.getItem("cached_version"); + if(!cached_version || cached_version != app_version) { + register_task(Stage.LOADED, { + priority: 0, + name: "cached version updater", + function: async () => { + localStorage.setItem("cached_version", app_version); + } + }); + } + cache_tag = "?_version=" + app_version; +} + +export function get_cache_version() { return cache_tag; } + +export function finished() { + return current_stage == Stage.DONE; +} +export function running() { return typeof(current_stage) !== "undefined"; } + +export function register_task(stage: Stage, task: Task) { + if(current_stage > stage) { + if(config.error) + console.warn("Register loading task, but it had already been finished. Executing task anyways!"); + task.function().catch(error => { + if(config.error) { + console.error("Failed to execute delayed loader task!"); + console.log(" - %s: %o", task.name, error); + } + + critical_error(error); + }); + return; + } + + const task_array = tasks[stage] || []; + task_array.push(task); + tasks[stage] = task_array.sort((a, b) => a.priority - b.priority); +} + +export async function execute() { + document.getElementById("loader-overlay").classList.add("started"); + loader_cache_tag(); + + const load_begin = Date.now(); + + let begin: number = 0; + let end: number = Date.now(); + while(current_stage <= Stage.LOADED || typeof(current_stage) === "undefined") { + + let current_tasks: Task[] = []; + while((tasks[current_stage] || []).length > 0) { + if(current_tasks.length == 0 || current_tasks[0].priority == tasks[current_stage][0].priority) { + current_tasks.push(tasks[current_stage].pop()); + } else break; + } + + const errors: { + error: any, + task: Task + }[] = []; + + const promises: Promise[] = []; + for(const task of current_tasks) { + try { + if(config.verbose) console.debug("Executing loader %s (%d)", task.name, task.priority); + promises.push(task.function().catch(error => { + errors.push({ + task: task, + error: error + }); + return Promise.resolve(); + })); + } catch(error) { + errors.push({ + task: task, + error: error + }); + } + } + + if(promises.length > 0) { + await Promise.all([...promises]); + } + + if(errors.length > 0) { + if(config.loader_groups) console.groupEnd(); + console.error("Failed to execute loader. The following tasks failed (%d):", errors.length); + for(const error of errors) + console.error(" - %s: %o", error.task.name, error.error); + + throw "failed to process step " + Stage[current_stage]; + } + + if(current_tasks.length == 0) { + if(typeof(current_stage) === "undefined") { + current_stage = -1; + if(config.verbose) console.debug("[loader] Booting app"); + } else if(current_stage < Stage.INITIALIZING) { + if(config.loader_groups) console.groupEnd(); + if(config.verbose) console.debug("[loader] Entering next state (%s). Last state took %dms", Stage[current_stage + 1], (end = Date.now()) - begin); + } else { + if(config.loader_groups) console.groupEnd(); + if(config.verbose) console.debug("[loader] Finish invoke took %dms", (end = Date.now()) - begin); + } + + begin = end; + current_stage += 1; + + if(current_stage != Stage.DONE && config.loader_groups) + console.groupCollapsed("Executing loading stage %s", Stage[current_stage]); + } + } + + /* cleanup */ + { + _script_promises = {}; + } + if(config.verbose) console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - load_begin); +} +export function execute_managed() { + execute().then(() => { + if(config.verbose) { + let message; + if(typeof(window.tr) !== "undefined") + message = tr("App loaded successfully!"); + else + message = "App loaded successfully!"; + + if(typeof(window.log) !== "undefined") { + /* We're having our log module */ + window.log.info(window.log.LogCategory.GENERAL, message); + } else { + console.log(message); + } + } + }).catch(error => { + console.error("App loading failed: %o", error); + critical_error("Failed to execute loader", "Lookup the console for more detail"); + }); +} + +export type DependSource = { + url: string; + depends: string[]; +} +export type SourcePath = string | DependSource | string[]; + +function script_name(path: SourcePath, html: boolean) { + if(Array.isArray(path)) { + let buffer = ""; + let _or = " or "; + for(let entry of path) + buffer += _or + script_name(entry, html); + return buffer.slice(_or.length); + } else if(typeof(path) === "string") + return html ? "" + path + "" : path; + else + return html ? "" + path.url + "" : path.url; +} + +class SyntaxError { + source: any; + + constructor(source: any) { + this.source = source; + } +} + +let _script_promises: {[key: string]: Promise} = {}; +export async function load_script(path: SourcePath) : Promise { + if(Array.isArray(path)) { //We have some fallback + return load_script(path[0]).catch(error => { + console.log(typeof error + " - " + (error instanceof SyntaxError)); + if(error instanceof SyntaxError) + return Promise.reject(error); + + if(path.length > 1) + return load_script(path.slice(1)); + + return Promise.reject(error); + }); + } else { + const source = typeof(path) === "string" ? {url: path, depends: []} : path; + if(source.url.length == 0) return Promise.resolve(); + + return _script_promises[source.url] = (async () => { + /* await depends */ + for(const depend of source.depends) { + if(!_script_promises[depend]) + throw "Missing dependency " + depend; + await _script_promises[depend]; + } + + const tag: HTMLScriptElement = document.createElement("script"); + + await new Promise((resolve, reject) => { + let error = false; + const error_handler = (event: ErrorEvent) => { + if(event.filename == tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error + if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); + window.removeEventListener('error', error_handler as any); + + reject(new SyntaxError(event.error)); + event.preventDefault(); + error = true; + } + }; + window.addEventListener('error', error_handler as any); + + const cleanup = () => { + tag.onerror = undefined; + tag.onload = undefined; + + clearTimeout(timeout_handle); + window.removeEventListener('error', error_handler as any); + }; + const timeout_handle = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 5000); + tag.type = "application/javascript"; + tag.async = true; + tag.defer = true; + tag.onerror = error => { + cleanup(); + tag.remove(); + reject(error); + }; + tag.onload = () => { + cleanup(); + + if(config.verbose) console.debug("Script %o loaded", path); + setTimeout(resolve, 100); + }; + + document.getElementById("scripts").appendChild(tag); + + tag.src = source.url + (cache_tag || ""); + }); + })(); + } +} + +export async function load_scripts(paths: SourcePath[]) : Promise { + const promises: Promise[] = []; + const errors: { + script: SourcePath, + error: any + }[] = []; + + for(const script of paths) + promises.push(load_script(script).catch(error => { + errors.push({ + script: script, + error: error + }); + return Promise.resolve(); + })); + + await Promise.all([...promises]); + + if(errors.length > 0) { + if(config.error) { + console.error("Failed to load the following scripts:"); + for(const script of errors) { + const sname = script_name(script.script, false); + if(script.error instanceof SyntaxError) { + const source = script.error.source as Error; + if(source.name === "TypeError") { + let prefix = ""; + while(prefix.length < sname.length + 7) prefix += " "; + console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n")); + } else { + console.log(" - %s: %o", sname, source); + } + } else { + console.log(" - %s: %o", sname, script.error); + } + } + } + + critical_error("Failed to load script " + script_name(errors[0].script, true) + "
" + "View the browser console for more information!"); + throw "failed to load script " + script_name(errors[0].script, false); + } +} + +export async function load_style(path: SourcePath) : Promise { + if(Array.isArray(path)) { //We have some fallback + return load_style(path[0]).catch(error => { + if(error instanceof SyntaxError) + return Promise.reject(error.source); + + if(path.length > 1) + return load_script(path.slice(1)); + + return Promise.reject(error); + }); + } else { + if(!path) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + const tag: HTMLLinkElement = document.createElement("link"); + + let error = false; + const error_handler = (event: ErrorEvent) => { + if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); + if(event.filename == tag.href) { //FIXME! + window.removeEventListener('error', error_handler as any); + + reject(new SyntaxError(event.error)); + event.preventDefault(); + error = true; + } + }; + window.addEventListener('error', error_handler as any); + + tag.type = "text/css"; + tag.rel = "stylesheet"; + + const cleanup = () => { + tag.onerror = undefined; + tag.onload = undefined; + + clearTimeout(timeout_handle); + window.removeEventListener('error', error_handler as any); + }; + + const timeout_handle = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 5000); + + tag.onerror = error => { + cleanup(); + tag.remove(); + if(config.error) + console.error("File load error for file %s: %o", path, error); + reject("failed to load file " + path); + }; + tag.onload = () => { + cleanup(); + { + const css: CSSStyleSheet = tag.sheet as CSSStyleSheet; + const rules = css.cssRules; + const rules_remove: number[] = []; + const rules_add: string[] = []; + + for(let index = 0; index < rules.length; index++) { + const rule = rules.item(index); + let rule_text = rule.cssText; + + if(rule.cssText.indexOf("%%base_path%%") != -1) { + rules_remove.push(index); + rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname)); + } + } + + for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) { + if(css.removeRule) + css.removeRule(index); + else + css.deleteRule(index); + } + for(const rule of rules_add) + css.insertRule(rule, rules_remove[0]); + } + + if(config.verbose) console.debug("Style sheet %o loaded", path); + setTimeout(resolve, 100); + }; + + document.getElementById("style").appendChild(tag); + tag.href = path + (cache_tag || ""); + }); + } +} + +export async function load_styles(paths: SourcePath[]) : Promise { + const promises: Promise[] = []; + const errors: { + sheet: SourcePath, + error: any + }[] = []; + + for(const sheet of paths) + promises.push(load_style(sheet).catch(error => { + errors.push({ + sheet: sheet, + error: error + }); + return Promise.resolve(); + })); + + await Promise.all([...promises]); + + if(errors.length > 0) { + if(config.error) { + console.error("Failed to load the following style sheet:"); + for(const sheet of errors) + console.log(" - %o: %o", sheet.sheet, sheet.error); + } + + critical_error("Failed to load style sheet " + script_name(errors[0].sheet, true) + "
" + "View the browser console for more information!"); + throw "failed to load style sheet " + script_name(errors[0].sheet, false); + } +} + +export async function load_template(path: SourcePath) : Promise { + try { + const response = await $.ajax(path + (cache_tag || "")); + + let node = document.createElement("html"); + node.innerHTML = response; + let tags: HTMLCollection; + if(node.getElementsByTagName("body").length > 0) + tags = node.getElementsByTagName("body")[0].children; + else + tags = node.children; + + let root = document.getElementById("templates"); + if(!root) { + critical_error("Failed to find template tag!"); + throw "Failed to find template tag"; + } + while(tags.length > 0){ + let tag = tags.item(0); + root.appendChild(tag); + + } + } catch(error) { + let msg; + if('responseText' in error) + msg = error.responseText; + else if(error instanceof Error) + msg = error.message; + + critical_error("failed to load template " + script_name(path, true), msg); + throw "template error"; + } +} + +export async function load_templates(paths: SourcePath[]) : Promise { + const promises: Promise[] = []; + const errors: { + template: SourcePath, + error: any + }[] = []; + + for(const template of paths) + promises.push(load_template(template).catch(error => { + errors.push({ + template: template, + error: error + }); + return Promise.resolve(); + })); + + await Promise.all([...promises]); + + if(errors.length > 0) { + if (config.error) { + console.error("Failed to load the following templates:"); + for (const sheet of errors) + console.log(" - %s: %o", script_name(sheet.template, false), sheet.error); + } + + critical_error("Failed to load template " + script_name(errors[0].template, true) + "
" + "View the browser console for more information!"); + throw "failed to load template " + script_name(errors[0].template, false); + } +} + +let version_: AppVersion; +export function version() : AppVersion { return version_; } +export function set_version(version: AppVersion) { version_ = version; } + +export type ErrorHandler = (message: string, detail: string) => void; + +let _callback_critical_error: ErrorHandler; +let _callback_critical_called: boolean = false; + +export function critical_error(message: string, detail?: string) { + if(_callback_critical_called) { + console.warn("[CRITICAL] %s", message); + if(typeof(detail) === "string") + console.warn("[CRITICAL] %s", detail); + return; + } + + _callback_critical_called = true; + if(_callback_critical_error) { + _callback_critical_error(message, detail); + return; + } + + /* default handling */ + let tag = document.getElementById("critical-load"); + + { + const error_tags = tag.getElementsByClassName("error"); + error_tags[0].innerHTML = message; + } + + if(typeof(detail) === "string") { + let node_detail = tag.getElementsByClassName("detail")[0]; + node_detail.innerHTML = detail; + } + + tag.classList.add("shown"); +} + +export function critical_error_handler(handler?: ErrorHandler, override?: boolean) : ErrorHandler { + if((typeof(handler) === "object" && handler !== _callback_critical_error) || override) + _callback_critical_error = handler; + return _callback_critical_error; +} + +let _fadeout_warned; +export function hide_overlay() { + if(typeof($) === "undefined") { + if(!_fadeout_warned) + console.warn("Could not fadeout loader screen. Missing jquery functions."); + _fadeout_warned = true; + return; + } + const animation_duration = 750; + + $(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, animation_duration); + $(".loader .half").animate({width: 0}, animation_duration, () => { + $(".loader").detach(); + }); +} + +{ + + const hello_world = () => { + const clog = console.log; + const print_security = () => { + { + const css = [ + "display: block", + "text-align: center", + "font-size: 42px", + "font-weight: bold", + "-webkit-text-stroke: 2px black", + "color: red" + ].join(";"); + clog("%c ", "font-size: 100px;"); + clog("%cSecurity warning:", css); + } + { + const css = [ + "display: block", + "text-align: center", + "font-size: 18px", + "font-weight: bold" + ].join(";"); + + clog("%cPasting anything in here could give attackers access to your data.", css); + clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css); + clog("%c ", "font-size: 100px;"); + } + }; + + /* print the hello world */ + { + const css = [ + "display: block", + "text-align: center", + "font-size: 72px", + "font-weight: bold", + "-webkit-text-stroke: 2px black", + "color: #18BC9C" + ].join(";"); + clog("%cHey, hold on!", css); + } + { + const css = [ + "display: block", + "text-align: center", + "font-size: 26px", + "font-weight: bold" + ].join(";"); + + const css_2 = [ + "display: block", + "text-align: center", + "font-size: 26px", + "font-weight: bold", + "color: blue" + ].join(";"); + + const display_detect = /./; + display_detect.toString = function() { print_security(); return ""; }; + + clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css); + clog("%cIf you have some good ideas or already done some incredible changes,", css); + clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2); + clog("%c ", display_detect); + } + }; + + try { /* lets try to print it as VM code :)*/ + let hello_world_code = hello_world.toString(); + hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8); + hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}")); + + //Look aheads are not possible with firefox + //hello_world_code = hello_world_code.replace(/(? Promise +}; +export enum Stage { + /* + loading loader required files (incl this) + */ + INITIALIZING, + /* + setting up the loading process + */ + SETUP, + /* + loading all style sheet files + */ + STYLE, + /* + loading all javascript files + */ + JAVASCRIPT, + /* + loading all template files + */ + TEMPLATES, + /* + initializing static/global stuff + */ + JAVASCRIPT_INITIALIZING, + /* + finalizing load process + */ + FINALIZING, + /* + invoking main task + */ + LOADED, + + DONE +} + +export function version() : AppVersion; + +export function finished(); +export function running(); +export function register_task(stage: Stage, task: Task); +export function execute() : Promise; +export function execute_managed(); +export type DependSource = { + url: string; + depends: string[]; +} +export type SourcePath = string | DependSource | string[]; +export function load_script(path: SourcePath) : Promise; +export function load_scripts(paths: SourcePath[]) : Promise; +export function load_style(path: SourcePath) : Promise; +export function load_styles(paths: SourcePath[]) : Promise; +export function load_template(path: SourcePath) : Promise; +export function load_templates(paths: SourcePath[]) : Promise; +export type ErrorHandler = (message: string, detail: string) => void; +export function critical_error(message: string, detail?: string); +export function critical_error_handler(handler?: ErrorHandler, override?: boolean); +export function hide_overlay(); \ No newline at end of file diff --git a/loader/webpack.config.js b/loader/webpack.config.js new file mode 100644 index 00000000..075fb34c --- /dev/null +++ b/loader/webpack.config.js @@ -0,0 +1,62 @@ +const path = require('path'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +const isDevelopment = process.env.NODE_ENV === 'development'; +module.exports = { + entry: path.join(__dirname, "app/index.ts"), + devtool: 'inline-source-map', + mode: "development", + plugins: [ + new MiniCssExtractPlugin({ + filename: isDevelopment ? '[name].css' : '[name].[hash].css', + chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }) + ], + module: { + rules: [ + { + test: /\.s[ac]ss$/, + loader: [ + //isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + sourceMap: isDevelopment + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: isDevelopment + } + } + ] + }, + { + test: /\.tsx?$/, + exclude: /node_modules/, + + loader: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true + } + } + ] + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js', ".scss"], + }, + output: { + filename: 'loader.js', + path: path.resolve(__dirname, '../dist'), + library: "loader", + //libraryTarget: "umd" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system" + }, + optimization: { } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e1cc94f0..df6a8f9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1087,6 +1087,11 @@ "safe-buffer": "^5.0.1" } }, + "circular-dependency-plugin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==" + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", diff --git a/package.json b/package.json index 6e2f47f6..b7c8f5ca 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output", "start": "npm run compile-file-helper && node file.js ndevelop", "build": "webpack --config webpack.config.js", - "watch": "webpack --watch" + "build-loader": "webpack --config loader/webpack.config.js", + "watch": "webpack --watch", + "watch-loader": "webpack --watch --config loader/webpack.config.js" }, "author": "TeaSpeak (WolverinDEV)", "license": "ISC", @@ -63,6 +65,7 @@ "homepage": "https://www.teaspeak.de", "dependencies": { "@types/fs-extra": "^8.0.1", + "circular-dependency-plugin": "^5.2.0", "react": "^16.13.1", "react-dom": "^16.13.1" } diff --git a/shared/backend.d/audio/player.d.ts b/shared/backend.d/audio/player.d.ts new file mode 100644 index 00000000..ef63390d --- /dev/null +++ b/shared/backend.d/audio/player.d.ts @@ -0,0 +1,19 @@ +import {Device} from "tc-shared/audio/player"; + +export function initialize() : boolean; +export function initialized() : boolean; + +export function context() : AudioContext; +export function get_master_volume() : number; +export function set_master_volume(volume: number); + +export function destination() : AudioNode; + +export function on_ready(cb: () => any); + +export function available_devices() : Promise; +export function set_device(device_id: string) : Promise; + +export function current_device() : Device; + +export function initializeFromGesture(); \ No newline at end of file diff --git a/shared/backend.d/audio/recorder.d.ts b/shared/backend.d/audio/recorder.d.ts new file mode 100644 index 00000000..a5fdd45d --- /dev/null +++ b/shared/backend.d/audio/recorder.d.ts @@ -0,0 +1,9 @@ +import {AbstractInput, InputDevice, LevelMeter} from "tc-shared/voice/RecorderBase"; + +export function devices() : InputDevice[]; + +export function device_refresh_available() : boolean; +export function refresh_devices() : Promise; + +export function create_input() : AbstractInput; +export function create_levelmeter(device: InputDevice) : Promise; \ No newline at end of file diff --git a/shared/backend.d/audio/sounds.d.ts b/shared/backend.d/audio/sounds.d.ts new file mode 100644 index 00000000..1140ee9e --- /dev/null +++ b/shared/backend.d/audio/sounds.d.ts @@ -0,0 +1,3 @@ +import {SoundFile} from "tc-shared/sound/Sounds"; + +export function play_sound(file: SoundFile) : Promise; \ No newline at end of file diff --git a/shared/backend.d/connection.d.ts b/shared/backend.d/connection.d.ts new file mode 100644 index 00000000..b362a4eb --- /dev/null +++ b/shared/backend.d/connection.d.ts @@ -0,0 +1,5 @@ +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; + +export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection; +export function destroy_server_connection(handle: AbstractServerConnection); \ No newline at end of file diff --git a/shared/backend.d/dns.d.ts b/shared/backend.d/dns.d.ts new file mode 100644 index 00000000..f7be12d6 --- /dev/null +++ b/shared/backend.d/dns.d.ts @@ -0,0 +1,5 @@ +import {AddressTarget, ResolveOptions} from "tc-shared/dns"; +import {ServerAddress} from "tc-shared/ui/server"; + +export function supported(); +export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise; \ No newline at end of file diff --git a/shared/backend.d/ppt.d.ts b/shared/backend.d/ppt.d.ts new file mode 100644 index 00000000..06e7a2ef --- /dev/null +++ b/shared/backend.d/ppt.d.ts @@ -0,0 +1,12 @@ +import {KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener"; + +export function initialize() : Promise; +export function finalize(); // most the times not really required + +export function register_key_listener(listener: (_: KeyEvent) => any); +export function unregister_key_listener(listener: (_: KeyEvent) => any); + +export function register_key_hook(hook: KeyHook); +export function unregister_key_hook(hook: KeyHook); + +export function key_pressed(code: string | SpecialKey) : boolean; \ No newline at end of file diff --git a/shared/backend/readme.md b/shared/backend.d/readme.md similarity index 100% rename from shared/backend/readme.md rename to shared/backend.d/readme.md diff --git a/shared/backend/audio.d.ts b/shared/backend/audio.d.ts deleted file mode 100644 index 53a5bdc3..00000000 --- a/shared/backend/audio.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -declare namespace audio { - export namespace player { - export function initialize() : boolean; - export function initialized() : boolean; - - export function context() : AudioContext; - export function get_master_volume() : number; - export function set_master_volume(volume: number); - - export function destination() : AudioNode; - - export function on_ready(cb: () => any); - - export function available_devices() : Promise; - export function set_device(device_id: string) : Promise; - - export function current_device() : Device; - - export function initializeFromGesture(); - } - - export namespace recorder { - export function devices() : InputDevice[]; - - export function device_refresh_available() : boolean; - export function refresh_devices() : Promise; - - export function create_input() : AbstractInput; - export function create_levelmeter(device: InputDevice) : Promise; - } - - export namespace sounds { - export function play_sound(file: sound.SoundFile) : Promise; - } -} \ No newline at end of file diff --git a/shared/backend/connection.d.ts b/shared/backend/connection.d.ts deleted file mode 100644 index e816b7df..00000000 --- a/shared/backend/connection.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare namespace connection { - export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection; - export function destroy_server_connection(handle: AbstractServerConnection); -} \ No newline at end of file diff --git a/shared/backend/dns.d.ts b/shared/backend/dns.d.ts deleted file mode 100644 index 483395c8..00000000 --- a/shared/backend/dns.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare namespace dns { - export function supported(); - export function resolve_address(address: ServerAddress, options?: ResolveOptions) : Promise; -} \ No newline at end of file diff --git a/shared/backend/ppt.d.ts b/shared/backend/ppt.d.ts deleted file mode 100644 index ad9a81e6..00000000 --- a/shared/backend/ppt.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare namespace ppt { - export function initialize() : Promise; - export function finalize(); // most the times not really required - - export function register_key_listener(listener: (_: KeyEvent) => any); - export function unregister_key_listener(listener: (_: KeyEvent) => any); - - export function register_key_hook(hook: KeyHook); - export function unregister_key_hook(hook: KeyHook); - - export function key_pressed(code: string | SpecialKey) : boolean; -} \ No newline at end of file diff --git a/shared/css/static/channel-tree.scss b/shared/css/static/channel-tree.scss index 64433d19..14b62dcb 100644 --- a/shared/css/static/channel-tree.scss +++ b/shared/css/static/channel-tree.scss @@ -94,7 +94,96 @@ } &.channel { + display: flex; + flex-direction: column; + .container-channel { + position: relative; + + display: flex; + flex-direction: row; + justify-content: stretch; + + width: 100%; + min-height: 16px; + + align-items: center; + cursor: pointer; + + .channel-type { + flex-grow: 0; + flex-shrink: 0; + + margin-right: 2px; + } + + .container-channel-name { + display: flex; + flex-direction: row; + + flex-grow: 1; + flex-shrink: 1; + + justify-content: left; + + max-width: 100%; /* important for the repetitive channel name! */ + overflow-x: hidden; + height: 16px; + + &.align-right { + justify-content: right; + } + + &.align-center, &.align-repetitive { + justify-content: center; + } + + .channel-name { + align-self: center; + color: $channel_tree_entry_text_color; + + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &.align-repetitive { + .channel-name { + text-overflow: clip; + } + } + } + + .icons { + display: flex; + flex-direction: row; + + flex-grow: 0; + flex-shrink: 0; + } + + &.move-selected { + border-bottom: 1px solid black; + } + + .show-channel-normal-only { + display: none; + + &.channel-normal { + display: block; + } + } + + .icon_no_sound { + display: flex; + } + } + + .container-clients { + display: flex; + flex-direction: column; + } } &.client { @@ -183,6 +272,40 @@ } } + &.channel .container-channel, &.client, &.server { + .marker-text-unread { + position: absolute; + left: 0; + top: 0; + bottom: 0; + + width: 1px; + background-color: #a814147F; + + opacity: 1; + + &:before { + content: ''; + position: absolute; + + left: 0; + top: 0; + bottom: 0; + + width: 24px; + + background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + } + + &.hidden { + opacity: 0; + } + + @include transition(opacity $button_hover_animation_time); + } + } } } diff --git a/shared/html/index.php b/shared/html/index.php index a601be7e..f0587239 100644 --- a/shared/html/index.php +++ b/shared/html/index.php @@ -145,9 +145,11 @@
+ +
diff --git a/shared/html/templates.html b/shared/html/templates.html index 017e1d11..b19bb293 100644 --- a/shared/html/templates.html +++ b/shared/html/templates.html @@ -2736,13 +2736,13 @@
{{else type == "default" }}
-
+
{{tr "English (Default / Fallback)" /}}
{{else}}
+ title="{{>country_name}}">
{{> name}}
diff --git a/shared/js/BrowserIPC.ts b/shared/js/BrowserIPC.ts index b7271f6e..45daa140 100644 --- a/shared/js/BrowserIPC.ts +++ b/shared/js/BrowserIPC.ts @@ -1,727 +1,724 @@ -interface Window { - BroadcastChannel: BroadcastChannel; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; + +export interface BroadcastMessage { + timestamp: number; + receiver: string; + sender: string; + + type: string; + data: any; } -namespace bipc { - export interface BroadcastMessage { - timestamp: number; - receiver: string; - sender: string; +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} - type: string; - data: any; +interface ProcessQuery { + timestamp: number + query_id: string; +} + +export interface ChannelMessage { + channel_id: string; + type: string; + data: any; +} + +export interface ProcessQueryResponse { + request_timestamp: number + request_query_id: string; + + device_id: string; + protocol: number; +} + +export interface CertificateAcceptCallback { + request_id: string; +} +export interface CertificateAcceptSucceeded { } + +export abstract class BasicIPCHandler { + protected static readonly BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000"; + protected static readonly PROTOCOL_VERSION = 1; + + protected _channels: Channel[] = []; + protected unique_id; + + protected constructor() { } + + setup() { + this.unique_id = uuidv4(); /* lets get an unique identifier */ } - function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); + get_local_address() { return this.unique_id; } + + abstract send_message(type: string, data: any, target?: string); + + protected handle_message(message: BroadcastMessage) { + //log.trace(LogCategory.IPC, tr("Received message %o"), message); + + if(message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) { + if(message.type == "process-query") { + log.debug(LogCategory.IPC, tr("Received a device query from %s."), message.sender); + this.send_message("process-query-response", { + request_query_id: (message.data).query_id, + request_timestamp: (message.data).timestamp, + + device_id: this.unique_id, + protocol: BasicIPCHandler.PROTOCOL_VERSION + } as ProcessQueryResponse, message.sender); + return; + } + } else if(message.receiver === this.unique_id) { + if(message.type == "process-query-response") { + const response: ProcessQueryResponse = message.data; + if(this._query_results[response.request_query_id]) + this._query_results[response.request_query_id].push(response); + else { + log.warn(LogCategory.IPC, tr("Received a query response for an unknown request.")); + } + return; + } + else if(message.type == "certificate-accept-callback") { + const data: CertificateAcceptCallback = message.data; + if(!this._cert_accept_callbacks[data.request_id]) { + log.warn(LogCategory.IPC, tr("Received certificate accept callback for an unknown request ID.")); + return; + } + this._cert_accept_callbacks[data.request_id](); + delete this._cert_accept_callbacks[data.request_id]; + + this.send_message("certificate-accept-succeeded", { + + } as CertificateAcceptSucceeded, message.sender); + return; + } + else if(message.type == "certificate-accept-succeeded") { + if(!this._cert_accept_succeeded[message.sender]) { + log.warn(LogCategory.IPC, tr("Received certificate accept succeeded, but haven't a callback.")); + return; + } + this._cert_accept_succeeded[message.sender](); + return; + } + } + if(message.type === "channel") { + const data: ChannelMessage = message.data; + + let channel_invoked = false; + for(const channel of this._channels) + if(channel.channel_id === data.channel_id && (typeof(channel.target_id) === "undefined" || channel.target_id === message.sender)) { + if(channel.message_handler) + channel.message_handler(message.sender, message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID, data); + channel_invoked = true; + } + if(!channel_invoked) { + console.warn(tr("Received channel message for unknown channel (%s)"), data.channel_id); + } + } } - interface ProcessQuery { - timestamp: number - query_id: string; + create_channel(target_id?: string, channel_id?: string) { + let channel: Channel = { + target_id: target_id, + channel_id: channel_id || uuidv4(), + message_handler: undefined, + send_message: (type: string, data: any, target?: string) => { + if(typeof target !== "undefined") { + if(typeof channel.target_id === "string" && target != channel.target_id) + throw "target id does not match channel target"; + } + + this.send_message("channel", { + type: type, + data: data, + channel_id: channel.channel_id + } as ChannelMessage, target || channel.target_id || BasicIPCHandler.BROADCAST_UNIQUE_ID); + } + }; + + this._channels.push(channel); + return channel; } - export interface ChannelMessage { - channel_id: string; - type: string; - data: any; + channels() : Channel[] { return this._channels; } + + delete_channel(channel: Channel) { + this._channels = this._channels.filter(e => e !== channel); } - export interface ProcessQueryResponse { - request_timestamp: number - request_query_id: string; + private _query_results: {[key: string]:ProcessQueryResponse[]} = {}; + async query_processes(timeout?: number) : Promise { + const query_id = uuidv4(); + this._query_results[query_id] = []; - device_id: string; - protocol: number; + this.send_message("process-query", { + query_id: query_id, + timestamp: Date.now() + } as ProcessQuery); + + await new Promise(resolve => setTimeout(resolve, timeout || 250)); + const result = this._query_results[query_id]; + delete this._query_results[query_id]; + return result; } - export interface CertificateAcceptCallback { + private _cert_accept_callbacks: {[key: string]:(() => any)} = {}; + register_certificate_accept_callback(callback: () => any) : string { + const id = uuidv4(); + this._cert_accept_callbacks[id] = callback; + return this.unique_id + ":" + id; + } + + private _cert_accept_succeeded: {[sender: string]:(() => any)} = {}; + post_certificate_accpected(id: string, timeout?: number) : Promise { + return new Promise((resolve, reject) => { + const data = id.split(":"); + const timeout_id = setTimeout(() => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + reject("timeout"); + }, timeout || 250); + this._cert_accept_succeeded[data[0]] = () => { + delete this._cert_accept_succeeded[data[0]]; + clearTimeout(timeout_id); + resolve(); + }; + this.send_message("certificate-accept-callback", { + request_id: data[1] + } as CertificateAcceptCallback, data[0]); + }) + } +} + +export interface Channel { + readonly channel_id: string; + target_id?: string; + + message_handler: (remote_id: string, broadcast: boolean, message: ChannelMessage) => any; + send_message(type: string, message: any, target?: string); +} + +class BroadcastChannelIPC extends BasicIPCHandler { + private static readonly CHANNEL_NAME = "TeaSpeak-Web"; + + private channel: BroadcastChannel; + + constructor() { + super(); + } + + setup() { + super.setup(); + + this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME); + this.channel.onmessage = this.on_message.bind(this); + this.channel.onmessageerror = this.on_error.bind(this); + } + + private on_message(event: MessageEvent) { + if(typeof(event.data) !== "string") { + log.warn(LogCategory.IPC, tr("Received message with an invalid type (%s): %o"), typeof(event.data), event.data); + return; + } + + let message: BroadcastMessage; + try { + message = JSON.parse(event.data); + } catch(error) { + log.error(LogCategory.IPC, tr("Received an invalid encoded message: %o"), event.data); + return; + } + super.handle_message(message); + } + + private on_error(event: MessageEvent) { + log.warn(LogCategory.IPC, tr("Received error: %o"), event); + } + + send_message(type: string, data: any, target?: string) { + const message: BroadcastMessage = {} as any; + + message.sender = this.unique_id; + message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID; + message.timestamp = Date.now(); + message.type = type; + message.data = data; + + this.channel.postMessage(JSON.stringify(message)); + } +} + +export namespace connect { + export type ConnectRequestData = { + address: string; + + profile?: string; + username?: string; + password?: { + value: string; + hashed: boolean; + }; + } + + export interface ConnectOffer { + request_id: string; + data: ConnectRequestData; + } + + export interface ConnectOfferAnswer { + request_id: string; + accepted: boolean; + } + + export interface ConnectExecute { request_id: string; } - export interface CertificateAcceptSucceeded { } - export abstract class BasicIPCHandler { - protected static readonly BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000"; - protected static readonly PROTOCOL_VERSION = 1; + export interface ConnectExecuted { + request_id: string; + succeeded: boolean; + message?: string; + } - protected _channels: Channel[] = []; - protected unique_id; + /* The connect process: + * 1. Broadcast an offer + * 2. Wait 50ms for all offer responses or until the first one respond with "ok" + * 3. Select (if possible) on accepted offer and execute the connect + */ + export class ConnectHandler { + private static readonly CHANNEL_NAME = "connect"; - protected constructor() { } + readonly ipc_handler: BasicIPCHandler; + private ipc_channel: Channel; - setup() { - this.unique_id = uuidv4(); /* lets get an unique identifier */ + public callback_available: (data: ConnectRequestData) => boolean = () => false; + public callback_execute: (data: ConnectRequestData) => boolean | string = () => false; + + + private _pending_connect_offers: { + id: string; + data: ConnectRequestData; + timeout: number; + + remote_handler: string; + }[] = []; + + private _pending_connects_requests: { + id: string; + + data: ConnectRequestData; + timeout: number; + + callback_success: () => any; + callback_failed: (message: string) => any; + callback_avail: () => Promise; + + remote_handler?: string; + }[] = []; + + constructor(ipc_handler: BasicIPCHandler) { + this.ipc_handler = ipc_handler; } - get_local_address() { return this.unique_id; } + public setup() { + this.ipc_channel = this.ipc_handler.create_channel(undefined, ConnectHandler.CHANNEL_NAME); + this.ipc_channel.message_handler = this.on_message.bind(this); + } - abstract send_message(type: string, data: any, target?: string); + private on_message(sender: string, broadcast: boolean, message: ChannelMessage) { + if(broadcast) { + if(message.type == "offer") { + const data = message.data as ConnectOffer; - protected handle_message(message: BroadcastMessage) { - //log.trace(LogCategory.IPC, tr("Received message %o"), message); + const response = { + accepted: this.callback_available(data.data), + request_id: data.request_id + } as ConnectOfferAnswer; - if(message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) { - if(message.type == "process-query") { - log.debug(LogCategory.IPC, tr("Received a device query from %s."), message.sender); - this.send_message("process-query-response", { - request_query_id: (message.data).query_id, - request_timestamp: (message.data).timestamp, + if(response.accepted) { + log.debug(LogCategory.IPC, tr("Received new connect offer from %s: %s"), sender, data.request_id); - device_id: this.unique_id, - protocol: BasicIPCHandler.PROTOCOL_VERSION - } as ProcessQueryResponse, message.sender); - return; - } - } else if(message.receiver === this.unique_id) { - if(message.type == "process-query-response") { - const response: ProcessQueryResponse = message.data; - if(this._query_results[response.request_query_id]) - this._query_results[response.request_query_id].push(response); - else { - log.warn(LogCategory.IPC, tr("Received a query response for an unknown request.")); + const ld = { + remote_handler: sender, + data: data.data, + id: data.request_id, + timeout: 0 + }; + this._pending_connect_offers.push(ld); + ld.timeout = setTimeout(() => { + log.debug(LogCategory.IPC, tr("Dropping connect request %s, because we never received an execute."), ld.id); + this._pending_connect_offers.remove(ld); + }, 120 * 1000) as any; } - return; + this.ipc_channel.send_message("offer-answer", response, sender); } - else if(message.type == "certificate-accept-callback") { - const data: CertificateAcceptCallback = message.data; - if(!this._cert_accept_callbacks[data.request_id]) { - log.warn(LogCategory.IPC, tr("Received certificate accept callback for an unknown request ID.")); + } else { + if(message.type == "offer-answer") { + const data = message.data as ConnectOfferAnswer; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if(!request) { + log.warn(LogCategory.IPC, tr("Received connect offer answer with unknown request id (%s)."), data.request_id); return; } - this._cert_accept_callbacks[data.request_id](); - delete this._cert_accept_callbacks[data.request_id]; - - this.send_message("certificate-accept-succeeded", { - - } as CertificateAcceptSucceeded, message.sender); - return; - } - else if(message.type == "certificate-accept-succeeded") { - if(!this._cert_accept_succeeded[message.sender]) { - log.warn(LogCategory.IPC, tr("Received certificate accept succeeded, but haven't a callback.")); + if(!data.accepted) { + log.debug(LogCategory.IPC, tr("Client %s rejected the connect offer (%s)."), sender, request.id); return; } - this._cert_accept_succeeded[message.sender](); - return; - } - } - if(message.type === "channel") { - const data: ChannelMessage = message.data; - - let channel_invoked = false; - for(const channel of this._channels) - if(channel.channel_id === data.channel_id && (typeof(channel.target_id) === "undefined" || channel.target_id === message.sender)) { - if(channel.message_handler) - channel.message_handler(message.sender, message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID, data); - channel_invoked = true; + if(request.remote_handler) { + log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s), but offer has already been accepted."), sender, request.id); + return; } - if(!channel_invoked) { - console.warn(tr("Received channel message for unknown channel (%s)"), data.channel_id); + + log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s). Request local acceptance."), sender, request.id); + request.remote_handler = sender; + clearTimeout(request.timeout); + + request.callback_avail().then(flag => { + if(!flag) { + request.callback_failed("local avail rejected"); + return; + } + + log.debug(LogCategory.IPC, tr("Executing connect with client %s"), request.remote_handler); + this.ipc_channel.send_message("execute", { + request_id: request.id + } as ConnectExecute, request.remote_handler); + request.timeout = setTimeout(() => { + request.callback_failed("connect execute timeout"); + }, 1000) as any; + }).catch(error => { + log.error(LogCategory.IPC, tr("Local avail callback caused an error: %o"), error); + request.callback_failed(tr("local avail callback caused an error")); + }); + + } + else if(message.type == "executed") { + const data = message.data as ConnectExecuted; + const request = this._pending_connects_requests.find(e => e.id === data.request_id); + if(!request) { + log.warn(LogCategory.IPC, tr("Received connect executed with unknown request id (%s)."), data.request_id); + return; + } + + if(request.remote_handler != sender) { + log.warn(LogCategory.IPC, tr("Received connect executed for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); + return; + } + + log.debug(LogCategory.IPC, tr("Received connect executed response from client %s for request %s. Succeeded: %o (%s)"), sender, data.request_id, data.succeeded, data.message); + clearTimeout(request.timeout); + if(data.succeeded) + request.callback_success(); + else + request.callback_failed(data.message); + } + else if(message.type == "execute") { + const data = message.data as ConnectExecute; + const request = this._pending_connect_offers.find(e => e.id === data.request_id); + if(!request) { + log.warn(LogCategory.IPC, tr("Received connect execute with unknown request id (%s)."), data.request_id); + return; + } + + if(request.remote_handler != sender) { + log.warn(LogCategory.IPC, tr("Received connect execute for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); + return; + } + clearTimeout(request.timeout); + this._pending_connect_offers.remove(request); + + log.debug(LogCategory.IPC, tr("Executing connect for %s"), data.request_id); + const cr = this.callback_execute(request.data); + + const response = { + request_id: data.request_id, + + succeeded: typeof(cr) !== "string" && cr, + message: typeof(cr) === "string" ? cr : "", + } as ConnectExecuted; + this.ipc_channel.send_message("executed", response, request.remote_handler); } } } - create_channel(target_id?: string, channel_id?: string) { - let channel: Channel = { - target_id: target_id, - channel_id: channel_id || uuidv4(), - message_handler: undefined, - send_message: (type: string, data: any, target?: string) => { - if(typeof target !== "undefined") { - if(typeof channel.target_id === "string" && target != channel.target_id) - throw "target id does not match channel target"; - } + post_connect_request(data: ConnectRequestData, callback_avail: () => Promise) : Promise { + return new Promise((resolve, reject) => { + const pd = { + data: data, + id: uuidv4(), + timeout: 0, - this.send_message("channel", { - type: type, - data: data, - channel_id: channel.channel_id - } as ChannelMessage, target || channel.target_id || BasicIPCHandler.BROADCAST_UNIQUE_ID); - } - }; + callback_success: () => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + resolve(); + }, - this._channels.push(channel); - return channel; - } + callback_failed: error => { + this._pending_connects_requests.remove(pd); + clearTimeout(pd.timeout); + reject(error); + }, - channels() : Channel[] { return this._channels; } - - delete_channel(channel: Channel) { - this._channels = this._channels.filter(e => e !== channel); - } - - private _query_results: {[key: string]:ProcessQueryResponse[]} = {}; - async query_processes(timeout?: number) : Promise { - const query_id = uuidv4(); - this._query_results[query_id] = []; - - this.send_message("process-query", { - query_id: query_id, - timestamp: Date.now() - } as ProcessQuery); - - await new Promise(resolve => setTimeout(resolve, timeout || 250)); - const result = this._query_results[query_id]; - delete this._query_results[query_id]; - return result; - } - - private _cert_accept_callbacks: {[key: string]:(() => any)} = {}; - register_certificate_accept_callback(callback: () => any) : string { - const id = uuidv4(); - this._cert_accept_callbacks[id] = callback; - return this.unique_id + ":" + id; - } - - private _cert_accept_succeeded: {[sender: string]:(() => any)} = {}; - post_certificate_accpected(id: string, timeout?: number) : Promise { - return new Promise((resolve, reject) => { - const data = id.split(":"); - const timeout_id = setTimeout(() => { - delete this._cert_accept_succeeded[data[0]]; - clearTimeout(timeout_id); - reject("timeout"); - }, timeout || 250); - this._cert_accept_succeeded[data[0]] = () => { - delete this._cert_accept_succeeded[data[0]]; - clearTimeout(timeout_id); - resolve(); + callback_avail: callback_avail, }; - this.send_message("certificate-accept-callback", { - request_id: data[1] - } as CertificateAcceptCallback, data[0]); + this._pending_connects_requests.push(pd); + + this.ipc_channel.send_message("offer", { + request_id: pd.id, + data: pd.data + } as ConnectOffer); + pd.timeout = setTimeout(() => { + pd.callback_failed("received no response to offer"); + }, 50) as any; }) } } +} - export interface Channel { - readonly channel_id: string; - target_id?: string; +export namespace mproxy { + export interface MethodProxyInvokeData { + method_name: string; + arguments: any[]; + promise_id: string; + } + export interface MethodProxyResultData { + promise_id: string; + result: any; + success: boolean; + } + export interface MethodProxyCallback { + promise: Promise; + promise_id: string; - message_handler: (remote_id: string, broadcast: boolean, message: ChannelMessage) => any; - send_message(type: string, message: any, target?: string); + resolve: (object: any) => any; + reject: (object: any) => any; } - class BroadcastChannelIPC extends BasicIPCHandler { - private static readonly CHANNEL_NAME = "TeaSpeak-Web"; + export type MethodProxyConnectParameters = { + channel_id: string; + client_id: string; + } + export abstract class MethodProxy { + readonly ipc_handler: BasicIPCHandler; + private _ipc_channel: Channel; + private _ipc_parameters: MethodProxyConnectParameters; - private channel: BroadcastChannel; + private readonly _local: boolean; + private readonly _slave: boolean; - constructor() { - super(); + private _connected: boolean; + private _proxied_methods: {[key: string]:() => Promise} = {}; + private _proxied_callbacks: {[key: string]:MethodProxyCallback} = {}; + + protected constructor(ipc_handler: BasicIPCHandler, connect_params?: MethodProxyConnectParameters) { + this.ipc_handler = ipc_handler; + this._ipc_parameters = connect_params; + this._connected = false; + this._slave = typeof(connect_params) !== "undefined"; + this._local = typeof(connect_params) !== "undefined" && connect_params.channel_id === "local" && connect_params.client_id === "local"; } - setup() { - super.setup(); + protected setup() { + if(this._local) { + this._connected = true; + this.on_connected(); + } else { + if(this._slave) + this._ipc_channel = this.ipc_handler.create_channel(this._ipc_parameters.client_id, this._ipc_parameters.channel_id); + else + this._ipc_channel = this.ipc_handler.create_channel(); - this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME); - this.channel.onmessage = this.on_message.bind(this); - this.channel.onmessageerror = this.on_error.bind(this); + this._ipc_channel.message_handler = this._handle_message.bind(this); + if(this._slave) + this._ipc_channel.send_message("initialize", {}); + } } - private on_message(event: MessageEvent) { - if(typeof(event.data) !== "string") { - log.warn(LogCategory.IPC, tr("Received message with an invalid type (%s): %o"), typeof(event.data), event.data); + protected finalize() { + if(!this._local) { + if(this._connected) + this._ipc_channel.send_message("finalize", {}); + + this.ipc_handler.delete_channel(this._ipc_channel); + this._ipc_channel = undefined; + } + for(const promise of Object.values(this._proxied_callbacks)) + promise.reject("disconnected"); + this._proxied_callbacks = {}; + + this._connected = false; + this.on_disconnected(); + } + + protected register_method(method: (...args: any[]) => Promise | string) { + let method_name: string; + if(typeof method === "function") { + log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method.name); + method_name = method.name; + } else { + log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method); + method_name = method; + } + + if(!this[method_name]) + throw "method is missing in current object"; + + this._proxied_methods[method_name] = this[method_name]; + if(!this._local) { + this[method_name] = (...args: any[]) => { + if(!this._connected) + return Promise.reject("not connected"); + + const proxy_callback = { + promise_id: uuidv4() + } as MethodProxyCallback; + this._proxied_callbacks[proxy_callback.promise_id] = proxy_callback; + proxy_callback.promise = new Promise((resolve, reject) => { + proxy_callback.resolve = resolve; + proxy_callback.reject = reject; + }); + + this._ipc_channel.send_message("invoke", { + promise_id: proxy_callback.promise_id, + arguments: [...args], + method_name: method_name + } as MethodProxyInvokeData); + return proxy_callback.promise; + } + } + } + + private _handle_message(remote_id: string, boradcast: boolean, message: ChannelMessage) { + if(message.type === "finalize") { + this._handle_finalize(); + } else if(message.type === "initialize") { + this._handle_remote_callback(remote_id); + } else if(message.type === "invoke") { + this._handle_invoke(message.data); + } else if(message.type === "result") { + this._handle_result(message.data); + } + } + + private _handle_finalize() { + this.on_disconnected(); + this.finalize(); + this._connected = false; + } + + private _handle_remote_callback(remote_id: string) { + if(!this._ipc_channel.target_id) { + if(this._slave) + throw "initialize wrong state!"; + + this._ipc_channel.target_id = remote_id; /* now we're able to send messages */ + this.on_connected(); + this._ipc_channel.send_message("initialize", true); + } else { + if(!this._slave) + throw "initialize wrong state!"; + + this.on_connected(); + } + this._connected = true; + } + + private _send_result(promise_id: string, success: boolean, message: any) { + this._ipc_channel.send_message("result", { + promise_id: promise_id, + result: message, + success: success + } as MethodProxyResultData); + } + + private _handle_invoke(data: MethodProxyInvokeData) { + if(this._proxied_methods[data.method_name]) + throw "we could not invoke a local proxied method!"; + + if(!this[data.method_name]) { + this._send_result(data.promise_id, false, "missing method"); return; } - let message: BroadcastMessage; try { - message = JSON.parse(event.data); + log.info(LogCategory.IPC, tr("Invoking method %s with arguments: %o"), data.method_name, data.arguments); + + const promise = this[data.method_name](...data.arguments); + promise.then(result => { + log.info(LogCategory.IPC, tr("Result: %o"), result); + this._send_result(data.promise_id, true, result); + }).catch(error => { + this._send_result(data.promise_id, false, error); + }); } catch(error) { - log.error(LogCategory.IPC, tr("Received an invalid encoded message: %o"), event.data); + this._send_result(data.promise_id, false, error); return; } - super.handle_message(message); } - private on_error(event: MessageEvent) { - log.warn(LogCategory.IPC, tr("Received error: %o"), event); + private _handle_result(data: MethodProxyResultData) { + if(!this._proxied_callbacks[data.promise_id]) { + console.warn(tr("Received proxy method result for unknown promise")); + return; + } + const callback = this._proxied_callbacks[data.promise_id]; + delete this._proxied_callbacks[data.promise_id]; + + if(data.success) + callback.resolve(data.result); + else + callback.reject(data.result); } - send_message(type: string, data: any, target?: string) { - const message: BroadcastMessage = {} as any; + generate_connect_parameters() : MethodProxyConnectParameters { + if(this._slave) + throw "only masters can generate connect parameters!"; + if(!this._ipc_channel) + throw "please call setup() before"; - message.sender = this.unique_id; - message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID; - message.timestamp = Date.now(); - message.type = type; - message.data = data; - - this.channel.postMessage(JSON.stringify(message)); - } - } - - export namespace connect { - export type ConnectRequestData = { - address: string; - - profile?: string; - username?: string; - password?: { - value: string; - hashed: boolean; + return { + channel_id: this._ipc_channel.channel_id, + client_id: this.ipc_handler.get_local_address() }; } - export interface ConnectOffer { - request_id: string; - data: ConnectRequestData; - } + is_slave() { return this._local || this._slave; } /* the popout modal */ + is_master() { return this._local || !this._slave; } /* the host (teaweb application) */ - export interface ConnectOfferAnswer { - request_id: string; - accepted: boolean; - } - - export interface ConnectExecute { - request_id: string; - } - - export interface ConnectExecuted { - request_id: string; - succeeded: boolean; - message?: string; - } - - /* The connect process: - * 1. Broadcast an offer - * 2. Wait 50ms for all offer responses or until the first one respond with "ok" - * 3. Select (if possible) on accepted offer and execute the connect - */ - export class ConnectHandler { - private static readonly CHANNEL_NAME = "connect"; - - readonly ipc_handler: BasicIPCHandler; - private ipc_channel: Channel; - - public callback_available: (data: ConnectRequestData) => boolean = () => false; - public callback_execute: (data: ConnectRequestData) => boolean | string = () => false; - - - private _pending_connect_offers: { - id: string; - data: ConnectRequestData; - timeout: number; - - remote_handler: string; - }[] = []; - - private _pending_connects_requests: { - id: string; - - data: ConnectRequestData; - timeout: number; - - callback_success: () => any; - callback_failed: (message: string) => any; - callback_avail: () => Promise; - - remote_handler?: string; - }[] = []; - - constructor(ipc_handler: BasicIPCHandler) { - this.ipc_handler = ipc_handler; - } - - public setup() { - this.ipc_channel = this.ipc_handler.create_channel(undefined, ConnectHandler.CHANNEL_NAME); - this.ipc_channel.message_handler = this.on_message.bind(this); - } - - private on_message(sender: string, broadcast: boolean, message: ChannelMessage) { - if(broadcast) { - if(message.type == "offer") { - const data = message.data as ConnectOffer; - - const response = { - accepted: this.callback_available(data.data), - request_id: data.request_id - } as ConnectOfferAnswer; - - if(response.accepted) { - log.debug(LogCategory.IPC, tr("Received new connect offer from %s: %s"), sender, data.request_id); - - const ld = { - remote_handler: sender, - data: data.data, - id: data.request_id, - timeout: 0 - }; - this._pending_connect_offers.push(ld); - ld.timeout = setTimeout(() => { - log.debug(LogCategory.IPC, tr("Dropping connect request %s, because we never received an execute."), ld.id); - this._pending_connect_offers.remove(ld); - }, 120 * 1000) as any; - } - this.ipc_channel.send_message("offer-answer", response, sender); - } - } else { - if(message.type == "offer-answer") { - const data = message.data as ConnectOfferAnswer; - const request = this._pending_connects_requests.find(e => e.id === data.request_id); - if(!request) { - log.warn(LogCategory.IPC, tr("Received connect offer answer with unknown request id (%s)."), data.request_id); - return; - } - if(!data.accepted) { - log.debug(LogCategory.IPC, tr("Client %s rejected the connect offer (%s)."), sender, request.id); - return; - } - if(request.remote_handler) { - log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s), but offer has already been accepted."), sender, request.id); - return; - } - - log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s). Request local acceptance."), sender, request.id); - request.remote_handler = sender; - clearTimeout(request.timeout); - - request.callback_avail().then(flag => { - if(!flag) { - request.callback_failed("local avail rejected"); - return; - } - - log.debug(LogCategory.IPC, tr("Executing connect with client %s"), request.remote_handler); - this.ipc_channel.send_message("execute", { - request_id: request.id - } as ConnectExecute, request.remote_handler); - request.timeout = setTimeout(() => { - request.callback_failed("connect execute timeout"); - }, 1000) as any; - }).catch(error => { - log.error(LogCategory.IPC, tr("Local avail callback caused an error: %o"), error); - request.callback_failed(tr("local avail callback caused an error")); - }); - - } - else if(message.type == "executed") { - const data = message.data as ConnectExecuted; - const request = this._pending_connects_requests.find(e => e.id === data.request_id); - if(!request) { - log.warn(LogCategory.IPC, tr("Received connect executed with unknown request id (%s)."), data.request_id); - return; - } - - if(request.remote_handler != sender) { - log.warn(LogCategory.IPC, tr("Received connect executed for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); - return; - } - - log.debug(LogCategory.IPC, tr("Received connect executed response from client %s for request %s. Succeeded: %o (%s)"), sender, data.request_id, data.succeeded, data.message); - clearTimeout(request.timeout); - if(data.succeeded) - request.callback_success(); - else - request.callback_failed(data.message); - } - else if(message.type == "execute") { - const data = message.data as ConnectExecute; - const request = this._pending_connect_offers.find(e => e.id === data.request_id); - if(!request) { - log.warn(LogCategory.IPC, tr("Received connect execute with unknown request id (%s)."), data.request_id); - return; - } - - if(request.remote_handler != sender) { - log.warn(LogCategory.IPC, tr("Received connect execute for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); - return; - } - clearTimeout(request.timeout); - this._pending_connect_offers.remove(request); - - log.debug(LogCategory.IPC, tr("Executing connect for %s"), data.request_id); - const cr = this.callback_execute(request.data); - - const response = { - request_id: data.request_id, - - succeeded: typeof(cr) !== "string" && cr, - message: typeof(cr) === "string" ? cr : "", - } as ConnectExecuted; - this.ipc_channel.send_message("executed", response, request.remote_handler); - } - } - } - - post_connect_request(data: ConnectRequestData, callback_avail: () => Promise) : Promise { - return new Promise((resolve, reject) => { - const pd = { - data: data, - id: uuidv4(), - timeout: 0, - - callback_success: () => { - this._pending_connects_requests.remove(pd); - clearTimeout(pd.timeout); - resolve(); - }, - - callback_failed: error => { - this._pending_connects_requests.remove(pd); - clearTimeout(pd.timeout); - reject(error); - }, - - callback_avail: callback_avail, - }; - this._pending_connects_requests.push(pd); - - this.ipc_channel.send_message("offer", { - request_id: pd.id, - data: pd.data - } as ConnectOffer); - pd.timeout = setTimeout(() => { - pd.callback_failed("received no response to offer"); - }, 50) as any; - }) - } - } + protected abstract on_connected(); + protected abstract on_disconnected(); } +} - export namespace mproxy { - export interface MethodProxyInvokeData { - method_name: string; - arguments: any[]; - promise_id: string; - } - export interface MethodProxyResultData { - promise_id: string; - result: any; - success: boolean; - } - export interface MethodProxyCallback { - promise: Promise; - promise_id: string; +let handler: BasicIPCHandler; +let connect_handler: connect.ConnectHandler; - resolve: (object: any) => any; - reject: (object: any) => any; - } +export function setup() { + if(!supported()) + return; - export type MethodProxyConnectParameters = { - channel_id: string; - client_id: string; - } - export abstract class MethodProxy { - readonly ipc_handler: BasicIPCHandler; - private _ipc_channel: Channel; - private _ipc_parameters: MethodProxyConnectParameters; + handler = new BroadcastChannelIPC(); + handler.setup(); - private readonly _local: boolean; - private readonly _slave: boolean; + connect_handler = new connect.ConnectHandler(handler); + connect_handler.setup(); +} - private _connected: boolean; - private _proxied_methods: {[key: string]:() => Promise} = {}; - private _proxied_callbacks: {[key: string]:MethodProxyCallback} = {}; +export function get_handler() { + return handler; +} - protected constructor(ipc_handler: BasicIPCHandler, connect_params?: MethodProxyConnectParameters) { - this.ipc_handler = ipc_handler; - this._ipc_parameters = connect_params; - this._connected = false; - this._slave = typeof(connect_params) !== "undefined"; - this._local = typeof(connect_params) !== "undefined" && connect_params.channel_id === "local" && connect_params.client_id === "local"; - } +export function get_connect_handler() { + return connect_handler; +} - protected setup() { - if(this._local) { - this._connected = true; - this.on_connected(); - } else { - if(this._slave) - this._ipc_channel = this.ipc_handler.create_channel(this._ipc_parameters.client_id, this._ipc_parameters.channel_id); - else - this._ipc_channel = this.ipc_handler.create_channel(); - - this._ipc_channel.message_handler = this._handle_message.bind(this); - if(this._slave) - this._ipc_channel.send_message("initialize", {}); - } - } - - protected finalize() { - if(!this._local) { - if(this._connected) - this._ipc_channel.send_message("finalize", {}); - - this.ipc_handler.delete_channel(this._ipc_channel); - this._ipc_channel = undefined; - } - for(const promise of Object.values(this._proxied_callbacks)) - promise.reject("disconnected"); - this._proxied_callbacks = {}; - - this._connected = false; - this.on_disconnected(); - } - - protected register_method(method: (...args: any[]) => Promise | string) { - let method_name: string; - if(typeof method === "function") { - log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method.name); - method_name = method.name; - } else { - log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method); - method_name = method; - } - - if(!this[method_name]) - throw "method is missing in current object"; - - this._proxied_methods[method_name] = this[method_name]; - if(!this._local) { - this[method_name] = (...args: any[]) => { - if(!this._connected) - return Promise.reject("not connected"); - - const proxy_callback = { - promise_id: uuidv4() - } as MethodProxyCallback; - this._proxied_callbacks[proxy_callback.promise_id] = proxy_callback; - proxy_callback.promise = new Promise((resolve, reject) => { - proxy_callback.resolve = resolve; - proxy_callback.reject = reject; - }); - - this._ipc_channel.send_message("invoke", { - promise_id: proxy_callback.promise_id, - arguments: [...args], - method_name: method_name - } as MethodProxyInvokeData); - return proxy_callback.promise; - } - } - } - - private _handle_message(remote_id: string, boradcast: boolean, message: ChannelMessage) { - if(message.type === "finalize") { - this._handle_finalize(); - } else if(message.type === "initialize") { - this._handle_remote_callback(remote_id); - } else if(message.type === "invoke") { - this._handle_invoke(message.data); - } else if(message.type === "result") { - this._handle_result(message.data); - } - } - - private _handle_finalize() { - this.on_disconnected(); - this.finalize(); - this._connected = false; - } - - private _handle_remote_callback(remote_id: string) { - if(!this._ipc_channel.target_id) { - if(this._slave) - throw "initialize wrong state!"; - - this._ipc_channel.target_id = remote_id; /* now we're able to send messages */ - this.on_connected(); - this._ipc_channel.send_message("initialize", true); - } else { - if(!this._slave) - throw "initialize wrong state!"; - - this.on_connected(); - } - this._connected = true; - } - - private _send_result(promise_id: string, success: boolean, message: any) { - this._ipc_channel.send_message("result", { - promise_id: promise_id, - result: message, - success: success - } as MethodProxyResultData); - } - - private _handle_invoke(data: MethodProxyInvokeData) { - if(this._proxied_methods[data.method_name]) - throw "we could not invoke a local proxied method!"; - - if(!this[data.method_name]) { - this._send_result(data.promise_id, false, "missing method"); - return; - } - - try { - log.info(LogCategory.IPC, tr("Invoking method %s with arguments: %o"), data.method_name, data.arguments); - - const promise = this[data.method_name](...data.arguments); - promise.then(result => { - log.info(LogCategory.IPC, tr("Result: %o"), result); - this._send_result(data.promise_id, true, result); - }).catch(error => { - this._send_result(data.promise_id, false, error); - }); - } catch(error) { - this._send_result(data.promise_id, false, error); - return; - } - } - - private _handle_result(data: MethodProxyResultData) { - if(!this._proxied_callbacks[data.promise_id]) { - console.warn(tr("Received proxy method result for unknown promise")); - return; - } - const callback = this._proxied_callbacks[data.promise_id]; - delete this._proxied_callbacks[data.promise_id]; - - if(data.success) - callback.resolve(data.result); - else - callback.reject(data.result); - } - - generate_connect_parameters() : MethodProxyConnectParameters { - if(this._slave) - throw "only masters can generate connect parameters!"; - if(!this._ipc_channel) - throw "please call setup() before"; - - return { - channel_id: this._ipc_channel.channel_id, - client_id: this.ipc_handler.get_local_address() - }; - } - - is_slave() { return this._local || this._slave; } /* the popout modal */ - is_master() { return this._local || !this._slave; } /* the host (teaweb application) */ - - protected abstract on_connected(); - protected abstract on_disconnected(); - } - } - - let handler: BasicIPCHandler; - let connect_handler: connect.ConnectHandler; - - export function setup() { - if(!supported()) - return; - - handler = new BroadcastChannelIPC(); - handler.setup(); - - connect_handler = new connect.ConnectHandler(handler); - connect_handler.setup(); - } - - export function get_handler() { - return handler; - } - - export function get_connect_handler() { - return connect_handler; - } - - export function supported() { - /* ios does not support this */ - return typeof(window.BroadcastChannel) !== "undefined"; - } +export function supported() { + /* ios does not support this */ + return typeof(window.BroadcastChannel) !== "undefined"; } \ No newline at end of file diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts index 85c3b8af..8a0b6731 100644 --- a/shared/js/ConnectionHandler.ts +++ b/shared/js/ConnectionHandler.ts @@ -1,14 +1,38 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// +import {ChannelTree} from "tc-shared/ui/view"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {PermissionManager} from "tc-shared/permission/PermissionManager"; +import {GroupManager} from "tc-shared/permission/GroupManager"; +import {ServerSettings, Settings, StaticSettings} from "tc-shared/settings"; +import {Sound, SoundManager} from "tc-shared/sound/Sounds"; +import {LocalClientEntry} from "tc-shared/ui/client"; +import {ServerLog} from "tc-shared/ui/frames/server_log"; +import {ConnectionProfile, default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile"; +import {ServerAddress} from "tc-shared/ui/server"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import * as server_log from "tc-shared/ui/frames/server_log"; +import {createErrorModal, createInfoModal, createInputModal, Modal} from "tc-shared/ui/elements/Modal"; +import {hashPassword} from "tc-shared/utils/helpers"; +import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler"; +import * as htmltags from "./ui/htmltags"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {InputStartResult, InputState} from "tc-shared/voice/RecorderBase"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {guid} from "tc-shared/crypto/uid"; +import * as bipc from "./BrowserIPC"; +import {FileManager, spawn_upload_transfer, UploadKey} from "tc-shared/FileManager"; +import {RecorderProfile} from "tc-shared/voice/RecorderProfile"; +import {Frame} from "tc-shared/ui/frames/chat_frame"; +import {Hostbanner} from "tc-shared/ui/frames/hostbanner"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {connection_log, Regex} from "tc-shared/ui/modal/ModalConnect"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {spawnAvatarUpload} from "tc-shared/ui/modal/ModalAvatar"; +import * as connection from "tc-backend/connection"; +import * as dns from "tc-backend/dns"; -enum DisconnectReason { +export enum DisconnectReason { HANDLER_DESTROYED, REQUESTED, DNS_FAILED, @@ -28,7 +52,7 @@ enum DisconnectReason { UNKNOWN } -enum ConnectionState { +export enum ConnectionState { UNCONNECTED, CONNECTING, INITIALISING, @@ -36,7 +60,7 @@ enum ConnectionState { DISCONNECTING } -enum ViewReasonId { +export enum ViewReasonId { VREASON_USER_ACTION = 0, VREASON_MOVED = 1, VREASON_SYSTEM = 2, @@ -51,7 +75,7 @@ enum ViewReasonId { VREASON_SERVER_SHUTDOWN = 11 } -interface VoiceStatus { +export interface VoiceStatus { input_hardware: boolean; input_muted: boolean; output_muted: boolean; @@ -68,7 +92,7 @@ interface VoiceStatus { queries_visible: boolean; } -interface ConnectParameters { +export interface ConnectParameters { nickname?: string; channel?: { target: string | number; @@ -79,20 +103,21 @@ interface ConnectParameters { auto_reconnect_attempt?: boolean; } -class ConnectionHandler { +declare const native_client; +export class ConnectionHandler { channelTree: ChannelTree; - serverConnection: connection.AbstractServerConnection; + serverConnection: AbstractServerConnection; fileManager: FileManager; permissions: PermissionManager; groups: GroupManager; - side_bar: chat.Frame; + side_bar: Frame; settings: ServerSettings; - sound: sound.SoundManager; + sound: SoundManager; hostbanner: Hostbanner; @@ -121,15 +146,15 @@ class ConnectionHandler { }; invoke_resized_on_activate: boolean = false; - log: log.ServerLog; + log: ServerLog; constructor() { this.settings = new ServerSettings(); - this.log = new log.ServerLog(this); + this.log = new ServerLog(this); this.channelTree = new ChannelTree(this); - this.side_bar = new chat.Frame(this); - this.sound = new sound.SoundManager(this); + this.side_bar = new Frame(this); + this.sound = new SoundManager(this); this.hostbanner = new Hostbanner(this); this.serverConnection = connection.spawn_server_connection(this); @@ -169,7 +194,7 @@ class ConnectionHandler { setup() { } - async startConnection(addr: string, profile: profiles.ConnectionProfile, user_action: boolean, parameters: ConnectParameters) { + async startConnection(addr: string, profile: ConnectionProfile, user_action: boolean, parameters: ConnectParameters) { this.tab_set_name(tr("Connecting")); this.cancel_reconnect(false); this._reconnect_attempt = parameters.auto_reconnect_attempt || false; @@ -192,7 +217,7 @@ class ConnectionHandler { } } log.info(LogCategory.CLIENT, tr("Start connection to %s:%d"), server_address.host, server_address.port); - this.log.log(log.server.Type.CONNECTION_BEGIN, { + this.log.log(server_log.Type.CONNECTION_BEGIN, { address: { server_hostname: server_address.host, server_port: server_address.port @@ -203,7 +228,7 @@ class ConnectionHandler { if(parameters.password && !parameters.password.hashed){ try { - const password = await helpers.hashPassword(parameters.password.password); + const password = await hashPassword(parameters.password.password); parameters.password = { hashed: true, password: password @@ -221,9 +246,9 @@ class ConnectionHandler { } const original_address = {host: server_address.host, port: server_address.port}; - if(dns.supported() && !server_address.host.match(Modals.Regex.IP_V4) && !server_address.host.match(Modals.Regex.IP_V6)) { + if(dns.supported() && !server_address.host.match(Regex.IP_V4) && !server_address.host.match(Regex.IP_V6)) { const id = ++this._connect_initialize_id; - this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE, {}); + this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE, {}); try { const resolved = await dns.resolve_address(server_address, { timeout: 5000 }) || {} as any; if(id != this._connect_initialize_id) @@ -231,7 +256,7 @@ class ConnectionHandler { server_address.host = typeof(resolved.target_ip) === "string" ? resolved.target_ip : server_address.host; server_address.port = typeof(resolved.target_port) === "number" ? resolved.target_port : server_address.port; - this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVED, { + this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVED, { address: { server_port: server_address.port, server_hostname: server_address.host @@ -245,7 +270,7 @@ class ConnectionHandler { } } - await this.serverConnection.connect(server_address, new connection.HandshakeHandler(profile, parameters)); + await this.serverConnection.connect(server_address, new HandshakeHandler(profile, parameters)); setTimeout(() => { const connected = this.serverConnection.connected(); if(user_action && connected) { @@ -270,7 +295,7 @@ class ConnectionHandler { return this._clientId; } - getServerConnection() : connection.AbstractServerConnection { return this.serverConnection; } + getServerConnection() : AbstractServerConnection { return this.serverConnection; } /** @@ -380,7 +405,7 @@ class ConnectionHandler { popup.close(); /* no need, but nicer */ - const profile = profiles.find_profile(properties.connect_profile) || profiles.default_profile(); + const profile = find_profile(properties.connect_profile) || default_profile(); const cprops = this.reconnect_properties(profile); this.startConnection(properties.connect_address, profile, true, cprops); }); @@ -418,12 +443,12 @@ class ConnectionHandler { case DisconnectReason.HANDLER_DESTROYED: if(data) { this.sound.play(Sound.CONNECTION_DISCONNECTED); - this.log.log(log.server.Type.DISCONNECTED, {}); + this.log.log(server_log.Type.DISCONNECTED, {}); } break; case DisconnectReason.DNS_FAILED: log.error(LogCategory.CLIENT, tr("Failed to resolve hostname: %o"), data); - this.log.log(log.server.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, { + this.log.log(server_log.Type.CONNECTION_HOSTNAME_RESOLVE_ERROR, { message: data as any }); this.sound.play(Sound.CONNECTION_REFUSED); @@ -431,7 +456,7 @@ class ConnectionHandler { case DisconnectReason.CONNECT_FAILURE: if(this._reconnect_attempt) { auto_reconnect = true; - this.log.log(log.server.Type.CONNECTION_FAILED, {}); + this.log.log(server_log.Type.CONNECTION_FAILED, {}); break; } if(data) @@ -452,7 +477,7 @@ class ConnectionHandler { this._certificate_modal = createErrorModal( tr("Could not connect"), - MessageHelper.formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept()) + formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept()) ); this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined); this._certificate_modal.open(); @@ -470,7 +495,7 @@ class ConnectionHandler { case DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED: createErrorModal( tr("Target server is a TeamSpeak server"), - MessageHelper.formatMessage(tr("The target server is a TeamSpeak 3 server!{:br:}Only TeamSpeak 3 based identities are able to connect.{:br:}Please select another profile or change the identify type.")) + formatMessage(tr("The target server is a TeamSpeak 3 server!{:br:}Only TeamSpeak 3 based identities are able to connect.{:br:}Please select another profile or change the identify type.")) ).open(); this.sound.play(Sound.CONNECTION_DISCONNECTED); auto_reconnect = false; @@ -478,7 +503,7 @@ class ConnectionHandler { case DisconnectReason.IDENTITY_TOO_LOW: createErrorModal( tr("Identity level is too low"), - MessageHelper.formatMessage(tr("You've been disconnected, because your Identity level is too low.{:br:}You need at least a level of {0}"), data["extra_message"]) + formatMessage(tr("You've been disconnected, because your Identity level is too low.{:br:}You need at least a level of {0}"), data["extra_message"]) ).open(); this.sound.play(Sound.CONNECTION_DISCONNECTED); @@ -506,7 +531,7 @@ class ConnectionHandler { break; case DisconnectReason.SERVER_CLOSED: - this.log.log(log.server.Type.SERVER_CLOSED, {message: data.reasonmsg}); + this.log.log(server_log.Type.SERVER_CLOSED, {message: data.reasonmsg}); createErrorModal( tr("Server closed"), @@ -518,7 +543,7 @@ class ConnectionHandler { auto_reconnect = true; break; case DisconnectReason.SERVER_REQUIRES_PASSWORD: - this.log.log(log.server.Type.SERVER_REQUIRES_PASSWORD, {}); + this.log.log(server_log.Type.SERVER_REQUIRES_PASSWORD, {}); createInputModal(tr("Server password"), tr("Enter server password:"), password => password.length != 0, password => { if(!(typeof password === "string")) return; @@ -540,7 +565,7 @@ class ConnectionHandler { const have_invoker = typeof(data["invokerid"]) !== "undefined" && parseInt(data["invokerid"]) !== 0; const modal = createErrorModal( tr("You've been kicked"), - MessageHelper.formatMessage( + formatMessage( have_invoker ? tr("You've been kicked from the server by {0}:{:br:}{1}") : tr("You've been kicked from the server:{:br:}{1}"), have_invoker ? htmltags.generate_client_object({ client_id: parseInt(data["invokerid"]), client_unique_id: data["invokeruid"], client_name: data["invokername"]}) : @@ -559,7 +584,7 @@ class ConnectionHandler { this.sound.play(Sound.CONNECTION_BANNED); break; case DisconnectReason.CLIENT_BANNED: - this.log.log(log.server.Type.SERVER_BANNED, { + this.log.log(server_log.Type.SERVER_BANNED, { invoker: { client_name: data["invokername"], client_id: parseInt(data["invokerid"]), @@ -592,7 +617,7 @@ class ConnectionHandler { log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect but cant reconnect because we dont have any information left...")); return; } - this.log.log(log.server.Type.RECONNECT_SCHEDULED, {timeout: 5000}); + this.log.log(server_log.Type.RECONNECT_SCHEDULED, {timeout: 5000}); log.info(LogCategory.NETWORKING, tr("Allowed to auto reconnect. Reconnecting in 5000ms")); const server_address = this.serverConnection.remote_address(); @@ -600,7 +625,7 @@ class ConnectionHandler { this._reconnect_timer = setTimeout(() => { this._reconnect_timer = undefined; - this.log.log(log.server.Type.RECONNECT_EXECUTE, {}); + this.log.log(server_log.Type.RECONNECT_EXECUTE, {}); log.info(LogCategory.NETWORKING, tr("Reconnecting...")); this.startConnection(server_address.host + ":" + server_address.port, profile, false, Object.assign(this.reconnect_properties(profile), {auto_reconnect_attempt: true})); @@ -610,7 +635,7 @@ class ConnectionHandler { cancel_reconnect(log_event: boolean) { if(this._reconnect_timer) { - if(log_event) this.log.log(log.server.Type.RECONNECT_CANCELED, {}); + if(log_event) this.log.log(server_log.Type.RECONNECT_CANCELED, {}); clearTimeout(this._reconnect_timer); this._reconnect_timer = undefined; } @@ -665,7 +690,7 @@ class ConnectionHandler { if(Object.keys(property_update).length > 0) { this.serverConnection.send_command("clientupdate", property_update).catch(error => { log.warn(LogCategory.GENERAL, tr("Failed to update client audio hardware properties. Error: %o"), error); - this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")}); + this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update audio hardware properties.")}); /* Update these properties anyways (for case the server fails to handle the command) */ const updates = []; @@ -708,15 +733,15 @@ class ConnectionHandler { const input = vconnection.voice_recorder().input; if(input) { if(active && this.serverConnection.connected()) { - if(input.current_state() === audio.recorder.InputState.PAUSED) { + if(input.current_state() === InputState.PAUSED) { input.start().then(result => { - if(result != audio.recorder.InputStartResult.EOK) + if(result != InputStartResult.EOK) throw result; }).catch(error => { log.warn(LogCategory.VOICE, tr("Failed to start microphone input (%s)."), error); if(Date.now() - (this._last_record_error_popup || 0) > 10 * 1000) { this._last_record_error_popup = Date.now(); - createErrorModal(tr("Failed to start recording"), MessageHelper.formatMessage(tr("Microphone start failed.{:br:}Error: {}"), error)).open(); + createErrorModal(tr("Failed to start recording"), formatMessage(tr("Microphone start failed.{:br:}Error: {}"), error)).open(); } }); } @@ -741,7 +766,7 @@ class ConnectionHandler { client_output_hardware: this.client_status.sound_playback_supported }).catch(error => { log.warn(LogCategory.GENERAL, tr("Failed to sync handler state with server. Error: %o"), error); - this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")}); + this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to sync handler state with server.")}); }); } @@ -761,7 +786,7 @@ class ConnectionHandler { client_away_message: typeof(this.client_status.away) === "string" ? this.client_status.away : "", }).catch(error => { log.warn(LogCategory.GENERAL, tr("Failed to update away status. Error: %o"), error); - this.log.log(log.server.Type.ERROR_CUSTOM, {message: tr("Failed to update away status.")}); + this.log.log(server_log.Type.ERROR_CUSTOM, {message: tr("Failed to update away status.")}); }); control_bar.update_button_away(); @@ -781,10 +806,10 @@ class ConnectionHandler { }); } - reconnect_properties(profile?: profiles.ConnectionProfile) : ConnectParameters { + reconnect_properties(profile?: ConnectionProfile) : ConnectParameters { const name = (this.getClient() ? this.getClient().clientNickName() : "") || (this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") || - settings.static_global(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) || + StaticSettings.instance.static(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) || "Another TeaSpeak user"; const channel = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().channelId : 0) || (this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {} as any).target : ""); @@ -798,7 +823,7 @@ class ConnectionHandler { } update_avatar() { - Modals.spawnAvatarUpload(data => { + spawnAvatarUpload(data => { if(typeof(data) === "undefined") return; if(data === null) { @@ -814,16 +839,16 @@ class ConnectionHandler { let message; if(error instanceof CommandResult) - message = MessageHelper.formatMessage(tr("Failed to delete avatar.{:br:}Error: {0}"), error.extra_message || error.message); + message = formatMessage(tr("Failed to delete avatar.{:br:}Error: {0}"), error.extra_message || error.message); if(!message) - message = MessageHelper.formatMessage(tr("Failed to delete avatar.{:br:}Lookup the console for more details")); + message = formatMessage(tr("Failed to delete avatar.{:br:}Lookup the console for more details")); createErrorModal(tr("Failed to delete avatar"), message).open(); return; }); } else { log.info(LogCategory.CLIENT, tr("Uploading new avatar")); (async () => { - let key: transfer.UploadKey; + let key: UploadKey; try { key = await this.fileManager.upload_file({ size: data.byteLength, @@ -840,28 +865,28 @@ class ConnectionHandler { //TODO: Resolve permission name //i_client_max_avatar_filesize if(error.id == ErrorID.PERMISSION_ERROR) { - message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Missing permission {0}"), error["failed_permid"]); + message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Missing permission {0}"), error["failed_permid"]); } else { - message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Error: {0}"), error.extra_message || error.message); + message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Error: {0}"), error.extra_message || error.message); } } if(!message) - message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details")); + message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details")); createErrorModal(tr("Failed to upload avatar"), message).open(); return; } try { - await transfer.spawn_upload_transfer(key).put_data(data); + await spawn_upload_transfer(key).put_data(data); } catch(error) { log.error(LogCategory.GENERAL, tr("Failed to upload avatar: %o"), error); let message; if(typeof(error) === "string") - message = MessageHelper.formatMessage(tr("Failed to upload avatar.{:br:}Error: {0}"), error); + message = formatMessage(tr("Failed to upload avatar.{:br:}Error: {0}"), error); if(!message) - message = MessageHelper.formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details")); + message = formatMessage(tr("Failed to initialize avatar upload.{:br:}Lookup the console for more details")); createErrorModal(tr("Failed to upload avatar"), message).open(); return; } @@ -874,9 +899,9 @@ class ConnectionHandler { let message; if(error instanceof CommandResult) - message = MessageHelper.formatMessage(tr("Failed to update avatar flag.{:br:}Error: {0}"), error.extra_message || error.message); + message = formatMessage(tr("Failed to update avatar flag.{:br:}Error: {0}"), error.extra_message || error.message); if(!message) - message = MessageHelper.formatMessage(tr("Failed to update avatar flag.{:br:}Lookup the console for more details")); + message = formatMessage(tr("Failed to update avatar flag.{:br:}Lookup the console for more details")); createErrorModal(tr("Failed to set avatar"), message).open(); return; } diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index e20cc336..a1167ffe 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -1,76 +1,80 @@ -/// -/// +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {ServerCommand} from "tc-shared/connection/ConnectionBase"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ClientEntry} from "tc-shared/ui/client"; +import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler"; -class FileEntry { +export class FileEntry { name: string; datetime: number; type: number; size: number; } -class FileListRequest { +export class FileListRequest { path: string; entries: FileEntry[]; callback: (entries: FileEntry[]) => void; } -namespace transfer { - export interface TransferKey { - client_transfer_id: number; - server_transfer_id: number; +export interface TransferKey { + client_transfer_id: number; + server_transfer_id: number; - key: string; + key: string; - file_path: string; - file_name: string; + file_path: string; + file_name: string; - peer: { - hosts: string[], - port: number; - }; + peer: { + hosts: string[], + port: number; + }; - total_size: number; - } - - export interface UploadOptions { - name: string; - path: string; - - channel?: ChannelEntry; - channel_password?: string; - - size: number; - overwrite: boolean; - } - - export interface DownloadTransfer { - get_key() : DownloadKey; - - request_file() : Promise; - } - - export interface UploadTransfer { - get_key(): UploadKey; - - put_data(data: BlobPart | File) : Promise; - } - - export type DownloadKey = TransferKey; - export type UploadKey = TransferKey; - - export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer { - return new RequestFileDownload(key); - } - export function spawn_upload_transfer(key: UploadKey) : UploadTransfer { - return new RequestFileUpload(key); - } + total_size: number; } -class RequestFileDownload implements transfer.DownloadTransfer { - readonly transfer_key: transfer.DownloadKey; +export interface UploadOptions { + name: string; + path: string; - constructor(key: transfer.DownloadKey) { + channel?: ChannelEntry; + channel_password?: string; + + size: number; + overwrite: boolean; +} + +export interface DownloadTransfer { + get_key() : DownloadKey; + + request_file() : Promise; +} + +export interface UploadTransfer { + get_key(): UploadKey; + + put_data(data: BlobPart | File) : Promise; +} + +export type DownloadKey = TransferKey; +export type UploadKey = TransferKey; + +export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer { + return new RequestFileDownload(key); +} +export function spawn_upload_transfer(key: UploadKey) : UploadTransfer { + return new RequestFileUpload(key); +} + +export class RequestFileDownload implements DownloadTransfer { + readonly transfer_key: DownloadKey; + + constructor(key: DownloadKey) { this.transfer_key = key; } @@ -97,18 +101,18 @@ class RequestFileDownload implements transfer.DownloadTransfer { return response; } - get_key(): transfer.DownloadKey { + get_key(): DownloadKey { return this.transfer_key; } } -class RequestFileUpload implements transfer.UploadTransfer { - readonly transfer_key: transfer.UploadKey; - constructor(key: transfer.DownloadKey) { +export class RequestFileUpload implements UploadTransfer { + readonly transfer_key: UploadKey; + constructor(key: DownloadKey) { this.transfer_key = key; } - get_key(): transfer.UploadKey { + get_key(): UploadKey { return this.transfer_key; } @@ -152,14 +156,14 @@ class RequestFileUpload implements transfer.UploadTransfer { } } -class FileManager extends connection.AbstractCommandHandler { +export class FileManager extends AbstractCommandHandler { handle: ConnectionHandler; icons: IconManager; avatars: AvatarManager; private listRequests: FileListRequest[] = []; - private pending_download_requests: transfer.DownloadKey[] = []; - private pending_upload_requests: transfer.UploadKey[] = []; + private pending_download_requests: DownloadKey[] = []; + private pending_upload_requests: UploadKey[] = []; private transfer_counter : number = 1; @@ -191,7 +195,7 @@ class FileManager extends connection.AbstractCommandHandler { this.avatars = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyfilelist": this.notifyFileList(command.arguments); @@ -276,15 +280,15 @@ class FileManager extends connection.AbstractCommandHandler { /******************************** File download/upload ********************************/ - download_file(path: string, file: string, channel?: ChannelEntry, password?: string) : Promise { - const transfer_data: transfer.DownloadKey = { + download_file(path: string, file: string, channel?: ChannelEntry, password?: string) : Promise { + const transfer_data: DownloadKey = { file_name: file, file_path: path, client_transfer_id: this.transfer_counter++ } as any; this.pending_download_requests.push(transfer_data); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { transfer_data["_callback"] = resolve; this.handle.serverConnection.send_command("ftinitdownload", { "path": path, @@ -301,8 +305,8 @@ class FileManager extends connection.AbstractCommandHandler { }); } - upload_file(options: transfer.UploadOptions) : Promise { - const transfer_data: transfer.UploadKey = { + upload_file(options: UploadOptions) : Promise { + const transfer_data: UploadKey = { file_path: options.path, file_name: options.name, client_transfer_id: this.transfer_counter++, @@ -310,7 +314,7 @@ class FileManager extends connection.AbstractCommandHandler { } as any; this.pending_upload_requests.push(transfer_data); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { transfer_data["_callback"] = resolve; this.handle.serverConnection.send_command("ftinitupload", { "path": options.path, @@ -333,7 +337,7 @@ class FileManager extends connection.AbstractCommandHandler { json = json[0]; let clientftfid = parseInt(json["clientftfid"]); - let transfer: transfer.DownloadKey; + let transfer: DownloadKey; for(let e of this.pending_download_requests) if(e.client_transfer_id == clientftfid) { transfer = e; @@ -355,14 +359,14 @@ class FileManager extends connection.AbstractCommandHandler { if(transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0') transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host; - (transfer["_callback"] as (val: transfer.DownloadKey) => void)(transfer); + (transfer["_callback"] as (val: DownloadKey) => void)(transfer); this.pending_download_requests.remove(transfer); } private notifyStartUpload(json) { json = json[0]; - let transfer: transfer.UploadKey; + let transfer: UploadKey; let clientftfid = parseInt(json["clientftfid"]); for(let e of this.pending_upload_requests) if(e.client_transfer_id == clientftfid) { @@ -384,7 +388,7 @@ class FileManager extends connection.AbstractCommandHandler { if(transfer.peer.hosts[0].length == 0 || transfer.peer.hosts[0] == '0.0.0.0') transfer.peer.hosts[0] = this.handle.serverConnection.remote_address().host; - (transfer["_callback"] as (val: transfer.UploadKey) => void)(transfer); + (transfer["_callback"] as (val: UploadKey) => void)(transfer); this.pending_upload_requests.remove(transfer); } @@ -411,12 +415,12 @@ class FileManager extends connection.AbstractCommandHandler { } } -class Icon { +export class Icon { id: number; url: string; } -enum ImageType { +export enum ImageType { UNKNOWN, BITMAP, PNG, @@ -425,7 +429,7 @@ enum ImageType { JPEG } -function media_image_type(type: ImageType, file?: boolean) { +export function media_image_type(type: ImageType, file?: boolean) { switch (type) { case ImageType.BITMAP: return "bmp"; @@ -442,7 +446,7 @@ function media_image_type(type: ImageType, file?: boolean) { } } -function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean) { +export function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean) { const ab2str10 = () => { const buf = new Uint8Array(encoded_data as ArrayBuffer); if(buf.byteLength < 10) @@ -472,7 +476,7 @@ function image_type(encoded_data: string | ArrayBuffer, base64_encoded?: boolean return ImageType.UNKNOWN; } -class CacheManager { +export class CacheManager { readonly cache_name: string; private _cache_category: Cache; @@ -547,7 +551,7 @@ class CacheManager { } } -class IconManager { +export class IconManager { private static cache: CacheManager = new CacheManager("icons"); handle: FileManager; @@ -590,7 +594,7 @@ class IconManager { return this.handle.requestFileList("/icons"); } - create_icon_download(id: number) : Promise { + create_icon_download(id: number) : Promise { return this.handle.download_file("", "/icon_" + id); } @@ -665,7 +669,7 @@ class IconManager { private async _load_icon(id: number) : Promise { try { - let download_key: transfer.DownloadKey; + let download_key: DownloadKey; try { download_key = await this.create_icon_download(id); } catch(error) { @@ -673,7 +677,7 @@ class IconManager { throw "Failed to request icon"; } - const downloader = transfer.spawn_download_transfer(download_key); + const downloader = spawn_download_transfer(download_key); let response: Response; try { response = await downloader.request_file(); @@ -801,14 +805,14 @@ class IconManager { } } -class Avatar { +export class Avatar { client_avatar_id: string; /* the base64 uid thing from a-m */ avatar_id: string; /* client_flag_avatar */ url: string; type: ImageType; } -class AvatarManager { +export class AvatarManager { handle: FileManager; private static cache: CacheManager; @@ -867,14 +871,14 @@ class AvatarManager { }; } - create_avatar_download(client_avatar_id: string) : Promise { + create_avatar_download(client_avatar_id: string) : Promise { log.debug(LogCategory.GENERAL, "Requesting download for avatar %s", client_avatar_id); return this.handle.download_file("", "/avatar_" + client_avatar_id); } private async _load_avatar(client_avatar_id: string, avatar_version: string) { try { - let download_key: transfer.DownloadKey; + let download_key: DownloadKey; try { download_key = await this.create_avatar_download(client_avatar_id); } catch(error) { @@ -882,7 +886,7 @@ class AvatarManager { throw "failed to request avatar download"; } - const downloader = transfer.spawn_download_transfer(download_key); + const downloader = spawn_download_transfer(download_key); let response: Response; try { response = await downloader.request_file(); diff --git a/shared/js/MessageFormatter.ts b/shared/js/MessageFormatter.ts index 66bb2d7f..462423c0 100644 --- a/shared/js/MessageFormatter.ts +++ b/shared/js/MessageFormatter.ts @@ -1,250 +1,280 @@ -namespace messages.formatter { - export namespace bbcode { - const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]"; - const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/; - const sanitizer_escaped_map: {[key: string]: string} = {}; +import {Settings, settings} from "tc-shared/settings"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import {guid} from "tc-shared/crypto/uid"; +import * as loader from "tc-loader"; +import * as image_preview from "./ui/frames/image_preview" - const yt_url_regex = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; +declare const xbbcode; +export namespace bbcode { + const sanitizer_escaped = (key: string) => "[-- sescaped: " + key + " --]"; + const sanitizer_escaped_regex = /\[-- sescaped: ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) --]/; + const sanitizer_escaped_map: {[key: string]: string} = {}; - export interface FormatSettings { - is_chat_message?: boolean - } + const yt_url_regex = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; - export function format(message: string, fsettings?: FormatSettings) : JQuery[] { - fsettings = fsettings || {}; - - single_url_parse: - if(fsettings.is_chat_message) { - /* try if its only one url */ - const raw_url = message.replace(/\[url(=\S+)?](\S+)\[\/url]/, "$2"); - let url: URL; - try { - url = new URL(raw_url); - } catch(error) { - break single_url_parse; - } - - single_url_yt: - { - const result = raw_url.match(yt_url_regex); - if(!result) break single_url_yt; - - return format("[yt]https://www.youtube.com/watch?v=" + result[5] + "[/yt]"); - } - - single_url_image: - { - const ext_index = url.pathname.lastIndexOf("."); - if(ext_index == -1) break single_url_image; - - const ext_name = url.pathname.substr(ext_index + 1).toLowerCase(); - if([ - "jpeg", "jpg", - "png", "bmp", "gif", - "tiff", "pdf", "svg" - ].findIndex(e => e === ext_name) == -1) break single_url_image; - - return format("[img]" + message + "[/img]"); - } - } - - const result = xbbcode.parse(message, { - tag_whitelist: [ - "b", "big", - "i", "italic", - "u", "underlined", - "s", "strikethrough", - "color", - "url", - "code", - "i-code", "icode", - "sub", "sup", - "size", - "hr", "br", - - "ul", "ol", "list", - "li", - - "table", - "tr", "td", "th", - - "yt", "youtube", - "img" - ] - }); - let html = result.build_html(); - if(typeof(window.twemoji) !== "undefined" && settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)) - html = twemoji.parse(html); - - const container = $.spawn("div"); - let sanitized = DOMPurify.sanitize(html, { - ADD_ATTR: [ - "x-highlight-type", - "x-code-type", - "x-image-url" - ] - }); - - sanitized = sanitized.replace(sanitizer_escaped_regex, data => { - const uid = data.match(sanitizer_escaped_regex)[1]; - const value = sanitizer_escaped_map[uid]; - if(!value) return data; - delete sanitizer_escaped_map[uid]; - - return value; - }); - - container[0].innerHTML = sanitized; - - - container.find("a") - .attr('target', "_blank") - .on('contextmenu', event => { - if(event.isDefaultPrevented()) return; - event.preventDefault(); - - const url = $(event.target).attr("href"); - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - callback: () => { - const win = window.open(url, '_blank'); - win.focus(); - }, - name: tr("Open URL"), - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-browse-addon-online" - }, { - callback: () => { - //TODO - }, - name: tr("Open URL in Browser"), - type: contextmenu.MenuEntryType.ENTRY, - visible: !app.is_web() && false // Currently not possible - }, contextmenu.Entry.HR(), { - callback: () => copy_to_clipboard(url), - name: tr("Copy URL to clipboard"), - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-copy" - }); - }); - - return [container.contents() as JQuery]; - //return result.root_tag.content.map(e => e.build_html()).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)); - } - - export function load_image(entry: HTMLImageElement) { - const url = decodeURIComponent(entry.getAttribute("x-image-url") || ""); - const proxy_url = "https://images.weserv.nl/?url=" + encodeURIComponent(url); - - entry.onload = undefined; - entry.src = proxy_url; - - const parent = $(entry.parentElement); - parent.on('contextmenu', event => { - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - callback: () => { - const win = window.open(url, '_blank'); - win.focus(); - }, - name: tr("Open image in browser"), - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-browse-addon-online" - }, contextmenu.Entry.HR(), { - callback: () => copy_to_clipboard(url), - name: tr("Copy image URL to clipboard"), - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-copy" - }) - }); - parent.css("cursor", "pointer").on('click', event => image_preview.preview_image(proxy_url, url)); - } - - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "XBBCode code tag init", - function: async () => { - /* override default parser */ - xbbcode.register.register_parser({ - tag: ["code", "icode", "i-code"], - content_tags_whitelist: [], - - build_html(layer) : string { - const klass = layer.tag_normalized != 'code' ? "tag-hljs-inline-code" : "tag-hljs-code"; - const language = (layer.options || "").replace("\"", "'").toLowerCase(); - - /* remove heading empty lines */ - let text = layer.content.map(e => e.build_text()) - .reduce((a, b) => a.length == 0 && b.replace(/[ \n\r\t]+/g, "").length == 0 ? "" : a + b, "") - .replace(/^([ \n\r\t]*)(?=\n)+/g, ""); - if(text.startsWith("\r") || text.startsWith("\n")) - text = text.substr(1); - - let result: HighlightJSResult; - if(window.hljs.getLanguage(language)) - result = window.hljs.highlight(language, text, true); - else - result = window.hljs.highlightAuto(text); - - let html = '
';
-                        html += '';
-                        html += result.value;
-                        return html + "
"; - } - }); - - /* override the yt parser */ - const original_parser = xbbcode.register.find_parser("yt"); - if(original_parser) - xbbcode.register.register_parser({ - tag: ["yt", "youtube"], - build_html(layer): string { - const result = original_parser.build_html(layer); - if(!result.startsWith(""; - return sanitizer_escaped(uid); - } - }); - - /* the image parse & displayer */ - xbbcode.register.register_parser({ - tag: ["img", "image"], - build_html(layer): string { - const uid = guid(); - const fallback_value = "[img]" + layer.build_text() + "[/img]"; - - let target; - let content = layer.content.map(e => e.build_text()).join(""); - if (!layer.options) { - target = content; - } else - target = layer.options; - - let url: URL; - try { - url = new URL(target); - if(!url.hostname) throw ""; - } catch(error) { - return fallback_value; - } - - sanitizer_escaped_map[uid] = "
"; - return sanitizer_escaped(uid); - } - }) - }, - priority: 10 - }); + export interface FormatSettings { + is_chat_message?: boolean } - export function sanitize_text(text: string) : string { - return $(DOMPurify.sanitize("" + text + "", { + export function format(message: string, fsettings?: FormatSettings) : JQuery[] { + fsettings = fsettings || {}; + + single_url_parse: + if(fsettings.is_chat_message) { + /* try if its only one url */ + const raw_url = message.replace(/\[url(=\S+)?](\S+)\[\/url]/, "$2"); + let url: URL; + try { + url = new URL(raw_url); + } catch(error) { + break single_url_parse; + } + + single_url_yt: + { + const result = raw_url.match(yt_url_regex); + if(!result) break single_url_yt; + + return format("[yt]https://www.youtube.com/watch?v=" + result[5] + "[/yt]"); + } + + single_url_image: + { + const ext_index = url.pathname.lastIndexOf("."); + if(ext_index == -1) break single_url_image; + + const ext_name = url.pathname.substr(ext_index + 1).toLowerCase(); + if([ + "jpeg", "jpg", + "png", "bmp", "gif", + "tiff", "pdf", "svg" + ].findIndex(e => e === ext_name) == -1) break single_url_image; + + return format("[img]" + message + "[/img]"); + } + } + + const result = xbbcode.parse(message, { + tag_whitelist: [ + "b", "big", + "i", "italic", + "u", "underlined", + "s", "strikethrough", + "color", + "url", + "code", + "i-code", "icode", + "sub", "sup", + "size", + "hr", "br", + + "ul", "ol", "list", + "li", + + "table", + "tr", "td", "th", + + "yt", "youtube", + "img" + ] + }); + let html = result.build_html(); + if(typeof(window.twemoji) !== "undefined" && settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)) + html = twemoji.parse(html); + + const container = $.spawn("div"); + let sanitized = DOMPurify.sanitize(html, { ADD_ATTR: [ "x-highlight-type", "x-code-type", "x-image-url" ] - })).text(); + }); + + sanitized = sanitized.replace(sanitizer_escaped_regex, data => { + const uid = data.match(sanitizer_escaped_regex)[1]; + const value = sanitizer_escaped_map[uid]; + if(!value) return data; + delete sanitizer_escaped_map[uid]; + + return value; + }); + + container[0].innerHTML = sanitized; + + + container.find("a") + .attr('target', "_blank") + .on('contextmenu', event => { + if(event.isDefaultPrevented()) return; + event.preventDefault(); + + const url = $(event.target).attr("href"); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + callback: () => { + const win = window.open(url, '_blank'); + win.focus(); + }, + name: tr("Open URL"), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-browse-addon-online" + }, { + callback: () => { + //TODO + }, + name: tr("Open URL in Browser"), + type: contextmenu.MenuEntryType.ENTRY, + visible: loader.version().type === "native" && false // Currently not possible + }, contextmenu.Entry.HR(), { + callback: () => copy_to_clipboard(url), + name: tr("Copy URL to clipboard"), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-copy" + }); + }); + + return [container.contents() as JQuery]; + //return result.root_tag.content.map(e => e.build_html()).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)); } + + export function load_image(entry: HTMLImageElement) { + const url = decodeURIComponent(entry.getAttribute("x-image-url") || ""); + const proxy_url = "https://images.weserv.nl/?url=" + encodeURIComponent(url); + + entry.onload = undefined; + entry.src = proxy_url; + + const parent = $(entry.parentElement); + parent.on('contextmenu', event => { + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + callback: () => { + const win = window.open(url, '_blank'); + win.focus(); + }, + name: tr("Open image in browser"), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-browse-addon-online" + }, contextmenu.Entry.HR(), { + callback: () => copy_to_clipboard(url), + name: tr("Copy image URL to clipboard"), + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-copy" + }) + }); + parent.css("cursor", "pointer").on('click', event => image_preview.preview_image(proxy_url, url)); + } + + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "XBBCode code tag init", + function: async () => { + /* override default parser */ + xbbcode.register.register_parser({ + tag: ["code", "icode", "i-code"], + content_tags_whitelist: [], + + build_html(layer) : string { + const klass = layer.tag_normalized != 'code' ? "tag-hljs-inline-code" : "tag-hljs-code"; + const language = (layer.options || "").replace("\"", "'").toLowerCase(); + + /* remove heading empty lines */ + let text = layer.content.map(e => e.build_text()) + .reduce((a, b) => a.length == 0 && b.replace(/[ \n\r\t]+/g, "").length == 0 ? "" : a + b, "") + .replace(/^([ \n\r\t]*)(?=\n)+/g, ""); + if(text.startsWith("\r") || text.startsWith("\n")) + text = text.substr(1); + + let result: HighlightJSResult; + if(window.hljs.getLanguage(language)) + result = window.hljs.highlight(language, text, true); + else + result = window.hljs.highlightAuto(text); + + let html = '
';
+                    html += '';
+                    html += result.value;
+                    return html + "
"; + } + }); + + /* override the yt parser */ + const original_parser = xbbcode.register.find_parser("yt"); + if(original_parser) + xbbcode.register.register_parser({ + tag: ["yt", "youtube"], + build_html(layer): string { + const result = original_parser.build_html(layer); + if(!result.startsWith(""; + return sanitizer_escaped(uid); + } + }); + + /* the image parse & displayer */ + xbbcode.register.register_parser({ + tag: ["img", "image"], + build_html(layer): string { + const uid = guid(); + const fallback_value = "[img]" + layer.build_text() + "[/img]"; + + let target; + let content = layer.content.map(e => e.build_text()).join(""); + if (!layer.options) { + target = content; + } else + target = layer.options; + + let url: URL; + try { + url = new URL(target); + if(!url.hostname) throw ""; + } catch(error) { + return fallback_value; + } + + sanitizer_escaped_map[uid] = "
"; + return sanitizer_escaped(uid); + } + }) + }, + priority: 10 + }); +} + +export function sanitize_text(text: string) : string { + return $(DOMPurify.sanitize("" + text + "", { + ADD_ATTR: [ + "x-highlight-type", + "x-code-type", + "x-image-url" + ] + })).text(); +} + +export function formatDate(secs: number) : string { + let years = Math.floor(secs / (60 * 60 * 24 * 365)); + let days = Math.floor(secs / (60 * 60 * 24)) % 365; + let hours = Math.floor(secs / (60 * 60)) % 24; + let minutes = Math.floor(secs / 60) % 60; + let seconds = Math.floor(secs % 60); + + let result = ""; + if(years > 0) + result += years + " " + tr("years") + " "; + if(years > 0 || days > 0) + result += days + " " + tr("days") + " "; + if(years > 0 || days > 0 || hours > 0) + result += hours + " " + tr("hours") + " "; + if(years > 0 || days > 0 || hours > 0 || minutes > 0) + result += minutes + " " + tr("minutes") + " "; + if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) + result += seconds + " " + tr("seconds") + " "; + else + result = tr("now") + " "; + + return result.substr(0, result.length - 1); } \ No newline at end of file diff --git a/shared/js/PPTListener.ts b/shared/js/PPTListener.ts index b0d32f5d..d0439914 100644 --- a/shared/js/PPTListener.ts +++ b/shared/js/PPTListener.ts @@ -1,4 +1,4 @@ -enum KeyCode { +export enum KeyCode { KEY_CANCEL = 3, KEY_HELP = 6, KEY_BACK_SPACE = 8, @@ -118,59 +118,57 @@ enum KeyCode { KEY_META = 224 } -namespace ppt { - export enum EventType { - KEY_PRESS, - KEY_RELEASE, - KEY_TYPED - } +export enum EventType { + KEY_PRESS, + KEY_RELEASE, + KEY_TYPED +} - export enum SpecialKey { - CTRL, - WINDOWS, - SHIFT, - ALT - } +export enum SpecialKey { + CTRL, + WINDOWS, + SHIFT, + ALT +} - export interface KeyDescriptor { - key_code: string; +export interface KeyDescriptor { + key_code: string; - key_ctrl: boolean; - key_windows: boolean; - key_shift: boolean; - key_alt: boolean; - } + key_ctrl: boolean; + key_windows: boolean; + key_shift: boolean; + key_alt: boolean; +} - export interface KeyEvent extends KeyDescriptor { - readonly type: EventType; +export interface KeyEvent extends KeyDescriptor { + readonly type: EventType; - readonly key: string; - } + readonly key: string; +} - export interface KeyHook extends KeyDescriptor { - cancel: boolean; +export interface KeyHook extends KeyDescriptor { + cancel: boolean; - callback_press: () => any; - callback_release: () => any; - } + callback_press: () => any; + callback_release: () => any; +} - export function key_description(key: KeyDescriptor) { - let result = ""; - if(key.key_shift) - result += " + " + tr("Shift"); - if(key.key_alt) - result += " + " + tr("Alt"); - if(key.key_ctrl) - result += " + " + tr("CTRL"); - if(key.key_windows) - result += " + " + tr("Win"); +export function key_description(key: KeyDescriptor) { + let result = ""; + if(key.key_shift) + result += " + " + tr("Shift"); + if(key.key_alt) + result += " + " + tr("Alt"); + if(key.key_ctrl) + result += " + " + tr("CTRL"); + if(key.key_windows) + result += " + " + tr("Win"); - if(!result && !key.key_code) - return tr("unset"); + if(!result && !key.key_code) + return tr("unset"); - if(key.key_code) - result += " + " + key.key_code; - return result.substr(3); - } + if(key.key_code) + result += " + " + key.key_code; + return result.substr(3); } \ No newline at end of file diff --git a/shared/js/audio/audio.ts b/shared/js/audio/audio.ts deleted file mode 100644 index 4314f775..00000000 --- a/shared/js/audio/audio.ts +++ /dev/null @@ -1,10 +0,0 @@ -namespace audio { - export namespace player { - export interface Device { - device_id: string; - - driver: string; - name: string; - } - } -} \ No newline at end of file diff --git a/shared/js/audio/player.ts b/shared/js/audio/player.ts new file mode 100644 index 00000000..0afb5f7c --- /dev/null +++ b/shared/js/audio/player.ts @@ -0,0 +1,6 @@ +export interface Device { + device_id: string; + + driver: string; + name: string; +} \ No newline at end of file diff --git a/shared/js/bookmarks.ts b/shared/js/bookmarks.ts index cb12ca5e..c16a79bf 100644 --- a/shared/js/bookmarks.ts +++ b/shared/js/bookmarks.ts @@ -1,262 +1,260 @@ -namespace bookmarks { - function guid() { - function s4() { - return Math - .floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {guid} from "tc-shared/crypto/uid"; +import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import {default_profile, find_profile} from "tc-shared/profiles/ConnectionProfile"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import * as top_menu from "./ui/frames/MenuBar"; + +export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => { + const profile = find_profile(mark.connect_profile) || default_profile(); + if(profile.valid()) { + const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection_handler() : server_connections.spawn_server_connection_handler(); + server_connections.set_active_connection_handler(connection); + connection.startConnection( + mark.server_properties.server_address + ":" + mark.server_properties.server_port, + profile, + true, + { + nickname: mark.nickname === "Another TeaSpeak user" || !mark.nickname ? profile.connect_username() : mark.nickname, + password: mark.server_properties.server_password_hash ? { + password: mark.server_properties.server_password_hash, + hashed: true + } : mark.server_properties.server_password ? { + hashed: false, + password: mark.server_properties.server_password + } : undefined + } + ); + } else { + spawnConnectModal({}, { + url: mark.server_properties.server_address + ":" + mark.server_properties.server_port, + enforce: true + }, { + profile: profile, + enforce: true + }) } +}; - export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => { - const profile = profiles.find_profile(mark.connect_profile) || profiles.default_profile(); - if(profile.valid()) { - const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection_handler() : server_connections.spawn_server_connection_handler(); - server_connections.set_active_connection_handler(connection); - connection.startConnection( - mark.server_properties.server_address + ":" + mark.server_properties.server_port, - profile, - true, - { - nickname: mark.nickname === "Another TeaSpeak user" || !mark.nickname ? profile.connect_username() : mark.nickname, - password: mark.server_properties.server_password_hash ? { - password: mark.server_properties.server_password_hash, - hashed: true - } : mark.server_properties.server_password ? { - hashed: false, - password: mark.server_properties.server_password - } : undefined - } - ); - } else { - Modals.spawnConnectModal({}, { - url: mark.server_properties.server_address + ":" + mark.server_properties.server_port, - enforce: true - }, { - profile: profile, - enforce: true - }) - } - }; +export interface ServerProperties { + server_address: string; + server_port: number; + server_password_hash?: string; + server_password?: string; +} - export interface ServerProperties { - server_address: string; - server_port: number; - server_password_hash?: string; - server_password?: string; - } +export enum BookmarkType { + ENTRY, + DIRECTORY +} - export enum BookmarkType { - ENTRY, - DIRECTORY - } +export interface Bookmark { + type: /* BookmarkType.ENTRY */ BookmarkType; + /* readonly */ parent: DirectoryBookmark; - export interface Bookmark { - type: /* BookmarkType.ENTRY */ BookmarkType; - /* readonly */ parent: DirectoryBookmark; + server_properties: ServerProperties; + display_name: string; + unique_id: string; - server_properties: ServerProperties; - display_name: string; - unique_id: string; + nickname: string; + default_channel?: number | string; + default_channel_password_hash?: string; + default_channel_password?: string; - nickname: string; - default_channel?: number | string; - default_channel_password_hash?: string; - default_channel_password?: string; + connect_profile: string; - connect_profile: string; + last_icon_id?: number; +} - last_icon_id?: number; - } +export interface DirectoryBookmark { + type: /* BookmarkType.DIRECTORY */ BookmarkType; + /* readonly */ parent: DirectoryBookmark; - export interface DirectoryBookmark { - type: /* BookmarkType.DIRECTORY */ BookmarkType; - /* readonly */ parent: DirectoryBookmark; + readonly content: (Bookmark | DirectoryBookmark)[]; + unique_id: string; + display_name: string; +} - readonly content: (Bookmark | DirectoryBookmark)[]; - unique_id: string; - display_name: string; - } +interface BookmarkConfig { + root_bookmark?: DirectoryBookmark; + default_added?: boolean; +} - interface BookmarkConfig { - root_bookmark?: DirectoryBookmark; - default_added?: boolean; - } - - let _bookmark_config: BookmarkConfig; - - function bookmark_config() : BookmarkConfig { - if(_bookmark_config) - return _bookmark_config; - - let bookmark_json = localStorage.getItem("bookmarks"); - let bookmarks; - try { - bookmarks = JSON.parse(bookmark_json) || {} as BookmarkConfig; - } catch(error) { - log.error(LogCategory.BOOKMARKS, tr("Failed to load bookmarks: %o"), error); - bookmarks = {} as any; - } - - _bookmark_config = bookmarks; - _bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY} as DirectoryBookmark; - - if(!_bookmark_config.default_added) { - _bookmark_config.default_added = true; - create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, { - server_address: "ts.teaspeak.de", - server_port: 9987 - }, undefined); - - save_config(); - } - - const fix_parent = (parent: DirectoryBookmark, entry: Bookmark | DirectoryBookmark) => { - entry.parent = parent; - if(entry.type === BookmarkType.DIRECTORY) - for(const child of (entry as DirectoryBookmark).content) - fix_parent(entry as DirectoryBookmark, child); - }; - for(const entry of _bookmark_config.root_bookmark.content) - fix_parent(_bookmark_config.root_bookmark, entry); +let _bookmark_config: BookmarkConfig; +function bookmark_config() : BookmarkConfig { + if(_bookmark_config) return _bookmark_config; + + let bookmark_json = localStorage.getItem("bookmarks"); + let bookmarks; + try { + bookmarks = JSON.parse(bookmark_json) || {} as BookmarkConfig; + } catch(error) { + log.error(LogCategory.BOOKMARKS, tr("Failed to load bookmarks: %o"), error); + bookmarks = {} as any; } - function save_config() { - localStorage.setItem("bookmarks", JSON.stringify(bookmark_config(), (key, value) => { - if(key === "parent") - return undefined; - return value; - })); + _bookmark_config = bookmarks; + _bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY} as DirectoryBookmark; + + if(!_bookmark_config.default_added) { + _bookmark_config.default_added = true; + create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, { + server_address: "ts.teaspeak.de", + server_port: 9987 + }, undefined); + + save_config(); } - export function bookmarks() : DirectoryBookmark { - return bookmark_config().root_bookmark; - } + const fix_parent = (parent: DirectoryBookmark, entry: Bookmark | DirectoryBookmark) => { + entry.parent = parent; + if(entry.type === BookmarkType.DIRECTORY) + for(const child of (entry as DirectoryBookmark).content) + fix_parent(entry as DirectoryBookmark, child); + }; + for(const entry of _bookmark_config.root_bookmark.content) + fix_parent(_bookmark_config.root_bookmark, entry); - export function bookmarks_flat() : Bookmark[] { - const result: Bookmark[] = []; - const _flat = (bookmark: Bookmark | DirectoryBookmark) => { - if(bookmark.type == BookmarkType.DIRECTORY) - for(const book of (bookmark as DirectoryBookmark).content) - _flat(book); - else - result.push(bookmark as Bookmark); - }; - _flat(bookmark_config().root_bookmark); - return result; - } + return _bookmark_config; +} - function find_bookmark_recursive(parent: DirectoryBookmark, uuid: string) : Bookmark | DirectoryBookmark { - for(const entry of parent.content) { - if(entry.unique_id == uuid) - return entry; - if(entry.type == BookmarkType.DIRECTORY) { - const result = find_bookmark_recursive(entry as DirectoryBookmark, uuid); - if(result) return result; - } - } - return undefined; - } +function save_config() { + localStorage.setItem("bookmarks", JSON.stringify(bookmark_config(), (key, value) => { + if(key === "parent") + return undefined; + return value; + })); +} - export function find_bookmark(uuid: string) : Bookmark | DirectoryBookmark | undefined { - return find_bookmark_recursive(bookmarks(), uuid); - } +export function bookmarks() : DirectoryBookmark { + return bookmark_config().root_bookmark; +} - export function parent_bookmark(bookmark: Bookmark) : DirectoryBookmark { - const books: (DirectoryBookmark | Bookmark)[] = [bookmarks()]; - while(!books.length) { - const directory = books.pop_front(); - if(directory.type == BookmarkType.DIRECTORY) { - const cast = directory; - - if(cast.content.indexOf(bookmark) != -1) - return cast; - books.push(...cast.content); - } - } - return bookmarks(); - } - - export function create_bookmark(display_name: string, directory: DirectoryBookmark, server_properties: ServerProperties, nickname: string) : Bookmark { - const bookmark = { - display_name: display_name, - server_properties: server_properties, - nickname: nickname, - type: BookmarkType.ENTRY, - connect_profile: "default", - unique_id: guid(), - parent: directory - } as Bookmark; - - directory.content.push(bookmark); - return bookmark; - } - - export function create_bookmark_directory(parent: DirectoryBookmark, name: string) : DirectoryBookmark { - const bookmark = { - type: BookmarkType.DIRECTORY, - - display_name: name, - content: [], - unique_id: guid(), - parent: parent - } as DirectoryBookmark; - - parent.content.push(bookmark); - return bookmark; - } - - //TODO test if the new parent is within the old bookmark - export function change_directory(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { - delete_bookmark(bookmark); - parent.content.push(bookmark) - } - - export function save_bookmark(bookmark?: Bookmark | DirectoryBookmark) { - save_config(); /* nvm we dont give a fuck... saving everything */ - } - - function delete_bookmark_recursive(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { - const index = parent.content.indexOf(bookmark); - if(index != -1) - parent.content.remove(bookmark); +export function bookmarks_flat() : Bookmark[] { + const result: Bookmark[] = []; + const _flat = (bookmark: Bookmark | DirectoryBookmark) => { + if(bookmark.type == BookmarkType.DIRECTORY) + for(const book of (bookmark as DirectoryBookmark).content) + _flat(book); else - for(const entry of parent.content) - if(entry.type == BookmarkType.DIRECTORY) - delete_bookmark_recursive(entry as DirectoryBookmark, bookmark) - } + result.push(bookmark as Bookmark); + }; + _flat(bookmark_config().root_bookmark); + return result; +} - export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) { - delete_bookmark_recursive(bookmarks(), bookmark) - } - - export function add_current_server() { - const ch = server_connections.active_connection_handler(); - if(ch && ch.connected) { - const ce = ch.getClient(); - const name = ce ? ce.clientNickName() : undefined; - createInputModal(tr("Enter bookmarks name"), tr("Please enter the bookmarks name:
"), text => text.length > 0, result => { - if(result) { - const bookmark = create_bookmark(result as string, bookmarks(), { - server_port: ch.serverConnection.remote_address().port, - server_address: ch.serverConnection.remote_address().host, - - server_password: "", - server_password_hash: "" - }, name); - save_bookmark(bookmark); - - control_bar.update_bookmarks(); - top_menu.rebuild_bookmarks(); - - createInfoModal(tr("Server added"), tr("Server has been successfully added to your bookmarks.")).open(); - } - }).open(); - } else { - createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open(); +function find_bookmark_recursive(parent: DirectoryBookmark, uuid: string) : Bookmark | DirectoryBookmark { + for(const entry of parent.content) { + if(entry.unique_id == uuid) + return entry; + if(entry.type == BookmarkType.DIRECTORY) { + const result = find_bookmark_recursive(entry as DirectoryBookmark, uuid); + if(result) return result; } } + return undefined; +} + +export function find_bookmark(uuid: string) : Bookmark | DirectoryBookmark | undefined { + return find_bookmark_recursive(bookmarks(), uuid); +} + +export function parent_bookmark(bookmark: Bookmark) : DirectoryBookmark { + const books: (DirectoryBookmark | Bookmark)[] = [bookmarks()]; + while(!books.length) { + const directory = books.pop_front(); + if(directory.type == BookmarkType.DIRECTORY) { + const cast = directory; + + if(cast.content.indexOf(bookmark) != -1) + return cast; + books.push(...cast.content); + } + } + return bookmarks(); +} + +export function create_bookmark(display_name: string, directory: DirectoryBookmark, server_properties: ServerProperties, nickname: string) : Bookmark { + const bookmark = { + display_name: display_name, + server_properties: server_properties, + nickname: nickname, + type: BookmarkType.ENTRY, + connect_profile: "default", + unique_id: guid(), + parent: directory + } as Bookmark; + + directory.content.push(bookmark); + return bookmark; +} + +export function create_bookmark_directory(parent: DirectoryBookmark, name: string) : DirectoryBookmark { + const bookmark = { + type: BookmarkType.DIRECTORY, + + display_name: name, + content: [], + unique_id: guid(), + parent: parent + } as DirectoryBookmark; + + parent.content.push(bookmark); + return bookmark; +} + +//TODO test if the new parent is within the old bookmark +export function change_directory(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark(bookmark); + parent.content.push(bookmark) +} + +export function save_bookmark(bookmark?: Bookmark | DirectoryBookmark) { + save_config(); /* nvm we dont give a fuck... saving everything */ +} + +function delete_bookmark_recursive(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + const index = parent.content.indexOf(bookmark); + if(index != -1) + parent.content.remove(bookmark); + else + for(const entry of parent.content) + if(entry.type == BookmarkType.DIRECTORY) + delete_bookmark_recursive(entry as DirectoryBookmark, bookmark) +} + +export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark_recursive(bookmarks(), bookmark) +} + +export function add_current_server() { + const ch = server_connections.active_connection_handler(); + if(ch && ch.connected) { + const ce = ch.getClient(); + const name = ce ? ce.clientNickName() : undefined; + createInputModal(tr("Enter bookmarks name"), tr("Please enter the bookmarks name:
"), text => text.length > 0, result => { + if(result) { + const bookmark = create_bookmark(result as string, bookmarks(), { + server_port: ch.serverConnection.remote_address().port, + server_address: ch.serverConnection.remote_address().host, + + server_password: "", + server_password_hash: "" + }, name); + save_bookmark(bookmark); + + control_bar.update_bookmarks(); + top_menu.rebuild_bookmarks(); + + createInfoModal(tr("Server added"), tr("Server has been successfully added to your bookmarks.")).open(); + } + }).open(); + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open(); + } } \ No newline at end of file diff --git a/shared/js/connection/AbstractCommandHandler.ts b/shared/js/connection/AbstractCommandHandler.ts new file mode 100644 index 00000000..c545574c --- /dev/null +++ b/shared/js/connection/AbstractCommandHandler.ts @@ -0,0 +1,98 @@ +import { + AbstractServerConnection, + ServerCommand, + SingleCommandHandler +} from "tc-shared/connection/ConnectionBase"; + +export abstract class AbstractCommandHandler { + readonly connection: AbstractServerConnection; + + handler_boss: AbstractCommandHandlerBoss | undefined; + volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ + + ignore_consumed: boolean = false; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + /** + * @return If the command should be consumed + */ + abstract handle_command(command: ServerCommand) : boolean; +} + +export abstract class AbstractCommandHandlerBoss { + readonly connection: AbstractServerConnection; + protected command_handlers: AbstractCommandHandler[] = []; + /* TODO: Timeout */ + protected single_command_handler: SingleCommandHandler[] = []; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + destroy() { + this.command_handlers = undefined; + this.single_command_handler = undefined; + } + + register_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; + + this.command_handlers.remove(handler); /* just to be sure */ + this.command_handlers.push(handler); + handler.handler_boss = this; + } + + unregister_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); + return; + } + + this.command_handlers.remove(handler); + handler.handler_boss = undefined; + } + + + register_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.push(handler); + } + + remove_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.remove(handler); + } + + handlers() : AbstractCommandHandler[] { + return this.command_handlers; + } + + invoke_handle(command: ServerCommand) : boolean { + let flag_consumed = false; + + for(const handler of this.command_handlers) { + try { + if(!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } catch(error) { + console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); + } + } + + for(const handler of [...this.single_command_handler]) { + if(handler.command && handler.command != command.command) + continue; + + try { + if(handler.function(command)) + this.single_command_handler.remove(handler); + } catch(error) { + console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); + } + } + + return flag_consumed; + } +} \ No newline at end of file diff --git a/shared/js/connection/CommandHandler.ts b/shared/js/connection/CommandHandler.ts index f5e4bfed..1620d8b5 100644 --- a/shared/js/connection/CommandHandler.ts +++ b/shared/js/connection/CommandHandler.ts @@ -1,309 +1,465 @@ -/// +import * as log from "tc-shared/log"; +import * as server_log from "tc-shared/ui/frames/server_log"; +import { + AbstractServerConnection, CommandOptions, ServerCommand +} from "tc-shared/connection/ConnectionBase"; +import {Sound} from "tc-shared/sound/Sounds"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {LogCategory} from "tc-shared/log"; +import {createErrorModal, createInfoModal, createInputModal, createModal} from "tc-shared/ui/elements/Modal"; +import { + ClientConnectionInfo, + ClientEntry, + ClientType, + LocalClientEntry, + MusicClientEntry, + SongInfo +} from "tc-shared/ui/client"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ConnectionHandler, DisconnectReason, ViewReasonId} from "tc-shared/ConnectionHandler"; +import {bbcode_chat, formatMessage} from "tc-shared/ui/frames/chat"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {spawnPoke} from "tc-shared/ui/modal/ModalPoke"; +import {PrivateConversationState} from "tc-shared/ui/frames/side/private_conversations"; +import {Conversation} from "tc-shared/ui/frames/side/conversations"; +import {AbstractCommandHandler, AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler"; -namespace connection { - import Conversation = chat.channel.Conversation; - import MusicInfo = chat.MusicInfo; +export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { + constructor(connection: AbstractServerConnection) { + super(connection); + } +} - export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { - constructor(connection: AbstractServerConnection) { - super(connection); +export class ConnectionCommandHandler extends AbstractCommandHandler { + readonly connection: AbstractServerConnection; + readonly connection_handler: ConnectionHandler; + + constructor(connection: AbstractServerConnection) { + super(connection); + this.connection_handler = connection.client; + + this["error"] = this.handleCommandResult; + this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; + this["notifychannelcreated"] = this.handleCommandChannelCreate; + this["notifychanneldeleted"] = this.handleCommandChannelDelete; + this["notifychannelhide"] = this.handleCommandChannelHide; + this["notifychannelshow"] = this.handleCommandChannelShow; + + this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; + this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; + + this["notifycliententerview"] = this.handleCommandClientEnterView; + this["notifyclientleftview"] = this.handleCommandClientLeftView; + this["notifyclientmoved"] = this.handleNotifyClientMoved; + this["initserver"] = this.handleCommandServerInit; + this["notifychannelmoved"] = this.handleNotifyChannelMoved; + this["notifychanneledited"] = this.handleNotifyChannelEdited; + this["notifytextmessage"] = this.handleNotifyTextMessage; + this["notifyclientchatcomposing"] = this.notifyClientChatComposing; + this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; + this["notifyclientupdated"] = this.handleNotifyClientUpdated; + this["notifyserveredited"] = this.handleNotifyServerEdited; + this["notifyserverupdated"] = this.handleNotifyServerUpdated; + + this["notifyclientpoke"] = this.handleNotifyClientPoke; + + this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; + + this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; + this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; + this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; + + this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; + this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; + + this["notifyconversationhistory"] = this.handleNotifyConversationHistory; + this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; + + this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; + this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; + + this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; + this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; + this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; + this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + } + + private loggable_invoker(unique_id, client_id, name) : server_log.base.Client | undefined { + const id = parseInt(client_id); + if(typeof(client_id) === "undefined" || Number.isNaN(id)) + return undefined; + + if(id == 0) + return { + client_id: 0, + client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, + client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, + }; + + return { + client_unique_id: unique_id, + client_name: name, + client_id: client_id + }; + } + + proxy_command_promise(promise: Promise, options: CommandOptions) { + if(!options.process_result) + return promise; + + return promise.catch(ex => { + if(options.process_result) { + if(ex instanceof CommandResult) { + let res = ex; + if(!res.success) { + if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error + const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); + res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); + this.connection_handler.log.log(server_log.Type.ERROR_PERMISSION, { + permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) + }); + this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } else if(res.id != ErrorID.EMPTY_RESULT) { + this.connection_handler.log.log(server_log.Type.ERROR_CUSTOM, { + message: res.extra_message.length == 0 ? res.message : res.extra_message + }); + } + } + } else if(typeof(ex) === "string") { + this.connection_handler.log.log(server_log.Type.CONNECTION_COMMAND_ERROR, {error: ex}); + } else { + log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); + } + } + + return Promise.reject(ex); + }); + } + + handle_command(command: ServerCommand) : boolean { + if(this[command.command]) { + this[command.command](command.arguments); + return true; + } + + return false; + } + + set_handler(command: string, handler: any) { + this[command] = handler; + } + + unset_handler(command: string, handler?: any) { + if(handler && this[command] != handler) return; + this[command] = undefined; + } + + handleCommandResult(json) { + json = json[0]; //Only one bulk + + let code : string = json["return_code"]; + if(!code || code.length == 0) { + log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); + return; + } + let retListeners = this.connection["_retListener"]; + + for(let e of retListeners) { + if(e.code != code) continue; + retListeners.remove(e); + let result = new CommandResult(json); + if(result.success) + e.resolve(result); + else + e.reject(result); + break; } } - export class ConnectionCommandHandler extends AbstractCommandHandler { - readonly connection: AbstractServerConnection; - readonly connection_handler: ConnectionHandler; - - constructor(connection: AbstractServerConnection) { - super(connection); - this.connection_handler = connection.client; - - this["error"] = this.handleCommandResult; - this["channellist"] = this.handleCommandChannelList; - this["channellistfinished"] = this.handleCommandChannelListFinished; - this["notifychannelcreated"] = this.handleCommandChannelCreate; - this["notifychanneldeleted"] = this.handleCommandChannelDelete; - this["notifychannelhide"] = this.handleCommandChannelHide; - this["notifychannelshow"] = this.handleCommandChannelShow; - - this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo; - this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo; - - this["notifycliententerview"] = this.handleCommandClientEnterView; - this["notifyclientleftview"] = this.handleCommandClientLeftView; - this["notifyclientmoved"] = this.handleNotifyClientMoved; - this["initserver"] = this.handleCommandServerInit; - this["notifychannelmoved"] = this.handleNotifyChannelMoved; - this["notifychanneledited"] = this.handleNotifyChannelEdited; - this["notifytextmessage"] = this.handleNotifyTextMessage; - this["notifyclientchatcomposing"] = this.notifyClientChatComposing; - this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed; - this["notifyclientupdated"] = this.handleNotifyClientUpdated; - this["notifyserveredited"] = this.handleNotifyServerEdited; - this["notifyserverupdated"] = this.handleNotifyServerUpdated; - - this["notifyclientpoke"] = this.handleNotifyClientPoke; - - this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; - - this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; - this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; - this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; - - this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed; - this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed; - - this["notifyconversationhistory"] = this.handleNotifyConversationHistory; - this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete; - - this["notifymusicstatusupdate"] = this.handleNotifyMusicStatusUpdate; - this["notifymusicplayersongchange"] = this.handleMusicPlayerSongChange; - - this["notifyplaylistsongadd"] = this.handleNotifyPlaylistSongAdd; - this["notifyplaylistsongremove"] = this.handleNotifyPlaylistSongRemove; - this["notifyplaylistsongreorder"] = this.handleNotifyPlaylistSongReorder; - this["notifyplaylistsongloaded"] = this.handleNotifyPlaylistSongLoaded; + handleCommandServerInit(json){ + //We could setup the voice channel + if(this.connection.support_voice()) { + log.debug(LogCategory.NETWORKING, tr("Setting up voice")); + } else { + log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); } - private loggable_invoker(unique_id, client_id, name) : log.server.base.Client | undefined { - const id = parseInt(client_id); - if(typeof(client_id) === "undefined" || Number.isNaN(id)) - return undefined; - if(id == 0) - return { - client_id: 0, - client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier, - client_name: this.connection_handler.channelTree.server.properties.virtualserver_name, - }; + json = json[0]; //Only one bulk - return { - client_unique_id: unique_id, - client_name: name, - client_id: client_id - }; + this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); + this.connection.client.side_bar.channel_conversations().reset(); + this.connection.client.clientId = parseInt(json["aclid"]); + this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "aclid") continue; + if(key === "acn") continue; + + updates.push({key: key, value: json[key]}); } + this.connection.client.channelTree.server.updateVariables(false, ...updates); - proxy_command_promise(promise: Promise, options: connection.CommandOptions) { - if(!options.process_result) - return promise; - - return promise.catch(ex => { - if(options.process_result) { - if(ex instanceof CommandResult) { - let res = ex; - if(!res.success) { - if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error - const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number); - res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown"); - this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, { - permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number) - }); - this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } else if(res.id != ErrorID.EMPTY_RESULT) { - this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, { - message: res.extra_message.length == 0 ? res.message : res.extra_message - }); - } - } - } else if(typeof(ex) === "string") { - this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, {error: ex}); - } else { - log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex); - } - } - - return Promise.reject(ex); - }); - } - - handle_command(command: ServerCommand) : boolean { - if(this[command.command]) { - this[command.command](command.arguments); - return true; - } - - return false; - } - - set_handler(command: string, handler: any) { - this[command] = handler; - } - - unset_handler(command: string, handler?: any) { - if(handler && this[command] != handler) return; - this[command] = undefined; - } - - handleCommandResult(json) { - json = json[0]; //Only one bulk - - let code : string = json["return_code"]; - if(!code || code.length == 0) { - log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json); - return; - } - let retListeners = this.connection["_retListener"]; - - for(let e of retListeners) { - if(e.code != code) continue; - retListeners.remove(e); - let result = new CommandResult(json); - if(result.success) - e.resolve(result); - else - e.reject(result); - break; - } - } - - handleCommandServerInit(json){ - //We could setup the voice channel - if(this.connection.support_voice()) { - log.debug(LogCategory.NETWORKING, tr("Setting up voice")); - } else { - log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)")); - } - - - json = json[0]; //Only one bulk - - this.connection_handler.channelTree.registerClient(this.connection_handler.getClient()); - this.connection.client.side_bar.channel_conversations().reset(); - this.connection.client.clientId = parseInt(json["aclid"]); - this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]}); - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "aclid") continue; - if(key === "acn") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - - const properties = this.connection.client.channelTree.server.properties; - /* host message */ - if(properties.virtualserver_hostmessage_mode > 0) { - if(properties.virtualserver_hostmessage_mode == 1) { - /* show in log */ - this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, { - message: properties.virtualserver_hostmessage - }); - } else { - /* create modal/create modal and quit */ - createModal({ - header: tr("Host message"), - body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage), - footer: undefined - }).open(); - - if(properties.virtualserver_hostmessage_mode == 3) { - /* first let the client initialize his stuff */ - setTimeout(() => { - this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, { - message: properties.virtualserver_welcomemessage - }); - - this.connection.disconnect("host message disconnect"); - this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); - this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); - }, 100); - } - } - } - - /* welcome message */ - if(properties.virtualserver_welcomemessage) { - this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, { - message: properties.virtualserver_welcomemessage + const properties = this.connection.client.channelTree.server.properties; + /* host message */ + if(properties.virtualserver_hostmessage_mode > 0) { + if(properties.virtualserver_hostmessage_mode == 1) { + /* show in log */ + this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE, { + message: properties.virtualserver_hostmessage }); - } + } else { + /* create modal/create modal and quit */ + createModal({ + header: tr("Host message"), + body: bbcode_chat(properties.virtualserver_hostmessage), + footer: undefined + }).open(); - /* priviledge key */ - if(properties.virtualserver_ask_for_privilegekey) { - createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { - if(!result) return; - const scon = server_connections.active_connection_handler(); - - if(scon.serverConnection.connected) - scon.serverConnection.send_command("tokenuse", { - token: result - }).then(() => { - createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); - }).catch(error => { - createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); + if(properties.virtualserver_hostmessage_mode == 3) { + /* first let the client initialize his stuff */ + setTimeout(() => { + this.connection_handler.log.log(server_log.Type.SERVER_HOST_MESSAGE_DISCONNECT, { + message: properties.virtualserver_welcomemessage }); - }, { field_placeholder: 'Enter Privilege Key' }).open(); - } - this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, { - own_client: this.connection_handler.getClient().log_data() - }); - this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); - this.connection.client.onConnected(); - } - - handleNotifyServerConnectionInfo(json) { - json = json[0]; - - /* everything is a number, so lets parse it */ - for(const key of Object.keys(json)) - json[key] = parseFloat(json[key]); - - this.connection_handler.channelTree.server.set_connection_info(json); - } - - handleNotifyConnectionInfo(json) { - json = json[0]; - - const object = new ClientConnectionInfo(); - /* everything is a number (except ip), so lets parse it */ - for(const key of Object.keys(json)) { - if(key === "connection_client_ip") - object[key] = json[key]; - else - object[key] = parseFloat(json[key]); - } - - const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); - if(!client) { - log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); - return; - } - - client.set_connection_info(object); - } - - private createChannelFromJson(json, ignoreOrder: boolean = false) { - let tree = this.connection.client.channelTree; - - let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); - tree.insertChannel(channel); - if(json["channel_order"] !== "0") { - let prev = tree.findChannel(json["channel_order"]); - if(!prev && json["channel_order"] != 0) { - if(!ignoreOrder) { - log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); - return; - } + this.connection.disconnect("host message disconnect"); + this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE); + this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); + }, 100); } + } + } - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + /* welcome message */ + if(properties.virtualserver_welcomemessage) { + this.connection_handler.log.log(server_log.Type.SERVER_WELCOME_MESSAGE, { + message: properties.virtualserver_welcomemessage + }); + } + + /* priviledge key */ + if(properties.virtualserver_ask_for_privilegekey) { + createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => { + if(!result) return; + const scon = server_connections.active_connection_handler(); + + if(scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open(); + }).catch(error => { + createErrorModal(tr("Use privilege key"), formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open(); + }); + }, { field_placeholder: 'Enter Privilege Key' }).open(); + } + + this.connection_handler.log.log(server_log.Type.CONNECTION_CONNECTED, { + own_client: this.connection_handler.getClient().log_data() + }); + this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED); + this.connection.client.onConnected(); + } + + handleNotifyServerConnectionInfo(json) { + json = json[0]; + + /* everything is a number, so lets parse it */ + for(const key of Object.keys(json)) + json[key] = parseFloat(json[key]); + + this.connection_handler.channelTree.server.set_connection_info(json); + } + + handleNotifyConnectionInfo(json) { + json = json[0]; + + const object = new ClientConnectionInfo(); + /* everything is a number (except ip), so lets parse it */ + for(const key of Object.keys(json)) { + if(key === "connection_client_ip") + object[key] = json[key]; + else + object[key] = parseFloat(json[key]); + } + + const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"])); + if(!client) { + log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]); + return; + } + + client.set_connection_info(object); + } + + private createChannelFromJson(json, ignoreOrder: boolean = false) { + let tree = this.connection.client.channelTree; + + let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); + tree.insertChannel(channel); + if(json["channel_order"] !== "0") { + let prev = tree.findChannel(json["channel_order"]); + if(!prev && json["channel_order"] != 0) { + if(!ignoreOrder) { + log.error(LogCategory.NETWORKING, tr("Invalid channel order id!")); return; } - tree.moveChannel(channel, prev, parent); //TODO test if channel exists! } - if(ignoreOrder) { - for(let ch of tree.channels) { - if(ch.properties.channel_order == channel.channelId) { - tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) - } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Invalid channel parent")); + return; + } + tree.moveChannel(channel, prev, parent); //TODO test if channel exists! + } + if(ignoreOrder) { + for(let ch of tree.channels) { + if(ch.properties.channel_order == channel.channelId) { + tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) + } + } + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "cpid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + } + + handleCommandChannelList(json) { + this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ + log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); + for(let index = 0; index < json.length; index++) + this.createChannelFromJson(json[index], true); + } + + + handleCommandChannelListFinished(json) { + this.connection.client.channelTree.show_channel_tree(); + } + + handleCommandChannelCreate(json) { + this.createChannelFromJson(json[0]); + } + + handleCommandChannelShow(json) { + this.createChannelFromJson(json[0]); //TODO may chat? + } + + handleCommandChannelDelete(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandChannelHide(json) { + let tree = this.connection.client.channelTree; + const conversations = this.connection.client.side_bar.channel_conversations(); + + log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); + for(let index = 0; index < json.length; index++) { + conversations.delete_conversation(parseInt(json[index]["cid"])); + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandClientEnterView(json) { + let tree = this.connection.client.channelTree; + + let client: ClientEntry; + let channel = undefined; + let old_channel = undefined; + let reason_id, reason_msg; + + let invokerid, invokername, invokeruid; + + for(const entry of json) { + /* attempt to update properties if given */ + channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; + old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; + reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; + reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; + + invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; + invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; + invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; + + client = tree.findClient(parseInt(entry["clid"])); + + if(!client) { + if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { + client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } else { + client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); + } + + client.properties.client_type = parseInt(entry["client_type"]); + client = tree.insertClient(client, channel); + } else { + tree.moveClient(client, channel); + } + + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_ENTER, { + channel_from: old_channel ? old_channel.log_data() : undefined, + channel_to: channel ? channel.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(invokeruid, invokerid, invokername), + message:reason_msg, + reason: parseInt(reason_id), + own_channel: channel == own_channel + }); + + if(reason_id == ViewReasonId.VREASON_USER_ACTION) { + if(own_channel == channel) + if(old_channel) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else + this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { + if(own_channel == channel) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { + + } else { + console.warn(tr("Unknown reasonid for %o"), reason_id); } } @@ -311,159 +467,113 @@ namespace connection { key: string, value: string }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "cpid") continue; + + for(let key in entry) { + if(key == "cfid") continue; + if(key == "ctid") continue; if(key === "invokerid") continue; if(key === "invokername") continue; if(key === "invokeruid") continue; if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); + updates.push({key: key, value: entry[key]}); } - channel.updateVariables(...updates); - } - handleCommandChannelList(json) { - this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ - log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length); - for(let index = 0; index < json.length; index++) - this.createChannelFromJson(json[index], true); - } + client.updateVariables(...updates); + /* if its a new client join, or a system reason (like we joined) */ + if(!old_channel || reason_id == 2) { + /* client new join */ + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + unique_id: client.properties.client_unique_identifier, + client_id: client.clientId(), + name: client.clientNickName() + }, { + create: false, + attach: true + }); + if(conversation) + client.flag_text_unread = conversation.is_unread(); + } - handleCommandChannelListFinished(json) { - this.connection.client.channelTree.show_channel_tree(); - } - - handleCommandChannelCreate(json) { - this.createChannelFromJson(json[0]); - } - - handleCommandChannelShow(json) { - this.createChannelFromJson(json[0]); //TODO may chat? - } - - handleCommandChannelDelete(json) { - let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); + if(client instanceof LocalClientEntry) { + client.initializeListener(); + this.connection_handler.update_voice_status(); + this.connection_handler.side_bar.info_frame().update_channel_talk(); + const conversations = this.connection.client.side_bar.channel_conversations(); + conversations.set_current_channel(client.currentChannel().channelId); } } + } - handleCommandChannelHide(json) { + handleCommandClientLeftView(json) { + let reason_id = -1; + + for(const entry of json) { + reason_id = entry["reasonid"] || reason_id; let tree = this.connection.client.channelTree; - const conversations = this.connection.client.side_bar.channel_conversations(); - - log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length); - for(let index = 0; index < json.length; index++) { - conversations.delete_conversation(parseInt(json[index]["cid"])); - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); + let client = tree.findClient(entry["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client left!")); + return 0; } - } - - handleCommandClientEnterView(json) { - let tree = this.connection.client.channelTree; - - let client: ClientEntry; - let channel = undefined; - let old_channel = undefined; - let reason_id, reason_msg; - - let invokerid, invokername, invokeruid; - - for(const entry of json) { - /* attempt to update properties if given */ - channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel; - old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel; - reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id; - reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg; - - invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid; - invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername; - invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid; - - client = tree.findClient(parseInt(entry["clid"])); - - if(!client) { - if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) { - client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } else { - client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]); - } - - client.properties.client_type = parseInt(entry["client_type"]); - client = tree.insertClient(client, channel); + if(client == this.connection.client.getClient()) { + if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); + } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); } else { - tree.moveClient(client, channel); + this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); } + this.connection_handler.side_bar.info_frame().update_channel_talk(); + return; + } - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, { - channel_from: old_channel ? old_channel.log_data() : undefined, - channel_to: channel ? channel.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(invokeruid, invokerid, invokername), - message:reason_msg, - reason: parseInt(reason_id), - own_channel: channel == own_channel - }); + if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + let channel_from = tree.findChannel(entry["cfid"]); + let channel_to = tree.findChannel(entry["ctid"]); + + const is_own_channel = channel_from == own_channel; + this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_LEAVE, { + channel_from: channel_from ? channel_from.log_data() : undefined, + channel_to: channel_to ? channel_to.log_data() : undefined, + client: client.log_data(), + invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), + message: entry["reasonmsg"], + reason: parseInt(entry["reasonid"]), + ban_time: parseInt(entry["bantime"]), + own_channel: is_own_channel + }); + + if(is_own_channel) { if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - if(own_channel == channel) - if(old_channel) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else - this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { + this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); + } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - if(own_channel == channel) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - } else if(reason_id == ViewReasonId.VREASON_SYSTEM) { - + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else if(reason_id == ViewReasonId.VREASON_BAN) { + this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); + } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { + this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); + } else if(reason_id == ViewReasonId.VREASON_MOVED) { + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); } else { - console.warn(tr("Unknown reasonid for %o"), reason_id); + log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); } } - let updates: { - key: string, - value: string - }[] = []; - - for(let key in entry) { - if(key == "cfid") continue; - if(key == "ctid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: entry[key]}); - } - - client.updateVariables(...updates); - - /* if its a new client join, or a system reason (like we joined) */ - if(!old_channel || reason_id == 2) { - /* client new join */ + if(!channel_to) { + /* client left the server */ const conversation_manager = this.connection_handler.side_bar.private_conversations(); const conversation = conversation_manager.find_conversation({ unique_id: client.properties.client_unique_identifier, @@ -471,690 +581,599 @@ namespace connection { name: client.clientNickName() }, { create: false, - attach: true + attach: false }); - if(conversation) - client.flag_text_unread = conversation.is_unread(); - } - - if(client instanceof LocalClientEntry) { - client.initializeListener(); - this.connection_handler.update_voice_status(); - this.connection_handler.side_bar.info_frame().update_channel_talk(); - const conversations = this.connection.client.side_bar.channel_conversations(); - conversations.set_current_channel(client.currentChannel().channelId); - } - } - } - - handleCommandClientLeftView(json) { - let reason_id = -1; - - for(const entry of json) { - reason_id = entry["reasonid"] || reason_id; - let tree = this.connection.client.channelTree; - let client = tree.findClient(entry["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client left!")); - return 0; - } - if(client == this.connection.client.getClient()) { - if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry); - } else { - this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry); - } - this.connection_handler.side_bar.info_frame().update_channel_talk(); - return; - } - - - if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection.client.getClient().currentChannel(); - let channel_from = tree.findChannel(entry["cfid"]); - let channel_to = tree.findChannel(entry["ctid"]); - - const is_own_channel = channel_from == own_channel; - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, { - channel_from: channel_from ? channel_from.log_data() : undefined, - channel_to: channel_to ? channel_to.log_data() : undefined, - client: client.log_data(), - invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]), - message: entry["reasonmsg"], - reason: parseInt(entry["reasonid"]), - ban_time: parseInt(entry["bantime"]), - own_channel: is_own_channel - }); - - if(is_own_channel) { - if(reason_id == ViewReasonId.VREASON_USER_ACTION) { - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) { - this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT); - } else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER); - } else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) { - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else if(reason_id == ViewReasonId.VREASON_BAN) { - this.connection_handler.sound.play(Sound.USER_LEFT_BANNED); - } else if(reason_id == ViewReasonId.VREASON_TIMEOUT) { - this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT); - } else if(reason_id == ViewReasonId.VREASON_MOVED) { - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); - } else { - log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id); - } - } - - if(!channel_to) { - /* client left the server */ - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - unique_id: client.properties.client_unique_identifier, - client_id: client.clientId(), - name: client.clientNickName() - }, { - create: false, - attach: false - }); - if(conversation) { - conversation.set_state(chat.PrivateConversationState.DISCONNECTED); - } + if(conversation) { + conversation.set_state(PrivateConversationState.DISCONNECTED); } } - - tree.deleteClient(client); - } - } - - handleNotifyClientMoved(json) { - json = json[0]; //Only one bulk - let tree = this.connection.client.channelTree; - let client = tree.findClient(json["clid"]); - let self = client instanceof LocalClientEntry; - - let channel_to = tree.findChannel(parseInt(json["ctid"])); - let channel_from = tree.findChannel(parseInt(json["cfid"])); - - if(!client) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); - return 0; } - if(!channel_to) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); - return 0; - } - - if(!self) { - if(!channel_from) { - log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); - channel_from = client.currentChannel(); - } else if(channel_from != client.currentChannel()) { - log.error(LogCategory.NETWORKING, - tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), - client.currentChannel().channelId, channel_from.channelId - ); - } - } else { - channel_from = client.currentChannel(); - } - - tree.moveClient(client, channel_to); - - if(self) { - this.connection_handler.update_voice_status(channel_to); - - for(const entry of client.channelTree.clientsByChannel(channel_from)) { - if(entry !== client && entry.get_audio_handle()) { - entry.get_audio_handle().abort_replay(); - entry.speaking = false; - } - } - - const side_bar = this.connection_handler.side_bar; - side_bar.info_frame().update_channel_talk(); - - const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); - if(conversation_to) - conversation_to.update_private_state(); - - if(channel_from) { - const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); - if(conversation_from) - conversation_from.update_private_state(); - } - - side_bar.channel_conversations().update_chat_box(); - } - - const own_channel = this.connection.client.getClient().currentChannel(); - this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, { - channel_from: channel_from ? { - channel_id: channel_from.channelId, - channel_name: channel_from.channelName() - } : undefined, - channel_from_own: channel_from == own_channel, - - channel_to: channel_to ? { - channel_id: channel_to.channelId, - channel_name: channel_to.channelName() - } : undefined, - channel_to_own: channel_to == own_channel, - - client: { - client_id: client.clientId(), - client_name: client.clientNickName(), - client_unique_id: client.properties.client_unique_identifier - }, - client_own: self, - - invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), - - message: json["reasonmsg"], - reason: parseInt(json["reasonid"]), - }); - if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { - if(self) - this.connection_handler.sound.play(Sound.USER_MOVED_SELF); - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); - } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - if(self) {} //If we do an action we wait for the error response - else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { - if(self) { - this.connection_handler.sound.play(Sound.CHANNEL_KICKED); - } else if(own_channel == channel_to) - this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); - else if(own_channel == channel_from) - this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else { - console.warn(tr("Unknown reason id %o"), json["reasonid"]); - } - } - - handleNotifyChannelMoved(json) { - json = json[0]; //Only one bulk - - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); - return 0; - } - - let prev = tree.findChannel(json["order"]); - if(!prev && json["order"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); - return 0; - } - - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); - return 0; - } - - tree.moveChannel(channel, prev, parent); - } - - handleNotifyChannelEdited(json) { - json = json[0]; //Only one bulk - - let tree = this.connection.client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); - return 0; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - - if(this.connection_handler.getClient().currentChannel() === channel) { - //TODO: Playback sound that your channel has been edited - this.connection_handler.update_voice_status(); - } - } - - handleNotifyTextMessage(json) { - json = json[0]; //Only one bulk - - let mode = json["targetmode"]; - if(mode == 1){ - //json["invokerid"], json["invokername"], json["invokeruid"] - const target_client_id = parseInt(json["target"]); - const target_own = target_client_id === this.connection.client.getClientId(); - - if(target_own && target_client_id === json["invokerid"]) { - log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json)); - return; - } - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, - unique_id: target_own ? json["invokeruid"] : undefined, - name: target_own ? json["invokername"] : undefined - }, { - create: target_own, - attach: target_own - }); - if(!conversation) { - log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); - return; - } - - conversation.append_message(json["msg"], { - type: target_own ? "partner" : "self", - name: json["invokername"], - unique_id: json["invokeruid"], - client_id: parseInt(json["invokerid"]) - }); - - if(target_own) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - if(client) /* the client itself might be invisible */ - client.flag_text_unread = conversation.is_unread(); - } else { - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - } - } else if(mode == 2) { - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const own_channel_id = this.connection.client.getClient().currentChannel().channelId; - const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; - const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); - - if(json["invokerid"] == this.connection.client.clientId) - this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - else if(channel_id == own_channel_id) { - this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - } - - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(channel_id); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], - - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] - }); - if(conversation.is_unread() && channel) - channel.flag_text_unread = true; - } else if(mode == 3) { - this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, { - message: json["msg"], - sender: { - client_unique_id: json["invokeruid"], - client_name: json["invokername"], - client_id: parseInt(json["invokerid"]) - } - }); - - const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); - const conversations = this.connection_handler.side_bar.channel_conversations(); - const conversation = conversations.conversation(0); - conversation.register_new_message({ - sender_database_id: invoker ? invoker.properties.client_database_id : 0, - sender_name: json["invokername"], - sender_unique_id: json["invokeruid"], - - timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), - message: json["msg"] - }); - this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); - } - } - - notifyClientChatComposing(json) { - json = json[0]; - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) - return; - - conversation.trigger_typing(); - } - - handleNotifyClientChatClosed(json) { - json = json[0]; //Only one bulk - - //Chat partner has closed the conversation - - //clid: "6" - //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" - - const conversation_manager = this.connection_handler.side_bar.private_conversations(); - const conversation = conversation_manager.find_conversation({ - client_id: parseInt(json["clid"]), - unique_id: json["cluid"], - name: undefined - }, { - create: false, - attach: false - }); - if(!conversation) { - log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); - return; - } - conversation.set_state(chat.PrivateConversationState.CLOSED); - } - - handleNotifyClientUpdated(json) { - json = json[0]; //Only one bulk - - let client = this.connection.client.channelTree.findClient(json["clid"]); - if(!client) { - log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); - return; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key == "clid") continue; - updates.push({key: key, value: json[key]}); - } - client.updateVariables(...updates); - } - - handleNotifyServerEdited(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(false, ...updates); - } - - handleNotifyServerUpdated(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection.client.channelTree.server.updateVariables(true, ...updates); - } - - handleNotifyMusicPlayerInfo(json) { - json = json[0]; - - let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); - if(!bot || !(bot instanceof MusicClientEntry)) { - log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); - return; - } - - bot.handlePlayerInfo(json); - } - - handleNotifyClientPoke(json) { - json = json[0]; - Modals.spawnPoke(this.connection_handler, { - id: parseInt(json["invokerid"]), - name: json["invokername"], - unique_id: json["invokeruid"] - }, json["msg"]); - - this.connection_handler.sound.play(Sound.USER_POKED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientAdd(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) - this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientRemove(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); - } else { - } - } - - //TODO server chat message - handleNotifyClientChannelGroupChanged(json) { - json = json[0]; - - const self = this.connection.client.getClient(); - if(json["clid"] == self.clientId()) { - this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); - } - } - - handleNotifyChannelSubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = true; - } - } - - handleNotifyChannelUnsubscribed(json) { - for(const entry of json) { - const channel = this.connection.client.channelTree.findChannel(entry["cid"]); - if(!channel) { - console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); - continue; - } - - channel.flag_subscribed = false; - for(const client of channel.clients(false)) - this.connection.client.channelTree.deleteClient(client); - } - } - - handleNotifyConversationHistory(json: any[]) { - const conversations = this.connection.client.side_bar.channel_conversations(); - const conversation = conversations.conversation(parseInt(json[0]["cid"])); - if(!conversation) { - log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); - return; - } - - for(const entry of json) { - conversation.register_new_message({ - message: entry["msg"], - sender_unique_id: entry["sender_unique_id"], - sender_name: entry["sender_name"], - timestamp: parseInt(entry["timestamp"]), - sender_database_id: parseInt(entry["sender_database_id"]) - }, false); - } - - /* now update the boxes */ - /* No update needed because the command which triggers this notify should update the chat box on success - conversation.fix_scroll(true); - conversation.handle.update_chat_box(); - */ - } - - handleNotifyConversationMessageDelete(json: any[]) { - let conversation: Conversation; - const conversations = this.connection.client.side_bar.channel_conversations(); - for(const entry of json) { - if(typeof(entry["cid"]) !== "undefined") - conversation = conversations.conversation(parseInt(entry["cid"]), false); - if(!conversation) - continue; - - conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); - } - } - - handleNotifyMusicStatusUpdate(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - client.events.fire("music_status_update", { - player_replay_index: parseInt(json["player_replay_index"]), - player_buffered_index: parseInt(json["player_buffered_index"]) - }); - } - - handleMusicPlayerSongChange(json: any[]) { - json = json[0]; - - const bot_id = parseInt(json["bot_id"]); - const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); - return; - } - - const song_id = parseInt(json["song_id"]); - let song: SongInfo; - if(song_id) { - song = new SongInfo(); - JSON.map_to(song, json); - } - - client.events.fire("music_song_change", { - song: song - }); - } - - handleNotifyPlaylistSongAdd(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - client.events.fire("playlist_song_add", { - song: { - song_id: parseInt(json["song_id"]), - song_invoker: json["song_invoker"], - song_previous_song_id: parseInt(json["song_previous_song_id"]), - song_url: json["song_url"], - song_url_loader: json["song_url_loader"], - - song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", - song_metadata: json["song_metadata"] - } - }); - } - - handleNotifyPlaylistSongRemove(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_remove", { song_id: song_id }); - } - - handleNotifyPlaylistSongReorder(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - const previous_song_id = parseInt(json["song_previous_song_id"]); - client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); - } - - handleNotifyPlaylistSongLoaded(json: any[]) { - json = json[0]; - - const playlist_id = parseInt(json["playlist_id"]); - const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); - if(!client) { - log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); - return; - } - - const song_id = parseInt(json["song_id"]); - client.events.fire("playlist_song_loaded", { - song_id: song_id, - success: json["success"] == 1, - error_msg: json["load_error_msg"], - metadata: json["song_metadata"] - }); + tree.deleteClient(client); } } + + handleNotifyClientMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + let self = client instanceof LocalClientEntry; + + let channel_to = tree.findChannel(parseInt(json["ctid"])); + let channel_from = tree.findChannel(parseInt(json["cfid"])); + + if(!client) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!")); + return 0; + } + + if(!channel_to) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!")); + return 0; + } + + if(!self) { + if(!channel_from) { + log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!")); + channel_from = client.currentChannel(); + } else if(channel_from != client.currentChannel()) { + log.error(LogCategory.NETWORKING, + tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."), + client.currentChannel().channelId, channel_from.channelId + ); + } + } else { + channel_from = client.currentChannel(); + } + + tree.moveClient(client, channel_to); + + if(self) { + this.connection_handler.update_voice_status(channel_to); + + for(const entry of client.channelTree.clientsByChannel(channel_from)) { + if(entry !== client && entry.get_audio_handle()) { + entry.get_audio_handle().abort_replay(); + entry.speaking = false; + } + } + + const side_bar = this.connection_handler.side_bar; + side_bar.info_frame().update_channel_talk(); + + const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false); + if(conversation_to) + conversation_to.update_private_state(); + + if(channel_from) { + const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false); + if(conversation_from) + conversation_from.update_private_state(); + } + + side_bar.channel_conversations().update_chat_box(); + } + + const own_channel = this.connection.client.getClient().currentChannel(); + this.connection_handler.log.log(server_log.Type.CLIENT_VIEW_MOVE, { + channel_from: channel_from ? { + channel_id: channel_from.channelId, + channel_name: channel_from.channelName() + } : undefined, + channel_from_own: channel_from == own_channel, + + channel_to: channel_to ? { + channel_id: channel_to.channelId, + channel_name: channel_to.channelName() + } : undefined, + channel_to_own: channel_to == own_channel, + + client: { + client_id: client.clientId(), + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier + }, + client_own: self, + + invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]), + + message: json["reasonmsg"], + reason: parseInt(json["reasonid"]), + }); + if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { + if(self) + this.connection_handler.sound.play(Sound.USER_MOVED_SELF); + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_MOVED); + } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + if(self) {} //If we do an action we wait for the error response + else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + if(self) { + this.connection_handler.sound.play(Sound.CHANNEL_KICKED); + } else if(own_channel == channel_to) + this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED); + else if(own_channel == channel_from) + this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else { + console.warn(tr("Unknown reason id %o"), json["reasonid"]); + } + } + + handleNotifyChannelMoved(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!")); + return 0; + } + + let prev = tree.findChannel(json["order"]); + if(!prev && json["order"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!")); + return 0; + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!")); + return 0; + } + + tree.moveChannel(channel, prev, parent); + } + + handleNotifyChannelEdited(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!")); + return 0; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + + if(this.connection_handler.getClient().currentChannel() === channel) { + //TODO: Playback sound that your channel has been edited + this.connection_handler.update_voice_status(); + } + } + + handleNotifyTextMessage(json) { + json = json[0]; //Only one bulk + + let mode = json["targetmode"]; + if(mode == 1){ + //json["invokerid"], json["invokername"], json["invokeruid"] + const target_client_id = parseInt(json["target"]); + const target_own = target_client_id === this.connection.client.getClientId(); + + if(target_own && target_client_id === json["invokerid"]) { + log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o"), json); + return; + } + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: target_own ? parseInt(json["invokerid"]) : target_client_id, + unique_id: target_own ? json["invokeruid"] : undefined, + name: target_own ? json["invokername"] : undefined + }, { + create: target_own, + attach: target_own + }); + if(!conversation) { + log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message")); + return; + } + + conversation.append_message(json["msg"], { + type: target_own ? "partner" : "self", + name: json["invokername"], + unique_id: json["invokeruid"], + client_id: parseInt(json["invokerid"]) + }); + + if(target_own) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + if(client) /* the client itself might be invisible */ + client.flag_text_unread = conversation.is_unread(); + } else { + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + } + } else if(mode == 2) { + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const own_channel_id = this.connection.client.getClient().currentChannel().channelId; + const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id; + const channel = this.connection_handler.channelTree.findChannel(channel_id) || this.connection_handler.getClient().currentChannel(); + + if(json["invokerid"] == this.connection.client.clientId) + this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + else if(channel_id == own_channel_id) { + this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + } + + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(channel_id); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + if(conversation.is_unread() && channel) + channel.flag_text_unread = true; + } else if(mode == 3) { + this.connection_handler.log.log(server_log.Type.GLOBAL_MESSAGE, { + message: json["msg"], + sender: { + client_unique_id: json["invokeruid"], + client_name: json["invokername"], + client_id: parseInt(json["invokerid"]) + } + }); + + const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"])); + const conversations = this.connection_handler.side_bar.channel_conversations(); + const conversation = conversations.conversation(0); + conversation.register_new_message({ + sender_database_id: invoker ? invoker.properties.client_database_id : 0, + sender_name: json["invokername"], + sender_unique_id: json["invokeruid"], + + timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]), + message: json["msg"] + }); + this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread(); + } + } + + notifyClientChatComposing(json) { + json = json[0]; + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if(!conversation) + return; + + conversation.trigger_typing(); + } + + handleNotifyClientChatClosed(json) { + json = json[0]; //Only one bulk + + //Chat partner has closed the conversation + + //clid: "6" + //cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY=" + + const conversation_manager = this.connection_handler.side_bar.private_conversations(); + const conversation = conversation_manager.find_conversation({ + client_id: parseInt(json["clid"]), + unique_id: json["cluid"], + name: undefined + }, { + create: false, + attach: false + }); + if(!conversation) { + log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open.")); + return; + } + conversation.set_state(PrivateConversationState.CLOSED); + } + + handleNotifyClientUpdated(json) { + json = json[0]; //Only one bulk + + let client = this.connection.client.channelTree.findClient(json["clid"]); + if(!client) { + log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client")); + return; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key == "clid") continue; + updates.push({key: key, value: json[key]}); + } + client.updateVariables(...updates); + } + + handleNotifyServerEdited(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + } + + handleNotifyServerUpdated(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(true, ...updates); + } + + handleNotifyMusicPlayerInfo(json) { + json = json[0]; + + let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); + if(!bot || !(bot instanceof MusicClientEntry)) { + log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); + return; + } + + bot.handlePlayerInfo(json); + } + + handleNotifyClientPoke(json) { + json = json[0]; + spawnPoke(this.connection_handler, { + id: parseInt(json["invokerid"]), + name: json["invokername"], + unique_id: json["invokeruid"] + }, json["msg"]); + + this.connection_handler.sound.play(Sound.USER_POKED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientAdd(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) + this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientRemove(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF); + } else { + } + } + + //TODO server chat message + handleNotifyClientChannelGroupChanged(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); + } + } + + handleNotifyChannelSubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = true; + } + } + + handleNotifyChannelUnsubscribed(json) { + for(const entry of json) { + const channel = this.connection.client.channelTree.findChannel(entry["cid"]); + if(!channel) { + console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']); + continue; + } + + channel.flag_subscribed = false; + for(const client of channel.clients(false)) + this.connection.client.channelTree.deleteClient(client); + } + } + + handleNotifyConversationHistory(json: any[]) { + const conversations = this.connection.client.side_bar.channel_conversations(); + const conversation = conversations.conversation(parseInt(json[0]["cid"])); + if(!conversation) { + log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]); + return; + } + + for(const entry of json) { + conversation.register_new_message({ + message: entry["msg"], + sender_unique_id: entry["sender_unique_id"], + sender_name: entry["sender_name"], + timestamp: parseInt(entry["timestamp"]), + sender_database_id: parseInt(entry["sender_database_id"]) + }, false); + } + + /* now update the boxes */ + /* No update needed because the command which triggers this notify should update the chat box on success + conversation.fix_scroll(true); + conversation.handle.update_chat_box(); + */ + } + + handleNotifyConversationMessageDelete(json: any[]) { + let conversation: Conversation; + const conversations = this.connection.client.side_bar.channel_conversations(); + for(const entry of json) { + if(typeof(entry["cid"]) !== "undefined") + conversation = conversations.conversation(parseInt(entry["cid"]), false); + if(!conversation) + continue; + + conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"])); + } + } + + handleNotifyMusicStatusUpdate(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + client.events.fire("music_status_update", { + player_replay_index: parseInt(json["player_replay_index"]), + player_buffered_index: parseInt(json["player_buffered_index"]) + }); + } + + handleMusicPlayerSongChange(json: any[]) { + json = json[0]; + + const bot_id = parseInt(json["bot_id"]); + const client = this.connection.client.channelTree.find_client_by_dbid(bot_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received music bot status update for unknown bot (%d)"), bot_id); + return; + } + + const song_id = parseInt(json["song_id"]); + let song: SongInfo; + if(song_id) { + song = new SongInfo(); + JSON.map_to(song, json); + } + + client.events.fire("music_song_change", { + song: song + }); + } + + handleNotifyPlaylistSongAdd(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song add event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + client.events.fire("playlist_song_add", { + song: { + song_id: parseInt(json["song_id"]), + song_invoker: json["song_invoker"], + song_previous_song_id: parseInt(json["song_previous_song_id"]), + song_url: json["song_url"], + song_url_loader: json["song_url_loader"], + + song_loaded: json["song_loaded"] == true || json["song_loaded"] == "1", + song_metadata: json["song_metadata"] + } + }); + } + + handleNotifyPlaylistSongRemove(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song remove event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_remove", { song_id: song_id }); + } + + handleNotifyPlaylistSongReorder(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song reorder event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + const previous_song_id = parseInt(json["song_previous_song_id"]); + client.events.fire("playlist_song_reorder", { song_id: song_id, previous_song_id: previous_song_id }); + } + + handleNotifyPlaylistSongLoaded(json: any[]) { + json = json[0]; + + const playlist_id = parseInt(json["playlist_id"]); + const client = this.connection.client.channelTree.clients.find(e => e instanceof MusicClientEntry && e.properties.client_playlist_id === playlist_id); + if(!client) { + log.warn(LogCategory.CLIENT, tr("Received playlist song loaded event, but we've no music bot for the playlist (%d)"), playlist_id); + return; + } + + const song_id = parseInt(json["song_id"]); + client.events.fire("playlist_song_loaded", { + song_id: song_id, + success: json["success"] == 1, + error_msg: json["load_error_msg"], + metadata: json["song_metadata"] + }); + } } \ No newline at end of file diff --git a/shared/js/connection/CommandHelper.ts b/shared/js/connection/CommandHelper.ts index 9eff210b..6685c841 100644 --- a/shared/js/connection/CommandHelper.ts +++ b/shared/js/connection/CommandHelper.ts @@ -1,448 +1,461 @@ -namespace connection { - export class CommandHelper extends AbstractCommandHandler { - private _who_am_i: any; - private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; - private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; +import {ServerCommand, SingleCommandHandler} from "tc-shared/connection/ConnectionBase"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import { + ClientNameInfo, + CommandResult, + ErrorID, Playlist, PlaylistInfo, PlaylistSong, + QueryList, + QueryListEntry, ServerGroupClient +} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ClientEntry} from "tc-shared/ui/client"; +import {ChatType} from "tc-shared/ui/frames/chat"; +import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler"; - constructor(connection) { - super(connection); +export class CommandHelper extends AbstractCommandHandler { + private _who_am_i: any; + private _awaiters_unique_ids: {[unique_id: string]:((resolved: ClientNameInfo) => any)[]} = {}; + private _awaiters_unique_dbid: {[database_id: number]:((resolved: ClientNameInfo) => any)[]} = {}; - this.volatile_handler_boss = false; - this.ignore_consumed = true; + constructor(connection) { + super(connection); + + this.volatile_handler_boss = false; + this.ignore_consumed = true; + } + + initialize() { + this.connection.command_handler_boss().register_handler(this); + } + + destroy() { + if(this.connection) { + const hboss = this.connection.command_handler_boss(); + hboss && hboss.unregister_handler(this); + } + this._awaiters_unique_ids = undefined; + } + + handle_command(command: ServerCommand): boolean { + if(command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + if(command.command == "notifyclientgetnamefromdbid") + this.handle_notifyclientgetnamefromdbid(command.arguments); + else + return false; + return true; + } + + joinChannel(channel: ChannelEntry, password?: string) : Promise { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); + } + + sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { + if(type == ChatType.SERVER) + return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); + else if(type == ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); + else if(type == ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); + } + + updateClient(key: string, value: string) : Promise { + let data = {}; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } + + async info_from_uid(..._unique_ids: string[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_ids = new Set(_unique_ids); + if(!unique_ids.size) return []; + + const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; + + + for(const unique_id of unique_ids) { + request.push({'cluid': unique_id}); + (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) + .push(unique_id_resolvers[unique_id] = info => response.push(info)); } - initialize() { - this.connection.command_handler_boss().register_handler(this); - } - - destroy() { - if(this.connection) { - const hboss = this.connection.command_handler_boss(); - hboss && hboss.unregister_handler(this); + try { + await this.connection.send_command("clientgetnamefromuid", request); + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } else { + throw error; } - this._awaiters_unique_ids = undefined; + } finally { + /* cleanup */ + for(const unique_id of Object.keys(unique_id_resolvers)) + (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); } - handle_command(command: connection.ServerCommand): boolean { - if(command.command == "notifyclientnamefromuid") - this.handle_notifyclientnamefromuid(command.arguments); - if(command.command == "notifyclientgetnamefromdbid") - this.handle_notifyclientgetnamefromdbid(command.arguments); - else - return false; - return true; + return response; + } + + private handle_notifyclientgetnamefromdbid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_dbid[info.client_database_id] || []; + delete this._awaiters_unique_dbid[info.client_database_id]; + + for(const fn of functions) + fn(info); + } + } + + async info_from_cldbid(..._cldbid: number[]) : Promise { + const response: ClientNameInfo[] = []; + const request = []; + const unique_cldbid = new Set(_cldbid); + if(!unique_cldbid.size) return []; + + const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; + + + for(const cldbid of unique_cldbid) { + request.push({'cldbid': cldbid}); + (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) + .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); } - joinChannel(channel: ChannelEntry, password?: string) : Promise { - return this.connection.send_command("clientmove", { - "clid": this.connection.client.getClientId(), - "cid": channel.getChannelId(), - "cpw": password || "" - }); + try { + await this.connection.send_command("clientgetnamefromdbid", request); + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + /* nothing */ + } else { + throw error; + } + } finally { + /* cleanup */ + for(const cldbid of Object.keys(unique_cldbid_resolvers)) + (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); } - sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { - if(type == ChatType.SERVER) - return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); - else if(type == ChatType.CHANNEL) - return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); - else if(type == ChatType.CLIENT) - return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); - } + return response; + } + + private handle_notifyclientnamefromuid(json: any[]) { + for(const entry of json) { + const info: ClientNameInfo = { + client_unique_id: entry["cluid"], + client_nickname: entry["clname"], + client_database_id: parseInt(entry["cldbid"]) + }; + + const functions = this._awaiters_unique_ids[entry["cluid"]] || []; + delete this._awaiters_unique_ids[entry["cluid"]]; + + for(const fn of functions) + fn(info); + } + } + + request_query_list(server_id: number = undefined) : Promise { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + + const result = {} as QueryList; + + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + + for(const entry of json) { + const rentry = {} as QueryListEntry; + rentry.bounded_server = parseInt(entry["client_bound_server"]); + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + + result.queries.push(rentry); + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); - updateClient(key: string, value: string) : Promise { let data = {}; - data[key] = value; - return this.connection.send_command("clientupdate", data); - } + if(server_id !== undefined) + data["server_id"] = server_id; - async info_from_uid(..._unique_ids: string[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_ids = new Set(_unique_ids); - if(!unique_ids.size) return []; + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); - const unique_id_resolvers: {[unique_id: string]: (resolved: ClientNameInfo) => any} = {}; - - - for(const unique_id of unique_ids) { - request.push({'cluid': unique_id}); - (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) - .push(unique_id_resolvers[unique_id] = info => response.push(info)); - } - - try { - await this.connection.send_command("clientgetnamefromuid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } } - } finally { - /* cleanup */ - for(const unique_id of Object.keys(unique_id_resolvers)) - (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); - } + reject(error); + }); + }); + } - return response; - } + request_playlist_list() : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result: Playlist[] = []; - private handle_notifyclientgetnamefromdbid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; + for(const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], - const functions = this._awaiters_unique_dbid[info.client_database_id] || []; - delete this._awaiters_unique_dbid[info.client_database_id]; + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); + } + } - for(const fn of functions) - fn(info); - } - } - - async info_from_cldbid(..._cldbid: number[]) : Promise { - const response: ClientNameInfo[] = []; - const request = []; - const unique_cldbid = new Set(_cldbid); - if(!unique_cldbid.size) return []; - - const unique_cldbid_resolvers: {[dbid: number]: (resolved: ClientNameInfo) => any} = {}; - - - for(const cldbid of unique_cldbid) { - request.push({'cldbid': cldbid}); - (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) - .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); - } - - try { - await this.connection.send_command("clientgetnamefromdbid", request); - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { - /* nothing */ - } else { - throw error; + resolve(result); + return true; } - } finally { - /* cleanup */ - for(const cldbid of Object.keys(unique_cldbid_resolvers)) - (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); - } + }; + this.handler_boss.register_single_handler(single_handler); - return response; - } + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); - private handle_notifyclientnamefromuid(json: any[]) { - for(const entry of json) { - const info: ClientNameInfo = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - - const functions = this._awaiters_unique_ids[entry["cluid"]] || []; - delete this._awaiters_unique_ids[entry["cluid"]]; - - for(const fn of functions) - fn(info); - } - } - - request_query_list(server_id: number = undefined) : Promise { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyquerylist", - function: command => { - const json = command.arguments; - - const result = {} as QueryList; - - result.flag_all = json[0]["flag_all"]; - result.flag_own = json[0]["flag_own"]; - result.queries = []; - - for(const entry of json) { - const rentry = {} as QueryListEntry; - rentry.bounded_server = parseInt(entry["client_bound_server"]); - rentry.username = entry["client_login_name"]; - rentry.unique_id = entry["client_unique_identifier"]; - - result.queries.push(rentry); - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - let data = {}; - if(server_id !== undefined) - data["server_id"] = server_id; - - this.connection.send_command("querylist", data).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve(undefined); - return; - } - } - reject(error); - }); - }); - } - - request_playlist_list() : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistlist", - function: command => { - const json = command.arguments; - const result: Playlist[] = []; - - for(const entry of json) { - try { - result.push({ - playlist_id: parseInt(entry["playlist_id"]), - playlist_bot_id: parseInt(entry["playlist_bot_id"]), - playlist_title: entry["playlist_title"], - playlist_type: parseInt(entry["playlist_type"]), - playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), - playlist_owner_name: entry["playlist_owner_name"], - - needed_power_modify: parseInt(entry["needed_power_modify"]), - needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), - needed_power_delete: parseInt(entry["needed_power_delete"]), - needed_power_song_add: parseInt(entry["needed_power_song_add"]), - needed_power_song_move: parseInt(entry["needed_power_song_move"]), - needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistlist").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_songs(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistsonglist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); - return false; - } - - const result: PlaylistSong[] = []; - - for(const entry of json) { - try { - result.push({ - song_id: parseInt(entry["song_id"]), - song_invoker: entry["song_invoker"], - song_previous_song_id: parseInt(entry["song_previous_song_id"]), - song_url: entry["song_url"], - song_url_loader: entry["song_url_loader"], - - song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", - song_metadata: entry["song_metadata"] - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); - } - } - - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_client_list(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistclientlist", - function: command => { - const json = command.arguments; - - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); - return false; - } - - const result: number[] = []; - - for(const entry of json) - result.push(parseInt(entry["cldbid"])); - - resolve(result.filter(e => !isNaN(e))); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - - this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { resolve([]); return; } - reject(error); - }) - }); - } + } + reject(error); + }) + }); + } - async request_clients_by_server_group(group_id: number) : Promise { - //servergroupclientlist sgid=2 - //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyservergroupclientlist", - function: command => { - if (command.arguments[0]["sgid"] != group_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); - return false; - } + request_playlist_songs(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; - try { - const result: ServerGroupClient[] = []; - for(const entry of command.arguments) - result.push({ - client_database_id: parseInt(entry["cldbid"]), - client_nickname: entry["client_nickname"], - client_unique_identifier: entry["client_unique_identifier"] - }); - resolve(result); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); - reject("failed to parse info"); - } - - return true; + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); + return false; } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } - - request_playlist_info(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - command: "notifyplaylistinfo", - function: command => { - const json = command.arguments[0]; - if (json["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); - return; - } + const result: PlaylistSong[] = []; + for(const entry of json) { try { - //resolve - resolve({ - playlist_id: parseInt(json["playlist_id"]), - playlist_title: json["playlist_title"], - playlist_description: json["playlist_description"], - playlist_type: parseInt(json["playlist_type"]), + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], - playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), - playlist_owner_name: json["playlist_owner_name"], - - playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", - playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", - playlist_replay_mode: parseInt(json["playlist_replay_mode"]), - playlist_current_song_id: parseInt(json["playlist_current_song_id"]), - - playlist_max_songs: parseInt(json["playlist_max_songs"]) + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] }); - } catch (error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); - reject("failed to parse info"); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); } - - return true; } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }) - }); - } + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); - /** - * @deprecated - * Its just a workaround for the query management. - * There is no garante that the whoami trick will work forever - */ - current_virtual_server_id() : Promise { - if(this._who_am_i) - return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); - - return new Promise((resolve, reject) => { - const single_handler: SingleCommandHandler = { - function: command => { - if(command.command != "" && command.command.indexOf("=") == -1) - return false; - - this._who_am_i = command.arguments[0]; - resolve(parseInt(this._who_am_i["virtualserver_id"])); - return true; + this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; } - }; - this.handler_boss.register_single_handler(single_handler); + } + reject(error); + }) + }); + } - this.connection.send_command("whoami").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }); + request_playlist_client_list(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistclientlist", + function: command => { + const json = command.arguments; + + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); + return false; + } + + const result: number[] = []; + + for(const entry of json) + result.push(parseInt(entry["cldbid"])); + + resolve(result.filter(e => !isNaN(e))); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistclientlist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + reject(error); + }) + }); + } + + async request_clients_by_server_group(group_id: number) : Promise { + //servergroupclientlist sgid=2 + //notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyservergroupclientlist", + function: command => { + if (command.arguments[0]["sgid"] != group_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); + return false; + } + + try { + const result: ServerGroupClient[] = []; + for(const entry of command.arguments) + result.push({ + client_database_id: parseInt(entry["cldbid"]), + client_nickname: entry["client_nickname"], + client_unique_identifier: entry["client_unique_identifier"] + }); + resolve(result); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("servergroupclientlist", {sgid: group_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + request_playlist_info(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + return; + } + + try { + //resolve + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + + playlist_max_songs: parseInt(json["playlist_max_songs"]) + }); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() : Promise { + if(this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + function: command => { + if(command.command != "" && command.command.indexOf("=") == -1) + return false; + + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); }); - } + }); } } \ No newline at end of file diff --git a/shared/js/connection/ConnectionBase.ts b/shared/js/connection/ConnectionBase.ts index cf975fdb..d2a87040 100644 --- a/shared/js/connection/ConnectionBase.ts +++ b/shared/js/connection/ConnectionBase.ts @@ -1,216 +1,129 @@ -namespace connection { - export interface CommandOptions { - flagset?: string[]; /* default: [] */ - process_result?: boolean; /* default: true */ +import {CommandHelper} from "tc-shared/connection/CommandHelper"; +import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ServerAddress} from "tc-shared/ui/server"; +import {RecorderProfile} from "tc-shared/voice/RecorderProfile"; +import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler"; +import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler"; - timeout?: number /* default: 1000 */; +export interface CommandOptions { + flagset?: string[]; /* default: [] */ + process_result?: boolean; /* default: true */ + + timeout?: number /* default: 1000 */; +} +export const CommandOptionDefaults: CommandOptions = { + flagset: [], + process_result: true, + timeout: 1000 +}; + +export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; +export abstract class AbstractServerConnection { + readonly client: ConnectionHandler; + readonly command_helper: CommandHelper; + + protected constructor(client: ConnectionHandler) { + this.client = client; + + this.command_helper = new CommandHelper(this); } - export const CommandOptionDefaults: CommandOptions = { - flagset: [], - process_result: true, - timeout: 1000 + + /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ + abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; + + abstract connected() : boolean; + abstract disconnect(reason?: string) : Promise; + + abstract support_voice() : boolean; + abstract voice_connection() : voice.AbstractVoiceConnection | undefined; + + abstract command_handler_boss() : AbstractCommandHandlerBoss; + abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + + abstract get onconnectionstatechanged() : ConnectionStateListener; + abstract set onconnectionstatechanged(listener: ConnectionStateListener); + + abstract remote_address() : ServerAddress; /* only valid when connected */ + abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ + + abstract ping() : { + native: number, + javascript?: number }; +} - export type ConnectionStateListener = (old_state: ConnectionState, new_state: ConnectionState) => any; - export abstract class AbstractServerConnection { - readonly client: ConnectionHandler; - readonly command_helper: CommandHelper; +export namespace voice { + export enum PlayerState { + PREBUFFERING, + PLAYING, + BUFFERING, + STOPPING, + STOPPED + } - protected constructor(client: ConnectionHandler) { - this.client = client; + export type LatencySettings = { + min_buffer: number; /* milliseconds */ + max_buffer: number; /* milliseconds */ + } - this.command_helper = new CommandHelper(this); + export interface VoiceClient { + client_id: number; + + callback_playback: () => any; + callback_stopped: () => any; + + callback_state_changed: (new_state: PlayerState) => any; + + get_state() : PlayerState; + + get_volume() : number; + set_volume(volume: number) : void; + + abort_replay(); + + support_latency_settings() : boolean; + + reset_latency_settings(); + latency_settings(settings?: LatencySettings) : LatencySettings; + + support_flush() : boolean; + flush(); + } + + export abstract class AbstractVoiceConnection { + readonly connection: AbstractServerConnection; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ - abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; - abstract connected() : boolean; - abstract disconnect(reason?: string) : Promise; + abstract encoding_supported(codec: number) : boolean; + abstract decoding_supported(codec: number) : boolean; - abstract support_voice() : boolean; - abstract voice_connection() : voice.AbstractVoiceConnection | undefined; + abstract register_client(client_id: number) : VoiceClient; + abstract available_clients() : VoiceClient[]; + abstract unregister_client(client: VoiceClient) : Promise; - abstract command_handler_boss() : AbstractCommandHandlerBoss; - abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + abstract voice_recorder() : RecorderProfile; + abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; - abstract get onconnectionstatechanged() : ConnectionStateListener; - abstract set onconnectionstatechanged(listener: ConnectionStateListener); - - abstract remote_address() : ServerAddress; /* only valid when connected */ - abstract handshake_handler() : HandshakeHandler; /* only valid when connected */ - - abstract ping() : { - native: number, - javascript?: number - }; + abstract get_encoder_codec() : number; + abstract set_encoder_codec(codec: number); } +} - export namespace voice { - export enum PlayerState { - PREBUFFERING, - PLAYING, - BUFFERING, - STOPPING, - STOPPED - } +export class ServerCommand { + command: string; + arguments: any[]; +} - export type LatencySettings = { - min_buffer: number; /* milliseconds */ - max_buffer: number; /* milliseconds */ - } +export interface SingleCommandHandler { + name?: string; + command?: string; + timeout?: number; - export interface VoiceClient { - client_id: number; - - callback_playback: () => any; - callback_stopped: () => any; - - callback_state_changed: (new_state: PlayerState) => any; - - get_state() : PlayerState; - - get_volume() : number; - set_volume(volume: number) : void; - - abort_replay(); - - support_latency_settings() : boolean; - - reset_latency_settings(); - latency_settings(settings?: LatencySettings) : LatencySettings; - - support_flush() : boolean; - flush(); - } - - export abstract class AbstractVoiceConnection { - readonly connection: AbstractServerConnection; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; - } - - abstract connected() : boolean; - abstract encoding_supported(codec: number) : boolean; - abstract decoding_supported(codec: number) : boolean; - - abstract register_client(client_id: number) : VoiceClient; - abstract available_clients() : VoiceClient[]; - abstract unregister_client(client: VoiceClient) : Promise; - - abstract voice_recorder() : RecorderProfile; - abstract acquire_voice_recorder(recorder: RecorderProfile | undefined) : Promise; - - abstract get_encoder_codec() : number; - abstract set_encoder_codec(codec: number); - } - } - - export class ServerCommand { - command: string; - arguments: any[]; - } - - export abstract class AbstractCommandHandler { - readonly connection: AbstractServerConnection; - - handler_boss: AbstractCommandHandlerBoss | undefined; - volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ - - ignore_consumed: boolean = false; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; - } - - /** - * @return If the command should be consumed - */ - abstract handle_command(command: ServerCommand) : boolean; - } - - export interface SingleCommandHandler { - name?: string; - command?: string; - timeout?: number; - - /* if the return is true then the command handler will be removed */ - function: (command: ServerCommand) => boolean; - } - - export abstract class AbstractCommandHandlerBoss { - readonly connection: AbstractServerConnection; - protected command_handlers: AbstractCommandHandler[] = []; - /* TODO: Timeout */ - protected single_command_handler: SingleCommandHandler[] = []; - - protected constructor(connection: AbstractServerConnection) { - this.connection = connection; - } - - destroy() { - this.command_handlers = undefined; - this.single_command_handler = undefined; - } - - register_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss) - throw "handler already registered"; - - this.command_handlers.remove(handler); /* just to be sure */ - this.command_handlers.push(handler); - handler.handler_boss = this; - } - - unregister_handler(handler: AbstractCommandHandler) { - if(!handler.volatile_handler_boss && handler.handler_boss !== this) { - console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); - return; - } - - this.command_handlers.remove(handler); - handler.handler_boss = undefined; - } - - - register_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.push(handler); - } - - remove_single_handler(handler: SingleCommandHandler) { - this.single_command_handler.remove(handler); - } - - handlers() : AbstractCommandHandler[] { - return this.command_handlers; - } - - invoke_handle(command: ServerCommand) : boolean { - let flag_consumed = false; - - for(const handler of this.command_handlers) { - try { - if(!flag_consumed || handler.ignore_consumed) - flag_consumed = flag_consumed || handler.handle_command(command); - } catch(error) { - console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); - } - } - - for(const handler of [...this.single_command_handler]) { - if(handler.command && handler.command != command.command) - continue; - - try { - if(handler.function(command)) - this.single_command_handler.remove(handler); - } catch(error) { - console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); - } - } - - return flag_consumed; - } - } + /* if the return is true then the command handler will be removed */ + function: (command: ServerCommand) => boolean; } \ No newline at end of file diff --git a/shared/js/connection/HandshakeHandler.ts b/shared/js/connection/HandshakeHandler.ts index de9c4910..14143346 100644 --- a/shared/js/connection/HandshakeHandler.ts +++ b/shared/js/connection/HandshakeHandler.ts @@ -1,146 +1,153 @@ -namespace connection { - export interface HandshakeIdentityHandler { - connection: AbstractServerConnection; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {IdentitifyType} from "tc-shared/profiles/Identity"; +import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {ConnectionProfile} from "tc-shared/profiles/ConnectionProfile"; +import {settings} from "tc-shared/settings"; +import {ConnectParameters, DisconnectReason} from "tc-shared/ConnectionHandler"; - start_handshake(); - register_callback(callback: (success: boolean, message?: string) => any); +export interface HandshakeIdentityHandler { + connection: AbstractServerConnection; + + start_handshake(); + register_callback(callback: (success: boolean, message?: string) => any); +} + +declare const native_client; +export class HandshakeHandler { + private connection: AbstractServerConnection; + private handshake_handler: HandshakeIdentityHandler; + private failed = false; + + readonly profile: ConnectionProfile; + readonly parameters: ConnectParameters; + + constructor(profile: ConnectionProfile, parameters: ConnectParameters) { + this.profile = profile; + this.parameters = parameters; } - export class HandshakeHandler { - private connection: AbstractServerConnection; - private handshake_handler: HandshakeIdentityHandler; - private failed = false; + setConnection(con: AbstractServerConnection) { + this.connection = con; + } - readonly profile: profiles.ConnectionProfile; - readonly parameters: ConnectParameters; - - constructor(profile: profiles.ConnectionProfile, parameters: ConnectParameters) { - this.profile = profile; - this.parameters = parameters; + initialize() { + this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection); + if(!this.handshake_handler) { + this.handshake_failed("failed to create identity handler"); + return; } - setConnection(con: AbstractServerConnection) { - this.connection = con; - } - - initialize() { - this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection); - if(!this.handshake_handler) { - this.handshake_failed("failed to create identity handler"); - return; - } - - this.handshake_handler.register_callback((flag, message) => { - if(flag) - this.handshake_finished(); - else - this.handshake_failed(message); - }); - } - - get_identity_handler() : HandshakeIdentityHandler { - return this.handshake_handler; - } - - startHandshake() { - this.handshake_handler.start_handshake(); - } - - on_teamspeak() { - const type = this.profile.selected_type(); - if(type == profiles.identities.IdentitifyType.TEAMSPEAK) + this.handshake_handler.register_callback((flag, message) => { + if(flag) this.handshake_finished(); - else { + else + this.handshake_failed(message); + }); + } - if(this.failed) return; + get_identity_handler() : HandshakeIdentityHandler { + return this.handshake_handler; + } - this.failed = true; - this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED); - } - } + startHandshake() { + this.handshake_handler.start_handshake(); + } + + on_teamspeak() { + const type = this.profile.selected_type(); + if(type == IdentitifyType.TEAMSPEAK) + this.handshake_finished(); + else { - private handshake_failed(message: string) { if(this.failed) return; this.failed = true; - this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_FAILED, message); - } - - private handshake_finished(version?: string) { - const _native = window["native"]; - if(native_client && _native && _native.client_version && !version) { - _native.client_version() - .then( this.handshake_finished.bind(this)) - .catch(error => { - console.error(tr("Failed to get version:")); - console.error(error); - this.handshake_finished("?.?.?"); - }); - return; - } - - const git_version = settings.static_global("version", "unknown"); - const browser_name = (navigator.browserSpecs || {})["name"] || " "; - let data = { - client_nickname: this.parameters.nickname || "Another TeaSpeak user", - client_platform: (browser_name ? browser_name + " " : "") + navigator.platform, - client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")", - client_version_sign: undefined, - - client_default_channel: (this.parameters.channel || {} as any).target, - client_default_channel_password: (this.parameters.channel || {} as any).password, - client_default_token: this.parameters.token, - - client_server_password: this.parameters.password ? this.parameters.password.password : undefined, - client_browser_engine: navigator.product, - - client_input_hardware: this.connection.client.client_status.input_hardware, - client_output_hardware: false, - client_input_muted: this.connection.client.client_status.input_muted, - client_output_muted: this.connection.client.client_status.output_muted, - }; - - //0.0.1 [Build: 1549713549] Linux 7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ== - - if(version) { - data.client_version = "TeaClient "; - data.client_version += " " + version; - - const os = require("os"); - const arch_mapping = { - "x32": "32bit", - "x64": "64bit" - }; - - data.client_version += " " + (arch_mapping[os.arch()] || os.arch()); - - const os_mapping = { - "win32": "Windows", - "linux": "Linux" - }; - data.client_platform = (os_mapping[os.platform()] || os.platform()); - } - - /* required to keep compatibility */ - if(this.profile.selected_type() === profiles.identities.IdentitifyType.TEAMSPEAK) { - data["client_key_offset"] = (this.profile.selected_identity() as profiles.identities.TeaSpeakIdentity).hash_number; - } - - this.connection.send_command("clientinit", data).catch(error => { - if(error instanceof CommandResult) { - if(error.id == 1028) { - this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); - } else if(error.id == 783 || error.id == 519) { - error.extra_message = isNaN(parseInt(error.extra_message)) ? "8" : error.extra_message; - this.connection.client.handleDisconnect(DisconnectReason.IDENTITY_TOO_LOW, error); - } else if(error.id == 3329) { - this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_BANNED, error); - } else { - this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); - } - } else - this.connection.disconnect(); - }); + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_TEAMSPEAK_REQUIRED); } } + + private handshake_failed(message: string) { + if(this.failed) return; + + this.failed = true; + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_FAILED, message); + } + + private handshake_finished(version?: string) { + const _native = window["native"]; + if(native_client && _native && _native.client_version && !version) { + _native.client_version() + .then( this.handshake_finished.bind(this)) + .catch(error => { + console.error(tr("Failed to get version:")); + console.error(error); + this.handshake_finished("?.?.?"); + }); + return; + } + + const git_version = settings.static_global("version", "unknown"); + const browser_name = (navigator.browserSpecs || {})["name"] || " "; + let data = { + client_nickname: this.parameters.nickname || "Another TeaSpeak user", + client_platform: (browser_name ? browser_name + " " : "") + navigator.platform, + client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")", + client_version_sign: undefined, + + client_default_channel: (this.parameters.channel || {} as any).target, + client_default_channel_password: (this.parameters.channel || {} as any).password, + client_default_token: this.parameters.token, + + client_server_password: this.parameters.password ? this.parameters.password.password : undefined, + client_browser_engine: navigator.product, + + client_input_hardware: this.connection.client.client_status.input_hardware, + client_output_hardware: false, + client_input_muted: this.connection.client.client_status.input_muted, + client_output_muted: this.connection.client.client_status.output_muted, + }; + + //0.0.1 [Build: 1549713549] Linux 7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ== + + if(version) { + data.client_version = "TeaClient "; + data.client_version += " " + version; + + const os = require("os"); + const arch_mapping = { + "x32": "32bit", + "x64": "64bit" + }; + + data.client_version += " " + (arch_mapping[os.arch()] || os.arch()); + + const os_mapping = { + "win32": "Windows", + "linux": "Linux" + }; + data.client_platform = (os_mapping[os.platform()] || os.platform()); + } + + /* required to keep compatibility */ + if(this.profile.selected_type() === IdentitifyType.TEAMSPEAK) { + data["client_key_offset"] = (this.profile.selected_identity() as TeaSpeakIdentity).hash_number; + } + + this.connection.send_command("clientinit", data).catch(error => { + if(error instanceof CommandResult) { + if(error.id == 1028) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); + } else if(error.id == 783 || error.id == 519) { + error.extra_message = isNaN(parseInt(error.extra_message)) ? "8" : error.extra_message; + this.connection.client.handleDisconnect(DisconnectReason.IDENTITY_TOO_LOW, error); + } else if(error.id == 3329) { + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_BANNED, error); + } else { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); + } + } else + this.connection.disconnect(); + }); + } } \ No newline at end of file diff --git a/shared/js/connection/ServerConnectionDeclaration.ts b/shared/js/connection/ServerConnectionDeclaration.ts index c2a71825..362e9163 100644 --- a/shared/js/connection/ServerConnectionDeclaration.ts +++ b/shared/js/connection/ServerConnectionDeclaration.ts @@ -1,4 +1,6 @@ -enum ErrorID { +import {LaterPromise} from "tc-shared/utils/LaterPromise"; + +export enum ErrorID { NOT_IMPLEMENTED = 0x2, COMMAND_NOT_FOUND = 0x100, @@ -15,7 +17,7 @@ enum ErrorID { CONVERSATION_IS_PRIVATE = 0x2202 } -class CommandResult { +export class CommandResult { success: boolean; id: number; message: string; @@ -35,39 +37,39 @@ class CommandResult { } } -interface ClientNameInfo { +export interface ClientNameInfo { //cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9 client_unique_id: string; client_nickname: string; client_database_id: number; } -interface ClientNameFromUid { +export interface ClientNameFromUid { promise: LaterPromise, keys: string[], response: ClientNameInfo[] } -interface ServerGroupClient { +export interface ServerGroupClient { client_nickname: string; client_unique_identifier: string; client_database_id: number; } -interface QueryListEntry { +export interface QueryListEntry { username: string; unique_id: string; bounded_server: number; } -interface QueryList { +export interface QueryList { flag_own: boolean; flag_all: boolean; queries: QueryListEntry[]; } -interface Playlist { +export interface Playlist { playlist_id: number; playlist_bot_id: number; playlist_title: string; @@ -83,7 +85,7 @@ interface Playlist { needed_power_song_remove: number; } -interface PlaylistInfo { +export interface PlaylistInfo { playlist_id: number, playlist_title: string, playlist_description: string, @@ -100,7 +102,7 @@ interface PlaylistInfo { playlist_max_songs: number } -interface PlaylistSong { +export interface PlaylistSong { song_id: number; song_previous_song_id: number; song_invoker: string; diff --git a/shared/js/crypto/asn1.ts b/shared/js/crypto/asn1.ts index 8184a4aa..c11ec360 100644 --- a/shared/js/crypto/asn1.ts +++ b/shared/js/crypto/asn1.ts @@ -14,534 +14,532 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -namespace asn1 { - declare class Int10 { - constructor(value?: any); +declare class Int10 { + constructor(value?: any); - sub(sub: number); - mulAdd(mul: number, add: number); - simplify(); + sub(sub: number); + mulAdd(mul: number, add: number); + simplify(); +} + +const ellipsis = "\u2026"; + +function string_cut(str, len) { + if (str.length > len) + str = str.substring(0, len) + ellipsis; + return str; +} + +export class Stream { + private static HEX_DIGITS = "0123456789ABCDEF"; + private static reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + private static reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; + + position: number; + data: string | ArrayBuffer; + + constructor(data: string | Stream | ArrayBuffer, position: number) { + if (data instanceof Stream) + this.data = data.data; + else + this.data = data; + + this.position = position; } - const ellipsis = "\u2026"; + length() : number { + if (this.data instanceof ArrayBuffer) + return this.data.byteLength; + return this.data.length; + } - function string_cut(str, len) { - if (str.length > len) - str = str.substring(0, len) + ellipsis; + get(position?: number) { + if (position === undefined) + position = this.position++; + + if (position >= this.length()) + throw 'Requesting byte offset ' + this.position + ' on a stream of length ' + this.length(); + + return (typeof(this.data) === "string") ? this.data.charCodeAt(position) : this.data[position]; + } + + hexByte(byte: number) { + return Stream.HEX_DIGITS.charAt((byte >> 4) & 0xF) + Stream.HEX_DIGITS.charAt(byte & 0xF); + } + + parseStringISO(start, end) { + let s = ""; + for (let i = start; i < end; ++i) + s += String.fromCharCode(this.get(i)); + return s; + } + + parseStringUTF(start, end) { + let s = ""; + for (let i = start; i < end;) { + let c = this.get(i++); + if (c < 128) + s += String.fromCharCode(c); + else if ((c > 191) && (c < 224)) + s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); + else + s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); + } + return s; + } + + parseStringBMP(start, end) { + let str = "", hi, lo; + for (let i = start; i < end;) { + hi = this.get(i++); + lo = this.get(i++); + str += String.fromCharCode((hi << 8) | lo); + } return str; } - export class Stream { - private static HEX_DIGITS = "0123456789ABCDEF"; - private static reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; - private static reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; - - position: number; - data: string | ArrayBuffer; - - constructor(data: string | Stream | ArrayBuffer, position: number) { - if (data instanceof Stream) - this.data = data.data; - else - this.data = data; - - this.position = position; + parseTime(start, end, shortYear) { + let s = this.parseStringISO(start, end), + m = (shortYear ? Stream.reTimeS : Stream.reTimeL).exec(s); + if (!m) + return "Unrecognized time: " + s; + if (shortYear) { + // to avoid querying the timer, use the fixed range [1970, 2069] + // it will conform with ITU X.400 [-10, +40] sliding window until 2030 + //m[1] = +m[1]; + //m[1] += (parseInt(m[1]) < 70) ? 2000 : 1900; + throw "fixme!"; } - - length() : number { - if (this.data instanceof ArrayBuffer) - return this.data.byteLength; - return this.data.length; + s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; + if (m[5]) { + s += ":" + m[5]; + if (m[6]) { + s += ":" + m[6]; + if (m[7]) + s += "." + m[7]; + } } - - get(position?: number) { - if (position === undefined) - position = this.position++; - - if (position >= this.length()) - throw 'Requesting byte offset ' + this.position + ' on a stream of length ' + this.length(); - - return (typeof(this.data) === "string") ? this.data.charCodeAt(position) : this.data[position]; + if (m[8]) { + s += " UTC"; + if (m[8] != 'Z') { + s += m[8]; + if (m[9]) + s += ":" + m[9]; + } } + return s; + }; - hexByte(byte: number) { - return Stream.HEX_DIGITS.charAt((byte >> 4) & 0xF) + Stream.HEX_DIGITS.charAt(byte & 0xF); + parseInteger(start, end) { + let current: number = this.get(start); + + let negative = (current > 127); + let padding = negative ? 255 : 0; + let length; + let descriptor: number | string; + + // skip unuseful bits (not allowed in DER) + while (current == padding && ++start < end) + current = this.get(start); + + length = end - start; + if (length === 0) + return negative ? '-1' : '0'; + + // show bit length of huge integers + if (length > 4) { + descriptor = current; + length <<= 3; /* calculate bit length */ + + while (((descriptor ^ padding) & 0x80) == 0) { + descriptor <<= 1; + --length; + } + descriptor = "(" + length + " bit)\n"; } + // decode the integer + if (negative) current = current - 256; - parseStringISO(start, end) { - let s = ""; - for (let i = start; i < end; ++i) - s += String.fromCharCode(this.get(i)); - return s; - } - - parseStringUTF(start, end) { - let s = ""; - for (let i = start; i < end;) { - let c = this.get(i++); - if (c < 128) - s += String.fromCharCode(c); - else if ((c > 191) && (c < 224)) - s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); - else - s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); - } - return s; - } - - parseStringBMP(start, end) { - let str = "", hi, lo; - for (let i = start; i < end;) { - hi = this.get(i++); - lo = this.get(i++); - str += String.fromCharCode((hi << 8) | lo); - } - return str; - } - - parseTime(start, end, shortYear) { - let s = this.parseStringISO(start, end), - m = (shortYear ? Stream.reTimeS : Stream.reTimeL).exec(s); - if (!m) - return "Unrecognized time: " + s; - if (shortYear) { - // to avoid querying the timer, use the fixed range [1970, 2069] - // it will conform with ITU X.400 [-10, +40] sliding window until 2030 - //m[1] = +m[1]; - //m[1] += (parseInt(m[1]) < 70) ? 2000 : 1900; - throw "fixme!"; - } - s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; - if (m[5]) { - s += ":" + m[5]; - if (m[6]) { - s += ":" + m[6]; - if (m[7]) - s += "." + m[7]; - } - } - if (m[8]) { - s += " UTC"; - if (m[8] != 'Z') { - s += m[8]; - if (m[9]) - s += ":" + m[9]; - } - } - return s; - }; - - parseInteger(start, end) { - let current: number = this.get(start); - - let negative = (current > 127); - let padding = negative ? 255 : 0; - let length; - let descriptor: number | string; - - // skip unuseful bits (not allowed in DER) - while (current == padding && ++start < end) - current = this.get(start); - - length = end - start; - if (length === 0) - return negative ? '-1' : '0'; - - // show bit length of huge integers - if (length > 4) { - descriptor = current; - length <<= 3; /* calculate bit length */ - - while (((descriptor ^ padding) & 0x80) == 0) { - descriptor <<= 1; - --length; - } - descriptor = "(" + length + " bit)\n"; - } - // decode the integer - if (negative) current = current - 256; - - let number = ""; - if(typeof(Int10) !== "undefined") { - let n = new Int10(current); - for (let i = start + 1; i < end; ++i) - n.mulAdd(256, this.get(i)); - number = n.toString(); - } else { - let n = 0; - for (let i = start + 1; i < end; ++i) { - n <<= 8; - n += this.get(i); - } - number = n.toString(); - } - return descriptor + number; - }; - - isASCII(start: number, end: number) { - for (let i = start; i < end; ++i) { - const c = this.get(i); - if (c < 32 || c > 176) - return false; - } - return true; - }; - - parseBitString(start, end, maxLength) { - let unusedBit = this.get(start), - lenBit = ((end - start - 1) << 3) - unusedBit, - intro = "(" + lenBit + " bit)\n", - s = ""; + let number = ""; + if(typeof(Int10) !== "undefined") { + let n = new Int10(current); + for (let i = start + 1; i < end; ++i) + n.mulAdd(256, this.get(i)); + number = n.toString(); + } else { + let n = 0; for (let i = start + 1; i < end; ++i) { - let b = this.get(i), - skip = (i == end - 1) ? unusedBit : 0; - for (let j = 7; j >= skip; --j) - s += (b >> j) & 1 ? "1" : "0"; + n <<= 8; + n += this.get(i); + } + number = n.toString(); + } + return descriptor + number; + }; + + isASCII(start: number, end: number) { + for (let i = start; i < end; ++i) { + const c = this.get(i); + if (c < 32 || c > 176) + return false; + } + return true; + }; + + parseBitString(start, end, maxLength) { + let unusedBit = this.get(start), + lenBit = ((end - start - 1) << 3) - unusedBit, + intro = "(" + lenBit + " bit)\n", + s = ""; + for (let i = start + 1; i < end; ++i) { + let b = this.get(i), + skip = (i == end - 1) ? unusedBit : 0; + for (let j = 7; j >= skip; --j) + s += (b >> j) & 1 ? "1" : "0"; + if (s.length > maxLength) + return intro + string_cut(s, maxLength); + } + return intro + s; + }; + + parseOctetString(start, end, maxLength) { + if (this.isASCII(start, end)) + return string_cut(this.parseStringISO(start, end), maxLength); + let len = end - start, + s = "(" + len + " byte)\n"; + maxLength /= 2; // we work in bytes + if (len > maxLength) + end = start + maxLength; + for (let i = start; i < end; ++i) + s += this.hexByte(this.get(i)); + if (len > maxLength) + s += ellipsis; + return s; + }; + + parseOID(start, end, maxLength) { + let s = '', + n = new Int10(), + bits = 0; + for (let i = start; i < end; ++i) { + let v = this.get(i); + n.mulAdd(128, v & 0x7F); + bits += 7; + if (!(v & 0x80)) { // finished + if (s === '') { + n = n.simplify(); + if (n instanceof Int10) { + n.sub(80); + s = "2." + n.toString(); + } else { + let m = n < 80 ? n < 40 ? 0 : 1 : 2; + s = m + "." + (n - m * 40); + } + } else + s += "." + n.toString(); if (s.length > maxLength) - return intro + string_cut(s, maxLength); - } - return intro + s; - }; - - parseOctetString(start, end, maxLength) { - if (this.isASCII(start, end)) - return string_cut(this.parseStringISO(start, end), maxLength); - let len = end - start, - s = "(" + len + " byte)\n"; - maxLength /= 2; // we work in bytes - if (len > maxLength) - end = start + maxLength; - for (let i = start; i < end; ++i) - s += this.hexByte(this.get(i)); - if (len > maxLength) - s += ellipsis; - return s; - }; - - parseOID(start, end, maxLength) { - let s = '', - n = new Int10(), + return string_cut(s, maxLength); + n = new Int10(); bits = 0; - for (let i = start; i < end; ++i) { - let v = this.get(i); - n.mulAdd(128, v & 0x7F); - bits += 7; - if (!(v & 0x80)) { // finished - if (s === '') { - n = n.simplify(); - if (n instanceof Int10) { - n.sub(80); - s = "2." + n.toString(); - } else { - let m = n < 80 ? n < 40 ? 0 : 1 : 2; - s = m + "." + (n - m * 40); - } - } else - s += "." + n.toString(); - if (s.length > maxLength) - return string_cut(s, maxLength); - n = new Int10(); - bits = 0; - } - } - if (bits > 0) - s += ".incomplete"; - /* FIXME - if (typeof oids === 'object') { - let oid = oids[s]; - if (oid) { - if (oid.d) s += "\n" + oid.d; - if (oid.c) s += "\n" + oid.c; - if (oid.w) s += "\n(warning!)"; - } - } - */ - return s; - }; - } - - export enum TagClass { - UNIVERSAL = 0x00, - APPLICATION = 0x01, - CONTEXT = 0x02, - PRIVATE = 0x03 - } - - export enum TagType { - EOC = 0x00, - BOOLEAN = 0x01, - INTEGER = 0x02, - BIT_STRING = 0x03, - OCTET_STRING = 0x04, - NULL = 0x05, - OBJECT_IDENTIFIER = 0x06, - ObjectDescriptor = 0x07, - EXTERNAL = 0x08, - REAL = 0x09, - ENUMERATED = 0x0A, - EMBEDDED_PDV = 0x0B, - UTF8String = 0x0C, - SEQUENCE = 0x10, - SET = 0x11, - NumericString = 0x12, - PrintableString = 0x13, // ASCII subset - TeletextString = 0x14, // aka T61String - VideotexString = 0x15, - IA5String = 0x16, // ASCII - UTCTime = 0x17, - GeneralizedTime = 0x18, - GraphicString = 0x19, - VisibleString = 0x1A, // ASCII subset - GeneralString = 0x1B, - UniversalString = 0x1C, - BMPString = 0x1E - } - - class ASN1Tag { - tagClass: TagClass; - type: TagType; - tagConstructed: boolean; - tagNumber: number; - - constructor(stream: Stream) { - let buf = stream.get(); - this.tagClass = buf >> 6; - this.tagConstructed = ((buf & 0x20) !== 0); - this.tagNumber = buf & 0x1F; - if (this.tagNumber == 0x1F) { // long tag - let n = new Int10(); - do { - buf = stream.get(); - n.mulAdd(128, buf & 0x7F); - } while (buf & 0x80); - this.tagNumber = n.simplify(); } } + if (bits > 0) + s += ".incomplete"; + /* FIXME + if (typeof oids === 'object') { + let oid = oids[s]; + if (oid) { + if (oid.d) s += "\n" + oid.d; + if (oid.c) s += "\n" + oid.c; + if (oid.w) s += "\n(warning!)"; + } + } + */ + return s; + }; +} - isUniversal() { - return this.tagClass === 0x00; - }; +export enum TagClass { + UNIVERSAL = 0x00, + APPLICATION = 0x01, + CONTEXT = 0x02, + PRIVATE = 0x03 +} - isEOC() { - return this.tagClass === 0x00 && this.tagNumber === 0x00; - }; +export enum TagType { + EOC = 0x00, + BOOLEAN = 0x01, + INTEGER = 0x02, + BIT_STRING = 0x03, + OCTET_STRING = 0x04, + NULL = 0x05, + OBJECT_IDENTIFIER = 0x06, + ObjectDescriptor = 0x07, + EXTERNAL = 0x08, + REAL = 0x09, + ENUMERATED = 0x0A, + EMBEDDED_PDV = 0x0B, + UTF8String = 0x0C, + SEQUENCE = 0x10, + SET = 0x11, + NumericString = 0x12, + PrintableString = 0x13, // ASCII subset + TeletextString = 0x14, // aka T61String + VideotexString = 0x15, + IA5String = 0x16, // ASCII + UTCTime = 0x17, + GeneralizedTime = 0x18, + GraphicString = 0x19, + VisibleString = 0x1A, // ASCII subset + GeneralString = 0x1B, + UniversalString = 0x1C, + BMPString = 0x1E +} + +class ASN1Tag { + tagClass: TagClass; + type: TagType; + tagConstructed: boolean; + tagNumber: number; + + constructor(stream: Stream) { + let buf = stream.get(); + this.tagClass = buf >> 6; + this.tagConstructed = ((buf & 0x20) !== 0); + this.tagNumber = buf & 0x1F; + if (this.tagNumber == 0x1F) { // long tag + let n = new Int10(); + do { + buf = stream.get(); + n.mulAdd(128, buf & 0x7F); + } while (buf & 0x80); + this.tagNumber = n.simplify(); + } } - export class ASN1 { - stream: Stream; - header: number; - length: number; - tag: ASN1Tag; - children: ASN1[]; + isUniversal() { + return this.tagClass === 0x00; + }; - constructor(stream: Stream, header: number, length: number, tag: ASN1Tag, children: ASN1[]) { - this.stream = stream; - this.header = header; - this.length = length; - this.tag = tag; - this.children = children; + isEOC() { + return this.tagClass === 0x00 && this.tagNumber === 0x00; + }; +} + +export class ASN1 { + stream: Stream; + header: number; + length: number; + tag: ASN1Tag; + children: ASN1[]; + + constructor(stream: Stream, header: number, length: number, tag: ASN1Tag, children: ASN1[]) { + this.stream = stream; + this.header = header; + this.length = length; + this.tag = tag; + this.children = children; + } + + content(max_length?: number, type?: TagType) { // a preview of the content (intended for humans) + if (this.tag === undefined) return null; + if (max_length === undefined) + max_length = Infinity; + + let content = this.posContent(), + len = Math.abs(this.length); + + if (!this.tag.isUniversal()) { + if (this.children !== null) + return "(" + this.children.length + " elem)"; + return this.stream.parseOctetString(content, content + len, max_length); } - - content(max_length?: number, type?: TagType) { // a preview of the content (intended for humans) - if (this.tag === undefined) return null; - if (max_length === undefined) - max_length = Infinity; - - let content = this.posContent(), - len = Math.abs(this.length); - - if (!this.tag.isUniversal()) { + switch (type || this.tag.tagNumber) { + case 0x01: // BOOLEAN + return (this.stream.get(content) === 0) ? "false" : "true"; + case 0x02: // INTEGER + return this.stream.parseInteger(content, content + len); + case 0x03: // BIT_STRING + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseBitString(content, content + len, max_length); + case 0x04: // OCTET_STRING + return this.children ? "(" + this.children.length + " elem)" : + this.stream.parseOctetString(content, content + len, max_length); + //case 0x05: // NULL + case 0x06: // OBJECT_IDENTIFIER + return this.stream.parseOID(content, content + len, max_length); + //case 0x07: // ObjectDescriptor + //case 0x08: // EXTERNAL + //case 0x09: // REAL + //case 0x0A: // ENUMERATED + //case 0x0B: // EMBEDDED_PDV + case 0x10: // SEQUENCE + case 0x11: // SET if (this.children !== null) return "(" + this.children.length + " elem)"; - return this.stream.parseOctetString(content, content + len, max_length); - } - switch (type || this.tag.tagNumber) { - case 0x01: // BOOLEAN - return (this.stream.get(content) === 0) ? "false" : "true"; - case 0x02: // INTEGER - return this.stream.parseInteger(content, content + len); - case 0x03: // BIT_STRING - return this.children ? "(" + this.children.length + " elem)" : - this.stream.parseBitString(content, content + len, max_length); - case 0x04: // OCTET_STRING - return this.children ? "(" + this.children.length + " elem)" : - this.stream.parseOctetString(content, content + len, max_length); - //case 0x05: // NULL - case 0x06: // OBJECT_IDENTIFIER - return this.stream.parseOID(content, content + len, max_length); - //case 0x07: // ObjectDescriptor - //case 0x08: // EXTERNAL - //case 0x09: // REAL - //case 0x0A: // ENUMERATED - //case 0x0B: // EMBEDDED_PDV - case 0x10: // SEQUENCE - case 0x11: // SET - if (this.children !== null) - return "(" + this.children.length + " elem)"; - else - return "(no elem)"; - case 0x0C: // UTF8String - return string_cut(this.stream.parseStringUTF(content, content + len), max_length); - case 0x12: // NumericString - case 0x13: // PrintableString - case 0x14: // TeletexString - case 0x15: // VideotexString - case 0x16: // IA5String - //case 0x19: // GraphicString - case 0x1A: // VisibleString - //case 0x1B: // GeneralString - //case 0x1C: // UniversalString - return string_cut(this.stream.parseStringISO(content, content + len), max_length); - case 0x1E: // BMPString - return string_cut(this.stream.parseStringBMP(content, content + len), max_length); - case 0x17: // UTCTime - case 0x18: // GeneralizedTime - return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); - } - return null; - }; - - typeName(): string { - switch (this.tag.tagClass) { - case 0: // universal - return TagType[this.tag.tagNumber] || ("Universal_" + this.tag.tagNumber.toString()); - case 1: - return "Application_" + this.tag.tagNumber.toString(); - case 2: - return "[" + this.tag.tagNumber.toString() + "]"; // Context - case 3: - return "Private_" + this.tag.tagNumber.toString(); - } - }; - - toString() { - return this.typeName() + "@" + this.stream.position + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.children === null) ? 'null' : this.children.length) + "]"; + else + return "(no elem)"; + case 0x0C: // UTF8String + return string_cut(this.stream.parseStringUTF(content, content + len), max_length); + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + //case 0x19: // GraphicString + case 0x1A: // VisibleString + //case 0x1B: // GeneralString + //case 0x1C: // UniversalString + return string_cut(this.stream.parseStringISO(content, content + len), max_length); + case 0x1E: // BMPString + return string_cut(this.stream.parseStringBMP(content, content + len), max_length); + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); } + return null; + }; - toPrettyString(indent) { - if (indent === undefined) indent = ''; - let s = indent + this.typeName() + " @" + this.stream.position; - if (this.length >= 0) - s += "+"; - s += this.length; - if (this.tag.tagConstructed) - s += " (constructed)"; - else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.children !== null)) - s += " (encapsulates)"; - let content = this.content(); - if (content) - s += ": " + content.replace(/\n/g, '|'); - s += "\n"; - if (this.children !== null) { - indent += ' '; - for (let i = 0, max = this.children.length; i < max; ++i) - s += this.children[i].toPrettyString(indent); - } - return s; - }; + typeName(): string { + switch (this.tag.tagClass) { + case 0: // universal + return TagType[this.tag.tagNumber] || ("Universal_" + this.tag.tagNumber.toString()); + case 1: + return "Application_" + this.tag.tagNumber.toString(); + case 2: + return "[" + this.tag.tagNumber.toString() + "]"; // Context + case 3: + return "Private_" + this.tag.tagNumber.toString(); + } + }; - posStart() { - return this.stream.position; - }; + toString() { + return this.typeName() + "@" + this.stream.position + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.children === null) ? 'null' : this.children.length) + "]"; + } - posContent() { - return this.stream.position + this.header; - }; + toPrettyString(indent) { + if (indent === undefined) indent = ''; + let s = indent + this.typeName() + " @" + this.stream.position; + if (this.length >= 0) + s += "+"; + s += this.length; + if (this.tag.tagConstructed) + s += " (constructed)"; + else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.children !== null)) + s += " (encapsulates)"; + let content = this.content(); + if (content) + s += ": " + content.replace(/\n/g, '|'); + s += "\n"; + if (this.children !== null) { + indent += ' '; + for (let i = 0, max = this.children.length; i < max; ++i) + s += this.children[i].toPrettyString(indent); + } + return s; + }; - posEnd() { - return this.stream.position + this.header + Math.abs(this.length); - }; + posStart() { + return this.stream.position; + }; - static decodeLength(stream: Stream) { - let buf = stream.get(); - const len = buf & 0x7F; - if (len == buf) - return len; - if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways - throw "Length over 48 bits not supported at position " + (stream.position - 1); - if (len === 0) - return null; // undefined + posContent() { + return this.stream.position + this.header; + }; - buf = 0; - for (let i = 0; i < len; ++i) - buf = (buf << 8) + stream.get(); - return buf; - }; + posEnd() { + return this.stream.position + this.header + Math.abs(this.length); + }; - static encodeLength(buffer: Uint8Array, offset: number, length: number) { - if(length < 0x7F) { - buffer[offset] = length; - } else { - buffer[offset] = 0x80; - let index = 1; - while(length > 0) { - buffer[offset + index++] = length & 0xFF; - length >>= 8; - buffer[offset] += 1; - } + static decodeLength(stream: Stream) { + let buf = stream.get(); + const len = buf & 0x7F; + if (len == buf) + return len; + if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways + throw "Length over 48 bits not supported at position " + (stream.position - 1); + if (len === 0) + return null; // undefined + + buf = 0; + for (let i = 0; i < len; ++i) + buf = (buf << 8) + stream.get(); + return buf; + }; + + static encodeLength(buffer: Uint8Array, offset: number, length: number) { + if(length < 0x7F) { + buffer[offset] = length; + } else { + buffer[offset] = 0x80; + let index = 1; + while(length > 0) { + buffer[offset + index++] = length & 0xFF; + length >>= 8; + buffer[offset] += 1; } } } +} - function decode0(stream: Stream) { - const streamStart = new Stream(stream, 0); /* copy */ - const tag = new ASN1Tag(stream); - let len = ASN1.decodeLength(stream); - const start = stream.position; - const length_header = start - streamStart.position; - let children = null; - const query_children = () => { - children = []; - if (len !== null) { - const end = start + len; - if (end > stream.length()) - throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; - while (stream.position < end) - children[children.length] = decode0(stream); - if (stream.position != end) - throw 'Content size is not correct for container at offset ' + start; - } else { - // undefined length - try { - while (true) { - const s = decode0(stream); - if (s.tag.isEOC()) break; - children[children.length] = s; - } - len = start - stream.position; // undefined lengths are represented as negative values - } catch (e) { - throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e; - } - } - }; - if (tag.tagConstructed) { - // must have valid content - query_children(); - } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { - // sometimes BitString and OctetString are used to encapsulate ASN.1 +function decode0(stream: Stream) { + const streamStart = new Stream(stream, 0); /* copy */ + const tag = new ASN1Tag(stream); + let len = ASN1.decodeLength(stream); + const start = stream.position; + const length_header = start - streamStart.position; + let children = null; + const query_children = () => { + children = []; + if (len !== null) { + const end = start + len; + if (end > stream.length()) + throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; + while (stream.position < end) + children[children.length] = decode0(stream); + if (stream.position != end) + throw 'Content size is not correct for container at offset ' + start; + } else { + // undefined length try { - if (tag.tagNumber == 0x03) - if (stream.get() != 0) - throw "BIT STRINGs with unused bits cannot encapsulate."; - query_children(); - for (let i = 0; i < children.length; ++i) - if (children[i].tag.isEOC()) - throw 'EOC is not supposed to be actual content.'; + while (true) { + const s = decode0(stream); + if (s.tag.isEOC()) break; + children[children.length] = s; + } + len = start - stream.position; // undefined lengths are represented as negative values } catch (e) { - // but silently ignore when they don't - children = null; - //DEBUG console.log('Could not decode structure at ' + start + ':', e); + throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e; } } - if (children === null) { - if (len === null) - throw "We can't skip over an invalid tag with undefined length at offset " + start; - stream.position = start + Math.abs(len); + }; + if (tag.tagConstructed) { + // must have valid content + query_children(); + } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { + // sometimes BitString and OctetString are used to encapsulate ASN.1 + try { + if (tag.tagNumber == 0x03) + if (stream.get() != 0) + throw "BIT STRINGs with unused bits cannot encapsulate."; + query_children(); + for (let i = 0; i < children.length; ++i) + if (children[i].tag.isEOC()) + throw 'EOC is not supposed to be actual content.'; + } catch (e) { + // but silently ignore when they don't + children = null; + //DEBUG console.log('Could not decode structure at ' + start + ':', e); } - return new ASN1(streamStart, length_header, len, tag, children); } + if (children === null) { + if (len === null) + throw "We can't skip over an invalid tag with undefined length at offset " + start; + stream.position = start + Math.abs(len); + } + return new ASN1(streamStart, length_header, len, tag, children); +} - export function decode(stream: string | ArrayBuffer) { - return decode0(new Stream(stream, 0)); - } +export function decode(stream: string | ArrayBuffer) { + return decode0(new Stream(stream, 0)); } \ No newline at end of file diff --git a/shared/js/crypto/crc32.ts b/shared/js/crypto/crc32.ts index 8122a8a2..52e14979 100644 --- a/shared/js/crypto/crc32.ts +++ b/shared/js/crypto/crc32.ts @@ -1,4 +1,4 @@ -class Crc32 { +export class Crc32 { private static readonly lookup = [ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, diff --git a/shared/js/crypto/hex.ts b/shared/js/crypto/hex.ts index 9c700341..642488d0 100644 --- a/shared/js/crypto/hex.ts +++ b/shared/js/crypto/hex.ts @@ -1,20 +1,18 @@ -namespace hex { - export function encode(buffer) { - let hexCodes = []; - let view = new DataView(buffer); - for (let i = 0; i < view.byteLength % 4; i ++) { - let value = view.getUint32(i * 4); - let stringValue = value.toString(16); - let padding = '00000000'; - let paddedValue = (padding + stringValue).slice(-padding.length); - hexCodes.push(paddedValue); - } - for (let i = (view.byteLength % 4) * 4; i < view.byteLength; i++) { - let value = view.getUint8(i).toString(16); - let padding = '00'; - hexCodes.push((padding + value).slice(-padding.length)); - } - - return hexCodes.join(""); +export function encode(buffer) { + let hexCodes = []; + let view = new DataView(buffer); + for (let i = 0; i < view.byteLength % 4; i ++) { + let value = view.getUint32(i * 4); + let stringValue = value.toString(16); + let padding = '00000000'; + let paddedValue = (padding + stringValue).slice(-padding.length); + hexCodes.push(paddedValue); } + for (let i = (view.byteLength % 4) * 4; i < view.byteLength; i++) { + let value = view.getUint8(i).toString(16); + let padding = '00'; + hexCodes.push((padding + value).slice(-padding.length)); + } + + return hexCodes.join(""); } \ No newline at end of file diff --git a/shared/js/crypto/sha.ts b/shared/js/crypto/sha.ts index 28998cff..a3d28f9f 100644 --- a/shared/js/crypto/sha.ts +++ b/shared/js/crypto/sha.ts @@ -6,405 +6,367 @@ declare class _sha1 { /* interface Window { - TextEncoder: any; +TextEncoder: any; } */ -namespace sha { - /* - * [js-sha1]{@link https://github.com/emn178/js-sha1} - * - * @version 0.6.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2017 - * @license MIT - */ - /*jslint bitwise: true */ - (function() { - 'use strict'; +/* + * [js-sha1]{@link https://github.com/emn178/js-sha1} + * + * @version 0.6.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2017 + * @license MIT + */ +/*jslint bitwise: true */ +(function() { + 'use strict'; - let root: any = typeof window === 'object' ? window : {}; - let NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; - if (NODE_JS) { - root = global; + let root: any = typeof window === 'object' ? window : {}; + let HEX_CHARS = '0123456789abcdef'.split(''); + let EXTRA = [-2147483648, 8388608, 32768, 128]; + let SHIFT = [24, 16, 8, 0]; + let OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + + let blocks = []; + + let createOutputMethod = function (outputType) { + return function (message) { + return new Sha1(true).update(message)[outputType](); + }; + }; + + let createMethod = function () { + let method: any = createOutputMethod('hex'); + method.create = function () { + return new (Sha1 as any)(); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type); } - let COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports; - let AMD = typeof define === 'function' && (define as any).amd; - let HEX_CHARS = '0123456789abcdef'.split(''); - let EXTRA = [-2147483648, 8388608, 32768, 128]; - let SHIFT = [24, 16, 8, 0]; - let OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + return method; + }; - let blocks = []; - - let createOutputMethod = function (outputType) { - return function (message) { - return new Sha1(true).update(message)[outputType](); - }; - }; - - let createMethod = function () { - let method: any = createOutputMethod('hex'); - if (NODE_JS) { - method = nodeWrap(method); - } - method.create = function () { - return new (Sha1 as any)(); - }; - method.update = function (message) { - return method.create().update(message); - }; - for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createOutputMethod(type); - } - return method; - }; - - var nodeWrap = function (method) { - var crypto = eval("require('crypto')"); - var Buffer = eval("require('buffer').Buffer"); - var nodeMethod = function (message) { - if (typeof message === 'string') { - return crypto.createHash('sha1').update(message, 'utf8').digest('hex'); - } else if (message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } else if (message.length === undefined) { - return method(message); - } - return crypto.createHash('sha1').update(new Buffer(message)).digest('hex'); - }; - return nodeMethod; - }; - - function Sha1(sharedMemory) { - if (sharedMemory) { - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.blocks = blocks; - } else { - this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - - this.h0 = 0x67452301; - this.h1 = 0xEFCDAB89; - this.h2 = 0x98BADCFE; - this.h3 = 0x10325476; - this.h4 = 0xC3D2E1F0; - - this.block = this.start = this.bytes = this.hBytes = 0; - this.finalized = this.hashed = false; - this.first = true; + function Sha1(sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; } - Sha1.prototype.update = function (message) { - if (this.finalized) { - return; - } - var notString = typeof(message) !== 'string'; - if (notString && message.constructor === root.ArrayBuffer) { - message = new Uint8Array(message); - } - var code, index = 0, i, length = message.length || 0, blocks = this.blocks; + this.h0 = 0x67452301; + this.h1 = 0xEFCDAB89; + this.h2 = 0x98BADCFE; + this.h3 = 0x10325476; + this.h4 = 0xC3D2E1F0; - while (index < length) { - if (this.hashed) { - this.hashed = false; - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + } - if(notString) { - for (i = this.start; index < length && i < 64; ++index) { - blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.start; index < length && i < 64; ++index) { - code = message.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } + Sha1.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString = typeof(message) !== 'string'; + if (notString && message.constructor === root.ArrayBuffer) { + message = new Uint8Array(message); + } + var code, index = 0, i, length = message.length || 0, blocks = this.blocks; - this.lastByteIndex = i; - this.bytes += i - this.start; - if (i >= 64) { - this.block = blocks[16]; - this.start = i - 64; - this.hash(); - this.hashed = true; - } else { - this.start = i; - } - } - if (this.bytes > 4294967295) { - this.hBytes += this.bytes / 4294967296 << 0; - this.bytes = this.bytes % 4294967296; - } - return this; - }; - - Sha1.prototype.finalize = function () { - if (this.finalized) { - return; - } - this.finalized = true; - var blocks = this.blocks, i = this.lastByteIndex; - blocks[16] = this.block; - blocks[i >> 2] |= EXTRA[i & 3]; - this.block = blocks[16]; - if (i >= 56) { - if (!this.hashed) { - this.hash(); - } + while (index < length) { + if (this.hashed) { + this.hashed = false; blocks[0] = this.block; blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; } - blocks[14] = this.hBytes << 3 | this.bytes >>> 29; - blocks[15] = this.bytes << 3; - this.hash(); - }; - Sha1.prototype.hash = function () { - var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4; - var f, j, t, blocks = this.blocks; - - for(j = 16; j < 80; ++j) { - t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; - blocks[j] = (t << 1) | (t >>> 31); + if(notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; + } + } else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } } - for(j = 0; j < 20; j += 5) { - f = (b & c) | ((~b) & d); - t = (a << 5) | (a >>> 27); - e = t + f + e + 1518500249 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - - f = (a & b) | ((~a) & c); - t = (e << 5) | (e >>> 27); - d = t + f + d + 1518500249 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - - f = (e & a) | ((~e) & b); - t = (d << 5) | (d >>> 27); - c = t + f + c + 1518500249 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - - f = (d & e) | ((~d) & a); - t = (c << 5) | (c >>> 27); - b = t + f + b + 1518500249 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - - f = (c & d) | ((~c) & e); - t = (b << 5) | (b >>> 27); - a = t + f + a + 1518500249 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - - for(; j < 40; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = t + f + e + 1859775393 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = t + f + d + 1859775393 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = t + f + c + 1859775393 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = t + f + b + 1859775393 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = t + f + a + 1859775393 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - - for(; j < 60; j += 5) { - f = (b & c) | (b & d) | (c & d); - t = (a << 5) | (a >>> 27); - e = t + f + e - 1894007588 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - - f = (a & b) | (a & c) | (b & c); - t = (e << 5) | (e >>> 27); - d = t + f + d - 1894007588 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - - f = (e & a) | (e & b) | (a & b); - t = (d << 5) | (d >>> 27); - c = t + f + c - 1894007588 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - - f = (d & e) | (d & a) | (e & a); - t = (c << 5) | (c >>> 27); - b = t + f + b - 1894007588 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - - f = (c & d) | (c & e) | (d & e); - t = (b << 5) | (b >>> 27); - a = t + f + a - 1894007588 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - - for(; j < 80; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = t + f + e - 899497514 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = t + f + d - 899497514 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = t + f + c - 899497514 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = t + f + b - 899497514 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = t + f + a - 899497514 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - - this.h0 = this.h0 + a << 0; - this.h1 = this.h1 + b << 0; - this.h2 = this.h2 + c << 0; - this.h3 = this.h3 + d << 0; - this.h4 = this.h4 + e << 0; - }; - - Sha1.prototype.hex = function () { - this.finalize(); - - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; - - return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + - HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + - HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + - HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + - HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + - HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + - HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + - HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + - HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + - HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + - HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + - HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + - HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + - HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + - HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + - HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + - HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + - HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + - HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + - HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F]; - }; - - Sha1.prototype.toString = Sha1.prototype.hex; - - Sha1.prototype.digest = function () { - this.finalize(); - - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; - - return [ - (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, - (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, - (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, - (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, - (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF - ]; - }; - - Sha1.prototype.array = Sha1.prototype.digest; - - Sha1.prototype.arrayBuffer = function () { - this.finalize(); - - var buffer = new ArrayBuffer(20); - var dataView = new DataView(buffer); - dataView.setUint32(0, this.h0); - dataView.setUint32(4, this.h1); - dataView.setUint32(8, this.h2); - dataView.setUint32(12, this.h3); - dataView.setUint32(16, this.h4); - return buffer; - }; - - var exports = createMethod(); - - if (COMMON_JS) { - module.exports = exports; - } else { - root._sha1 = exports; - if (AMD) { - define(function () { - return exports; - }); + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } else { + this.start = i; } } - })(); + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; - export function encode_text(buffer: string) : ArrayBuffer { - if ((window as any).TextEncoder) { - return new TextEncoder().encode(buffer).buffer; + Sha1.prototype.finalize = function () { + if (this.finalized) { + return; } - let utf8 = unescape(encodeURIComponent(buffer)); - let result = new Uint8Array(utf8.length); - for (let i = 0; i < utf8.length; i++) { - result[i] = utf8.charCodeAt(i); + this.finalized = true; + var blocks = this.blocks, i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; } - return result.buffer; + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + + Sha1.prototype.hash = function () { + var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4; + var f, j, t, blocks = this.blocks; + + for(j = 16; j < 80; ++j) { + t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; + blocks[j] = (t << 1) | (t >>> 31); + } + + for(j = 0; j < 20; j += 5) { + f = (b & c) | ((~b) & d); + t = (a << 5) | (a >>> 27); + e = t + f + e + 1518500249 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + + f = (a & b) | ((~a) & c); + t = (e << 5) | (e >>> 27); + d = t + f + d + 1518500249 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + + f = (e & a) | ((~e) & b); + t = (d << 5) | (d >>> 27); + c = t + f + c + 1518500249 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + + f = (d & e) | ((~d) & a); + t = (c << 5) | (c >>> 27); + b = t + f + b + 1518500249 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + + f = (c & d) | ((~c) & e); + t = (b << 5) | (b >>> 27); + a = t + f + a + 1518500249 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + + for(; j < 40; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e + 1859775393 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d + 1859775393 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c + 1859775393 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b + 1859775393 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a + 1859775393 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + + for(; j < 60; j += 5) { + f = (b & c) | (b & d) | (c & d); + t = (a << 5) | (a >>> 27); + e = t + f + e - 1894007588 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + + f = (a & b) | (a & c) | (b & c); + t = (e << 5) | (e >>> 27); + d = t + f + d - 1894007588 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + + f = (e & a) | (e & b) | (a & b); + t = (d << 5) | (d >>> 27); + c = t + f + c - 1894007588 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + + f = (d & e) | (d & a) | (e & a); + t = (c << 5) | (c >>> 27); + b = t + f + b - 1894007588 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + + f = (c & d) | (c & e) | (d & e); + t = (b << 5) | (b >>> 27); + a = t + f + a - 1894007588 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + + for(; j < 80; j += 5) { + f = b ^ c ^ d; + t = (a << 5) | (a >>> 27); + e = t + f + e - 899497514 + blocks[j] << 0; + b = (b << 30) | (b >>> 2); + + f = a ^ b ^ c; + t = (e << 5) | (e >>> 27); + d = t + f + d - 899497514 + blocks[j + 1] << 0; + a = (a << 30) | (a >>> 2); + + f = e ^ a ^ b; + t = (d << 5) | (d >>> 27); + c = t + f + c - 899497514 + blocks[j + 2] << 0; + e = (e << 30) | (e >>> 2); + + f = d ^ e ^ a; + t = (c << 5) | (c >>> 27); + b = t + f + b - 899497514 + blocks[j + 3] << 0; + d = (d << 30) | (d >>> 2); + + f = c ^ d ^ e; + t = (b << 5) | (b >>> 27); + a = t + f + a - 899497514 + blocks[j + 4] << 0; + c = (c << 30) | (c >>> 2); + } + + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + }; + + Sha1.prototype.hex = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + + return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F]; + }; + + Sha1.prototype.toString = Sha1.prototype.hex; + + Sha1.prototype.digest = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; + + return [ + (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, + (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, + (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, + (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, + (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF + ]; + }; + + Sha1.prototype.array = Sha1.prototype.digest; + + Sha1.prototype.arrayBuffer = function () { + this.finalize(); + + const buffer = new ArrayBuffer(20); + const dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + return buffer; + }; + + createMethod(); +})(); + +export function encode_text(buffer: string) : ArrayBuffer { + if ((window as any).TextEncoder) { + return new TextEncoder().encode(buffer).buffer; } - export function sha1(message: string | ArrayBuffer) : PromiseLike { - if(!(typeof(message) === "string" || message instanceof ArrayBuffer)) throw "Invalid type!"; - - let buffer = message instanceof ArrayBuffer ? message : encode_text(message as string); - - if(!crypto || !crypto.subtle || !crypto.subtle.digest || /Edge/.test(navigator.userAgent)) - return new Promise(resolve => { - resolve(_sha1.arrayBuffer(buffer as ArrayBuffer)); - }); - else - return crypto.subtle.digest("SHA-1", buffer); + let utf8 = unescape(encodeURIComponent(buffer)); + let result = new Uint8Array(utf8.length); + for (let i = 0; i < utf8.length; i++) { + result[i] = utf8.charCodeAt(i); } + return result.buffer; +} -} \ No newline at end of file +export function sha1(message: string | ArrayBuffer) : PromiseLike { + if(!(typeof(message) === "string" || message instanceof ArrayBuffer)) throw "Invalid type!"; + + let buffer = message instanceof ArrayBuffer ? message : encode_text(message as string); + + if(!crypto || !crypto.subtle || !crypto.subtle.digest || /Edge/.test(navigator.userAgent)) + return new Promise(resolve => { + resolve(_sha1.arrayBuffer(buffer as ArrayBuffer)); + }); + else + return crypto.subtle.digest("SHA-1", buffer); +} diff --git a/shared/js/crypto/uid.ts b/shared/js/crypto/uid.ts new file mode 100644 index 00000000..c97ab5f0 --- /dev/null +++ b/shared/js/crypto/uid.ts @@ -0,0 +1,10 @@ +function s4() { + return Math + .floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); +} + +export function guid() { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} \ No newline at end of file diff --git a/shared/js/dns.ts b/shared/js/dns.ts index 866b0daf..146ebcdf 100644 --- a/shared/js/dns.ts +++ b/shared/js/dns.ts @@ -1,24 +1,22 @@ -namespace dns { - export interface AddressTarget { - target_ip: string; - target_port?: number; - } +export interface AddressTarget { + target_ip: string; + target_port?: number; +} - export interface ResolveOptions { - timeout?: number; - allow_cache?: boolean; - max_depth?: number; +export interface ResolveOptions { + timeout?: number; + allow_cache?: boolean; + max_depth?: number; - allow_srv?: boolean; - allow_cname?: boolean; - allow_any?: boolean; - allow_a?: boolean; - allow_aaaa?: boolean; - } + allow_srv?: boolean; + allow_cname?: boolean; + allow_any?: boolean; + allow_a?: boolean; + allow_aaaa?: boolean; +} - export const default_options: ResolveOptions = { - timeout: 5000, - allow_cache: true, - max_depth: 5 - }; -} \ No newline at end of file +export const default_options: ResolveOptions = { + timeout: 5000, + allow_cache: true, + max_depth: 5 +}; \ No newline at end of file diff --git a/shared/js/events.ts b/shared/js/events.ts index 348621a8..d57c9c06 100644 --- a/shared/js/events.ts +++ b/shared/js/events.ts @@ -1,604 +1,605 @@ -namespace events { - export interface EventConvert { - as() : All[T]; +//TODO: Combine EventConvert and Event? +import {MusicClientEntry, SongInfo} from "tc-shared/ui/client"; +import {PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration"; +import {guid} from "tc-shared/crypto/uid"; + +export interface EventConvert { + as() : All[T]; +} + +export interface Event { + readonly type: T; +} + +export class SingletonEvent implements Event<"singletone-instance"> { + static readonly instance = new SingletonEvent(); + + readonly type = "singletone-instance"; + private constructor() { } +} + +export class Registry { + private readonly registry_uuid; + + private handler: {[key: string]: ((event) => void)[]} = {}; + private connections: {[key: string]:Registry[]} = {}; + private debug_prefix = undefined; + + constructor() { + this.registry_uuid = "evreg_data_" + guid(); } - export interface Event { - readonly type: T; + + enable_debug(prefix: string) { this.debug_prefix = prefix || "---"; } + disable_debug() { this.debug_prefix = undefined; } + + on(event: T, handler: (event?: Events[T] & Event & EventConvert) => void); + on(events: (keyof Events)[], handler: (event?: Event & EventConvert) => void); + on(events, handler) { + if(!Array.isArray(events)) + events = [events]; + + handler[this.registry_uuid] = { + singleshot: false + }; + for(const event of events) { + const handlers = this.handler[event] || (this.handler[event] = []); + handlers.push(handler); + } } - export class SingletonEvent implements Event<"singletone-instance"> { - static readonly instance = new SingletonEvent(); + /* one */ + one(event: T, handler: (event?: Events[T] & Event & EventConvert) => void); + one(events: (keyof Events)[], handler: (event?: Event & EventConvert) => void); + one(events, handler) { + if(!Array.isArray(events)) + events = [events]; - readonly type = "singletone-instance"; - private constructor() { } + for(const event of events) { + const handlers = this.handler[event] || (this.handler[event] = []); + + handler[this.registry_uuid] = { singleshot: true }; + handlers.push(handler); + } } - export class Registry { - private readonly registry_uuid; + off(handler: (event?: Event) => void); + off(event: T, handler: (event?: Event & EventConvert) => void); + off(event: (keyof Events)[], handler: (event?: Event & EventConvert) => void); + off(handler_or_events, handler?) { + if(typeof handler_or_events === "function") { + for(const key of Object.keys(this.handler)) + this.handler[key].remove(handler_or_events); + } else { + if(!Array.isArray(handler_or_events)) + handler_or_events = [handler_or_events]; - private handler: {[key: string]: ((event) => void)[]} = {}; - private connections: {[key: string]:Registry[]} = {}; - private debug_prefix = undefined; + for(const event of handler_or_events) { + const handlers = this.handler[event]; + if(handlers) handlers.remove(handler); + } + } + } - constructor() { - this.registry_uuid = "evreg_data_" + guid(); + connect(event: T, target: Registry) { + (this.connections[event as string] || (this.connections[event as string] = [])).push(target as any); + } + + disconnect(event: T, target: Registry) { + (this.connections[event as string] || []).remove(target as any); + } + + disconnect_all(target: Registry) { + for(const event of Object.keys(this.connections)) + this.connections[event].remove(target as any); + } + + fire(event_type: T, data?: Events[T]) { + if(this.debug_prefix) console.log("[%s] Trigger event: %s", this.debug_prefix, event_type); + + const event = Object.assign(typeof data === "undefined" ? SingletonEvent.instance : data, { + type: event_type, + as: function () { return this; } + }); + + for(const handler of (this.handler[event_type as string] || [])) { + handler(event); + + const reg_data = handler[this.registry_uuid]; + if(typeof reg_data === "object" && reg_data.singleshot) + this.handler[event_type as string].remove(handler); } + for(const evhandler of (this.connections[event_type as string] || [])) + evhandler.fire(event_type as any, event as any); + } - enable_debug(prefix: string) { this.debug_prefix = prefix || "---"; } - disable_debug() { this.debug_prefix = undefined; } + fire_async(event_type: T, data?: Events[T]) { + setTimeout(() => this.fire(event_type, data)); + } - on(event: T, handler: (event?: Events[T] & Event & EventConvert) => void); - on(events: (keyof Events)[], handler: (event?: Event & EventConvert) => void); - on(events, handler) { - if(!Array.isArray(events)) - events = [events]; + destory() { + this.handler = {}; + } +} - handler[this.registry_uuid] = { - singleshot: false - }; - for(const event of events) { - const handlers = this.handler[event] || (this.handler[event] = []); - handlers.push(handler); +export namespace channel_tree { + export interface client { + "enter_view": {}, + "left_view": {}, + + "property_update": { + properties: string[] + }, + + "music_status_update": { + player_buffered_index: number, + player_replay_index: number + }, + "music_song_change": { + "song": SongInfo + }, + + /* TODO: Move this out of the music bots interface? */ + "playlist_song_add": { song: PlaylistSong }, + "playlist_song_remove": { song_id: number }, + "playlist_song_reorder": { song_id: number, previous_song_id: number }, + "playlist_song_loaded": { song_id: number, success: boolean, error_msg?: string, metadata?: string }, + } +} + +export namespace sidebar { + export interface music { + "open": {}, /* triggers when frame should be shown */ + "close": {}, /* triggers when frame will be closed */ + + "bot_change": { + old: MusicClientEntry | undefined, + new: MusicClientEntry | undefined + }, + "bot_property_update": { + properties: string[] + }, + + "action_play": {}, + "action_pause": {}, + "action_song_set": { song_id: number }, + "action_forward": {}, + "action_rewind": {}, + "action_forward_ms": { + units: number; + }, + "action_rewind_ms": { + units: number; + }, + "action_song_add": {}, + "action_song_delete": { song_id: number }, + "action_playlist_reload": {}, + + "playtime_move_begin": {}, + "playtime_move_end": { + canceled: boolean, + target_time?: number + }, + + "reorder_begin": { song_id: number; entry: JQuery }, + "reorder_end": { song_id: number; canceled: boolean; entry: JQuery; previous_entry?: number }, + + "player_time_update": channel_tree.client["music_status_update"], + "player_song_change": channel_tree.client["music_song_change"], + + "playlist_song_add": channel_tree.client["playlist_song_add"] & { insert_effect?: boolean }, + "playlist_song_remove": channel_tree.client["playlist_song_remove"], + "playlist_song_reorder": channel_tree.client["playlist_song_reorder"], + "playlist_song_loaded": channel_tree.client["playlist_song_loaded"] & { html_entry?: JQuery }, + } +} + +export namespace modal { + export type BotStatusType = "name" | "description" | "volume" | "country_code" | "channel_commander" | "priority_speaker"; + export type PlaylistStatusType = "replay_mode" | "finished" | "delete_played" | "max_size" | "notify_song_change"; + export interface music_manage { + show_container: { container: "settings" | "permissions"; }; + + /* setting relevant */ + query_bot_status: {}, + bot_status: { + status: "success" | "error"; + error_msg?: string; + data?: { + name: string, + description: string, + volume: number, + + country_code: string, + default_country_code: string, + + channel_commander: boolean, + priority_speaker: boolean, + + client_version: string, + client_platform: string, + + uptime_mode: number, + bot_type: number + } + }, + set_bot_status: { + key: BotStatusType, + value: any + }, + set_bot_status_result: { + key: BotStatusType, + status: "success" | "error" | "timeout", + error_msg?: string, + value?: any + } + + query_playlist_status: {}, + playlist_status: { + status: "success" | "error", + error_msg?: string, + data?: { + replay_mode: number, + finished: boolean, + delete_played: boolean, + max_size: number, + notify_song_change: boolean + } + }, + set_playlist_status: { + key: PlaylistStatusType, + value: any + }, + set_playlist_status_result: { + key: PlaylistStatusType, + status: "success" | "error" | "timeout", + error_msg?: string, + value?: any + } + + /* permission relevant */ + show_client_list: {}, + hide_client_list: {}, + + filter_client_list: { filter: string | undefined }, + + "refresh_permissions": {}, + + query_special_clients: {}, + special_client_list: { + status: "success" | "error" | "error-permission", + error_msg?: string, + clients?: { + name: string, + unique_id: string, + database_id: number + }[] + }, + + search_client: { text: string }, + search_client_result: { + status: "error" | "timeout" | "empty" | "success", + error_msg?: string, + client?: { + name: string, + unique_id: string, + database_id: number + } + }, + + /* sets a client to set the permission for */ + special_client_set: { + client?: { + name: string, + unique_id: string, + database_id: number + } + }, + + "query_general_permissions": {}, + "general_permissions": { + status: "error" | "timeout" | "success", + error_msg?: string, + permissions?: {[key: string]:number} + }, + "set_general_permission_result": { + status: "error" | "success", + key: string, + value?: number, + error_msg?: string + }, + "set_general_permission": { /* try to change a permission for the server */ + key: string, + value: number + }, + + + "query_client_permissions": { client_database_id: number }, + "client_permissions": { + status: "error" | "timeout" | "success", + client_database_id: number, + error_msg?: string, + permissions?: {[key: string]:number} + }, + "set_client_permission_result": { + status: "error" | "success", + client_database_id: number, + key: string, + value?: number, + error_msg?: string + }, + "set_client_permission": { /* try to change a permission for the server */ + client_database_id: number, + key: string, + value: number + }, + + "query_group_permissions": { permission_name: string }, + "group_permissions": { + permission_name: string; + status: "error" | "timeout" | "success" + groups?: { + name: string, + value: number, + id: number + }[], + error_msg?: string + } + } + + export interface newcomer { + "show_step": { + "step": "welcome" | "microphone" | "identity" | "finish" + }, + "exit_guide": { + ask_yesno: boolean + }, + + "modal-shown": {}, + + + "step-status": { + next_button: boolean, + previous_button: boolean + } + } + + export namespace settings { + export type ProfileInfo = { + id: string, + name: string, + nickname: string, + identity_type: "teaforo" | "teamspeak" | "nickname", + + identity_forum?: { + valid: boolean, + fallback_name: string + }, + identity_nickname?: { + name: string, + fallback_name: string + }, + identity_teamspeak?: { + unique_id: string, + fallback_name: string } } - /* one */ - one(event: T, handler: (event?: Events[T] & Event & EventConvert) => void); - one(events: (keyof Events)[], handler: (event?: Event & EventConvert) => void); - one(events, handler) { - if(!Array.isArray(events)) - events = [events]; + export interface profiles { + "reload-profile": { profile_id?: string }, + "select-profile": { profile_id: string }, - for(const event of events) { - const handlers = this.handler[event] || (this.handler[event] = []); + "query-profile-list": { }, + "query-profile-list-result": { + status: "error" | "success" | "timeout", - handler[this.registry_uuid] = { singleshot: true }; - handlers.push(handler); - } - } - - off(handler: (event?: Event) => void); - off(event: T, handler: (event?: Event & EventConvert) => void); - off(event: (keyof Events)[], handler: (event?: Event & EventConvert) => void); - off(handler_or_events, handler?) { - if(typeof handler_or_events === "function") { - for(const key of Object.keys(this.handler)) - this.handler[key].remove(handler_or_events); - } else { - if(!Array.isArray(handler_or_events)) - handler_or_events = [handler_or_events]; - - for(const event of handler_or_events) { - const handlers = this.handler[event]; - if(handlers) handlers.remove(handler); - } - } - } - - connect(event: T, target: Registry) { - (this.connections[event as string] || (this.connections[event as string] = [])).push(target as any); - } - - disconnect(event: T, target: Registry) { - (this.connections[event as string] || []).remove(target as any); - } - - disconnect_all(target: Registry) { - for(const event of Object.keys(this.connections)) - this.connections[event].remove(target as any); - } - - fire(event_type: T, data?: Events[T]) { - if(this.debug_prefix) console.log("[%s] Trigger event: %s", this.debug_prefix, event_type); - - const event = Object.assign(typeof data === "undefined" ? SingletonEvent.instance : data, { - type: event_type, - as: function () { return this; } - }); - - for(const handler of (this.handler[event_type as string] || [])) { - handler(event); - - const reg_data = handler[this.registry_uuid]; - if(typeof reg_data === "object" && reg_data.singleshot) - this.handler[event_type as string].remove(handler); + error?: string; + profiles?: ProfileInfo[] } - for(const evhandler of (this.connections[event_type as string] || [])) - evhandler.fire(event_type as any, event as any); + "query-profile": { profile_id: string }, + "query-profile-result": { + status: "error" | "success" | "timeout", + profile_id: string, + + error?: string; + info?: ProfileInfo + }, + + "select-identity-type": { + profile_id: string, + identity_type: "teamspeak" | "teaforo" | "nickname" | "unset" + }, + + "query-profile-validity": { profile_id: string }, + "query-profile-validity-result": { + profile_id: string, + status: "error" | "success" | "timeout", + + error?: string, + valid?: boolean + } + + "create-profile": { name: string }, + "create-profile-result": { + status: "error" | "success" | "timeout", + name: string; + + profile_id?: string; + error?: string; + }, + + "delete-profile": { profile_id: string }, + "delete-profile-result": { + status: "error" | "success" | "timeout", + profile_id: string, + error?: string + } + + "set-default-profile": { profile_id: string }, + "set-default-profile-result": { + status: "error" | "success" | "timeout", + + /* the profile which now has the id "default" */ + old_profile_id: string, + + /* the "default" profile which now has a new id */ + new_profile_id?: string + + error?: string; + } + + /* profile name events */ + "set-profile-name": { + profile_id: string, + name: string + }, + "set-profile-name-result": { + status: "error" | "success" | "timeout", + profile_id: string, + name?: string + }, + + /* profile nickname events */ + "set-default-name": { + profile_id: string, + name: string | null + }, + "set-default-name-result": { + status: "error" | "success" | "timeout", + profile_id: string, + name?: string | null + }, + + "query-identity-teamspeak": { profile_id: string }, + "query-identity-teamspeak-result": { + status: "error" | "success" | "timeout", + profile_id: string, + + error?: string, + level?: number + } + + "set-identity-name-name": { profile_id: string, name: string }, + "set-identity-name-name-result": { + status: "error" | "success" | "timeout", + profile_id: string, + + error?: string, + name?: string + }, + + "generate-identity-teamspeak": { profile_id: string }, + "generate-identity-teamspeak-result": { + profile_id: string, + status: "error" | "success" | "timeout", + + error?: string, + + level?: number + unique_id?: string + }, + + "improve-identity-teamspeak-level": { profile_id: string }, + "improve-identity-teamspeak-level-update": { + profile_id: string, + new_level: number + }, + + "import-identity-teamspeak": { profile_id: string }, + "import-identity-teamspeak-result": { + profile_id: string, + + level?: number + unique_id?: string + } + + "export-identity-teamspeak": { + profile_id: string, + filename: string + }, + + + "setup-forum-connection": {} } - fire_async(event_type: T, data?: Events[T]) { - setTimeout(() => this.fire(event_type, data)); - } + export type MicrophoneSettings = "volume" | "vad-type" | "ppt-key" | "ppt-release-delay" | "ppt-release-delay-active" | "threshold-threshold"; + export interface microphone { + "query-devices": { refresh_list: boolean }, + "query-device-result": { + status: "success" | "error" | "timeout", - destory() { - this.handler = {}; - } - } - - namespace global { - - } - - export namespace channel_tree { - export interface client { - "enter_view": {}, - "left_view": {}, - - "property_update": { - properties: string[] - }, - - "music_status_update": { - player_buffered_index: number, - player_replay_index: number - }, - "music_song_change": { - "song": SongInfo - }, - - /* TODO: Move this out of the music bots interface? */ - "playlist_song_add": { song: PlaylistSong }, - "playlist_song_remove": { song_id: number }, - "playlist_song_reorder": { song_id: number, previous_song_id: number }, - "playlist_song_loaded": { song_id: number, success: boolean, error_msg?: string, metadata?: string }, - } - } - - export namespace sidebar { - export interface music { - "open": {}, /* triggers when frame should be shown */ - "close": {}, /* triggers when frame will be closed */ - - "bot_change": { - old: MusicClientEntry | undefined, - new: MusicClientEntry | undefined - }, - "bot_property_update": { - properties: string[] - }, - - "action_play": {}, - "action_pause": {}, - "action_song_set": { song_id: number }, - "action_forward": {}, - "action_rewind": {}, - "action_forward_ms": { - units: number; - }, - "action_rewind_ms": { - units: number; - }, - "action_song_add": {}, - "action_song_delete": { song_id: number }, - "action_playlist_reload": {}, - - "playtime_move_begin": {}, - "playtime_move_end": { - canceled: boolean, - target_time?: number - }, - - "reorder_begin": { song_id: number; entry: JQuery }, - "reorder_end": { song_id: number; canceled: boolean; entry: JQuery; previous_entry?: number }, - - "player_time_update": channel_tree.client["music_status_update"], - "player_song_change": channel_tree.client["music_song_change"], - - "playlist_song_add": channel_tree.client["playlist_song_add"] & { insert_effect?: boolean }, - "playlist_song_remove": channel_tree.client["playlist_song_remove"], - "playlist_song_reorder": channel_tree.client["playlist_song_reorder"], - "playlist_song_loaded": channel_tree.client["playlist_song_loaded"] & { html_entry?: JQuery }, - } - } - - export namespace modal { - export type BotStatusType = "name" | "description" | "volume" | "country_code" | "channel_commander" | "priority_speaker"; - export type PlaylistStatusType = "replay_mode" | "finished" | "delete_played" | "max_size" | "notify_song_change"; - export interface music_manage { - show_container: { container: "settings" | "permissions"; }; - - /* setting relevant */ - query_bot_status: {}, - bot_status: { - status: "success" | "error"; - error_msg?: string; - data?: { + error?: string, + devices?: { + id: string, name: string, - description: string, + driver: string + }[] + active_device?: string; + }, + + "query-settings": {}, + "query-settings-result": { + status: "success" | "error" | "timeout", + + error?: string, + info?: { volume: number, + vad_type: string, - country_code: string, - default_country_code: string, - - channel_commander: boolean, - priority_speaker: boolean, - - client_version: string, - client_platform: string, - - uptime_mode: number, - bot_type: number + vad_ppt: { + key: any, /* ppt.KeyDescriptor */ + release_delay: number, + release_delay_active: boolean + }, + vad_threshold: { + threshold: number + } } }, - set_bot_status: { - key: BotStatusType, - value: any - }, - set_bot_status_result: { - key: BotStatusType, + + "set-device": { device_id: string }, + "set-device-result": { + device_id: string, status: "success" | "error" | "timeout", - error_msg?: string, - value?: any - } - query_playlist_status: {}, - playlist_status: { - status: "success" | "error", - error_msg?: string, - data?: { - replay_mode: number, - finished: boolean, - delete_played: boolean, - max_size: number, - notify_song_change: boolean - } + error?: string }, - set_playlist_status: { - key: PlaylistStatusType, - value: any + + "set-setting": { + setting: MicrophoneSettings; + value: any; }, - set_playlist_status_result: { - key: PlaylistStatusType, + "set-setting-result": { + setting: MicrophoneSettings, status: "success" | "error" | "timeout", - error_msg?: string, + + error?: string, value?: any - } + }, - /* permission relevant */ - show_client_list: {}, - hide_client_list: {}, + "update-device-level": { + devices: { + device_id: string, + status: "success" | "error", - filter_client_list: { filter: string | undefined }, - - "refresh_permissions": {}, - - query_special_clients: {}, - special_client_list: { - status: "success" | "error" | "error-permission", - error_msg?: string, - clients?: { - name: string, - unique_id: string, - database_id: number + level?: number, + error?: string }[] }, - search_client: { text: string }, - search_client_result: { - status: "error" | "timeout" | "empty" | "success", - error_msg?: string, - client?: { - name: string, - unique_id: string, - database_id: number - } - }, - - /* sets a client to set the permission for */ - special_client_set: { - client?: { - name: string, - unique_id: string, - database_id: number - } - }, - - "query_general_permissions": {}, - "general_permissions": { - status: "error" | "timeout" | "success", - error_msg?: string, - permissions?: {[key: string]:number} - }, - "set_general_permission_result": { - status: "error" | "success", - key: string, - value?: number, - error_msg?: string - }, - "set_general_permission": { /* try to change a permission for the server */ - key: string, - value: number - }, - - - "query_client_permissions": { client_database_id: number }, - "client_permissions": { - status: "error" | "timeout" | "success", - client_database_id: number, - error_msg?: string, - permissions?: {[key: string]:number} - }, - "set_client_permission_result": { - status: "error" | "success", - client_database_id: number, - key: string, - value?: number, - error_msg?: string - }, - "set_client_permission": { /* try to change a permission for the server */ - client_database_id: number, - key: string, - value: number - }, - - "query_group_permissions": { permission_name: string }, - "group_permissions": { - permission_name: string; - status: "error" | "timeout" | "success" - groups?: { - name: string, - value: number, - id: number - }[], - error_msg?: string - } - } - - export interface newcomer { - "show_step": { - "step": "welcome" | "microphone" | "identity" | "finish" - }, - "exit_guide": { - ask_yesno: boolean - }, - - "modal-shown": {}, - - - "step-status": { - next_button: boolean, - previous_button: boolean - } - } - - export namespace settings { - export type ProfileInfo = { - id: string, - name: string, - nickname: string, - identity_type: "teaforo" | "teamspeak" | "nickname", - - identity_forum?: { - valid: boolean, - fallback_name: string - }, - identity_nickname?: { - name: string, - fallback_name: string - }, - identity_teamspeak?: { - unique_id: string, - fallback_name: string - } - } - - export interface profiles { - "reload-profile": { profile_id?: string }, - "select-profile": { profile_id: string }, - - "query-profile-list": { }, - "query-profile-list-result": { - status: "error" | "success" | "timeout", - - error?: string; - profiles?: ProfileInfo[] - } - - "query-profile": { profile_id: string }, - "query-profile-result": { - status: "error" | "success" | "timeout", - profile_id: string, - - error?: string; - info?: ProfileInfo - }, - - "select-identity-type": { - profile_id: string, - identity_type: "teamspeak" | "teaforo" | "nickname" | "unset" - }, - - "query-profile-validity": { profile_id: string }, - "query-profile-validity-result": { - profile_id: string, - status: "error" | "success" | "timeout", - - error?: string, - valid?: boolean - } - - "create-profile": { name: string }, - "create-profile-result": { - status: "error" | "success" | "timeout", - name: string; - - profile_id?: string; - error?: string; - }, - - "delete-profile": { profile_id: string }, - "delete-profile-result": { - status: "error" | "success" | "timeout", - profile_id: string, - error?: string - } - - "set-default-profile": { profile_id: string }, - "set-default-profile-result": { - status: "error" | "success" | "timeout", - - /* the profile which now has the id "default" */ - old_profile_id: string, - - /* the "default" profile which now has a new id */ - new_profile_id?: string - - error?: string; - } - - /* profile name events */ - "set-profile-name": { - profile_id: string, - name: string - }, - "set-profile-name-result": { - status: "error" | "success" | "timeout", - profile_id: string, - name?: string - }, - - /* profile nickname events */ - "set-default-name": { - profile_id: string, - name: string | null - }, - "set-default-name-result": { - status: "error" | "success" | "timeout", - profile_id: string, - name?: string | null - }, - - "query-identity-teamspeak": { profile_id: string }, - "query-identity-teamspeak-result": { - status: "error" | "success" | "timeout", - profile_id: string, - - error?: string, - level?: number - } - - "set-identity-name-name": { profile_id: string, name: string }, - "set-identity-name-name-result": { - status: "error" | "success" | "timeout", - profile_id: string, - - error?: string, - name?: string - }, - - "generate-identity-teamspeak": { profile_id: string }, - "generate-identity-teamspeak-result": { - profile_id: string, - status: "error" | "success" | "timeout", - - error?: string, - - level?: number - unique_id?: string - }, - - "improve-identity-teamspeak-level": { profile_id: string }, - "improve-identity-teamspeak-level-update": { - profile_id: string, - new_level: number - }, - - "import-identity-teamspeak": { profile_id: string }, - "import-identity-teamspeak-result": { - profile_id: string, - - level?: number - unique_id?: string - } - - "export-identity-teamspeak": { - profile_id: string, - filename: string - }, - - - "setup-forum-connection": {} - } - - export type MicrophoneSettings = "volume" | "vad-type" | "ppt-key" | "ppt-release-delay" | "ppt-release-delay-active" | "threshold-threshold"; - export interface microphone { - "query-devices": { refresh_list: boolean }, - "query-device-result": { - status: "success" | "error" | "timeout", - - error?: string, - devices?: { - id: string, - name: string, - driver: string - }[] - active_device?: string; - }, - - "query-settings": {}, - "query-settings-result": { - status: "success" | "error" | "timeout", - - error?: string, - info?: { - volume: number, - vad_type: string, - - vad_ppt: { - key: ppt.KeyDescriptor, - release_delay: number, - release_delay_active: boolean - }, - vad_threshold: { - threshold: number - } - } - }, - - "set-device": { device_id: string }, - "set-device-result": { - device_id: string, - status: "success" | "error" | "timeout", - - error?: string - }, - - "set-setting": { - setting: MicrophoneSettings; - value: any; - }, - "set-setting-result": { - setting: MicrophoneSettings, - status: "success" | "error" | "timeout", - - error?: string, - value?: any - }, - - "update-device-level": { - devices: { - device_id: string, - status: "success" | "error", - - level?: number, - error?: string - }[] - }, - - "audio-initialized": {}, - "deinitialize": {} - } + "audio-initialized": {}, + "deinitialize": {} } } - } +/* +//Some test code const eclient = new events.Registry(); const emusic = new events.Registry(); eclient.connect("playlist_song_loaded", emusic); eclient.connect("playlist_song_loaded", emusic); +*/ \ No newline at end of file diff --git a/shared/js/i18n/country.ts b/shared/js/i18n/country.ts index ce152e30..5fe3ce89 100644 --- a/shared/js/i18n/country.ts +++ b/shared/js/i18n/country.ts @@ -1,1504 +1,1501 @@ - -namespace i18n { - interface CountryInfo { - name: string; - alpha_2: string; - alpha_3: string; - un_code: number; - } - const country_infos: CountryInfo[] = []; - const alpha_2_map: {[name: string]:CountryInfo} = {}; - - const fill_country_infos = (array: CountryInfo[]) => { - array.push({ - name: "Afghanistan", - alpha_2: "AF", - alpha_3: "AFG", - un_code: 4 - }); - array.push({ - name: "Aland Islands", - alpha_2: "AX", - alpha_3: "ALA", - un_code: 248 - }); - array.push({ - name: "Albania", - alpha_2: "AL", - alpha_3: "ALB", - un_code: 8 - }); - array.push({ - name: "Algeria", - alpha_2: "DZ", - alpha_3: "DZA", - un_code: 12 - }); - array.push({ - name: "American Samoa", - alpha_2: "AS", - alpha_3: "ASM", - un_code: 16 - }); - array.push({ - name: "Andorra", - alpha_2: "AD", - alpha_3: "AND", - un_code: 20 - }); - array.push({ - name: "Angola", - alpha_2: "AO", - alpha_3: "AGO", - un_code: 24 - }); - array.push({ - name: "Anguilla", - alpha_2: "AI", - alpha_3: "AIA", - un_code: 660 - }); - array.push({ - name: "Antarctica", - alpha_2: "AQ", - alpha_3: "ATA", - un_code: 10 - }); - array.push({ - name: "Antigua and Barbuda", - alpha_2: "AG", - alpha_3: "ATG", - un_code: 28 - }); - array.push({ - name: "Argentina", - alpha_2: "AR", - alpha_3: "ARG", - un_code: 32 - }); - array.push({ - name: "Armenia", - alpha_2: "AM", - alpha_3: "ARM", - un_code: 51 - }); - array.push({ - name: "Aruba", - alpha_2: "AW", - alpha_3: "ABW", - un_code: 533 - }); - array.push({ - name: "Australia", - alpha_2: "AU", - alpha_3: "AUS", - un_code: 36 - }); - array.push({ - name: "Austria", - alpha_2: "AT", - alpha_3: "AUT", - un_code: 40 - }); - array.push({ - name: "Azerbaijan", - alpha_2: "AZ", - alpha_3: "AZE", - un_code: 31 - }); - array.push({ - name: "Bahamas", - alpha_2: "BS", - alpha_3: "BHS", - un_code: 44 - }); - array.push({ - name: "Bahrain", - alpha_2: "BH", - alpha_3: "BHR", - un_code: 48 - }); - array.push({ - name: "Bangladesh", - alpha_2: "BD", - alpha_3: "BGD", - un_code: 50 - }); - array.push({ - name: "Barbados", - alpha_2: "BB", - alpha_3: "BRB", - un_code: 52 - }); - array.push({ - name: "Belarus", - alpha_2: "BY", - alpha_3: "BLR", - un_code: 112 - }); - array.push({ - name: "Belgium", - alpha_2: "BE", - alpha_3: "BEL", - un_code: 56 - }); - array.push({ - name: "Belize", - alpha_2: "BZ", - alpha_3: "BLZ", - un_code: 84 - }); - array.push({ - name: "Benin", - alpha_2: "BJ", - alpha_3: "BEN", - un_code: 204 - }); - array.push({ - name: "Bermuda", - alpha_2: "BM", - alpha_3: "BMU", - un_code: 60 - }); - array.push({ - name: "Bhutan", - alpha_2: "BT", - alpha_3: "BTN", - un_code: 64 - }); - array.push({ - name: "Bolivia", - alpha_2: "BO", - alpha_3: "BOL", - un_code: 68 - }); - array.push({ - name: "Bosnia and Herzegovina", - alpha_2: "BA", - alpha_3: "BIH", - un_code: 70 - }); - array.push({ - name: "Botswana", - alpha_2: "BW", - alpha_3: "BWA", - un_code: 72 - }); - array.push({ - name: "Bouvet Island", - alpha_2: "BV", - alpha_3: "BVT", - un_code: 74 - }); - array.push({ - name: "Brazil", - alpha_2: "BR", - alpha_3: "BRA", - un_code: 76 - }); - array.push({ - name: "British Virgin Islands", - alpha_2: "VG", - alpha_3: "VGB", - un_code: 92 - }); - array.push({ - name: "British Indian Ocean Territory", - alpha_2: "IO", - alpha_3: "IOT", - un_code: 86 - }); - array.push({ - name: "Brunei Darussalam", - alpha_2: "BN", - alpha_3: "BRN", - un_code: 96 - }); - array.push({ - name: "Bulgaria", - alpha_2: "BG", - alpha_3: "BGR", - un_code: 100 - }); - array.push({ - name: "Burkina Faso", - alpha_2: "BF", - alpha_3: "BFA", - un_code: 854 - }); - array.push({ - name: "Burundi", - alpha_2: "BI", - alpha_3: "BDI", - un_code: 108 - }); - array.push({ - name: "Cambodia", - alpha_2: "KH", - alpha_3: "KHM", - un_code: 116 - }); - array.push({ - name: "Cameroon", - alpha_2: "CM", - alpha_3: "CMR", - un_code: 120 - }); - array.push({ - name: "Canada", - alpha_2: "CA", - alpha_3: "CAN", - un_code: 124 - }); - array.push({ - name: "Cape Verde", - alpha_2: "CV", - alpha_3: "CPV", - un_code: 132 - }); - array.push({ - name: "Cayman Islands", - alpha_2: "KY", - alpha_3: "CYM", - un_code: 136 - }); - array.push({ - name: "Central African Republic", - alpha_2: "CF", - alpha_3: "CAF", - un_code: 140 - }); - array.push({ - name: "Chad", - alpha_2: "TD", - alpha_3: "TCD", - un_code: 148 - }); - array.push({ - name: "Chile", - alpha_2: "CL", - alpha_3: "CHL", - un_code: 152 - }); - array.push({ - name: "China", - alpha_2: "CN", - alpha_3: "CHN", - un_code: 156 - }); - array.push({ - name: "Hong Kong, SAR China", - alpha_2: "HK", - alpha_3: "HKG", - un_code: 344 - }); - array.push({ - name: "Macao, SAR China", - alpha_2: "MO", - alpha_3: "MAC", - un_code: 446 - }); - array.push({ - name: "Christmas Island", - alpha_2: "CX", - alpha_3: "CXR", - un_code: 162 - }); - array.push({ - name: "Cocos (Keeling) Islands", - alpha_2: "CC", - alpha_3: "CCK", - un_code: 166 - }); - array.push({ - name: "Colombia", - alpha_2: "CO", - alpha_3: "COL", - un_code: 170 - }); - array.push({ - name: "Comoros", - alpha_2: "KM", - alpha_3: "COM", - un_code: 174 - }); - array.push({ - name: "Congo (Brazzaville)", - alpha_2: "CG", - alpha_3: "COG", - un_code: 178 - }); - array.push({ - name: "Congo, (Kinshasa)", - alpha_2: "CD", - alpha_3: "COD", - un_code: 180 - }); - array.push({ - name: "Cook Islands", - alpha_2: "CK", - alpha_3: "COK", - un_code: 184 - }); - array.push({ - name: "Costa Rica", - alpha_2: "CR", - alpha_3: "CRI", - un_code: 188 - }); - array.push({ - name: "Côte d'Ivoire", - alpha_2: "CI", - alpha_3: "CIV", - un_code: 384 - }); - array.push({ - name: "Croatia", - alpha_2: "HR", - alpha_3: "HRV", - un_code: 191 - }); - array.push({ - name: "Cuba", - alpha_2: "CU", - alpha_3: "CUB", - un_code: 192 - }); - array.push({ - name: "Cyprus", - alpha_2: "CY", - alpha_3: "CYP", - un_code: 196 - }); - array.push({ - name: "Czech Republic", - alpha_2: "CZ", - alpha_3: "CZE", - un_code: 203 - }); - array.push({ - name: "Denmark", - alpha_2: "DK", - alpha_3: "DNK", - un_code: 208 - }); - array.push({ - name: "Djibouti", - alpha_2: "DJ", - alpha_3: "DJI", - un_code: 262 - }); - array.push({ - name: "Dominica", - alpha_2: "DM", - alpha_3: "DMA", - un_code: 212 - }); - array.push({ - name: "Dominican Republic", - alpha_2: "DO", - alpha_3: "DOM", - un_code: 214 - }); - array.push({ - name: "Ecuador", - alpha_2: "EC", - alpha_3: "ECU", - un_code: 218 - }); - array.push({ - name: "Egypt", - alpha_2: "EG", - alpha_3: "EGY", - un_code: 818 - }); - array.push({ - name: "El Salvador", - alpha_2: "SV", - alpha_3: "SLV", - un_code: 222 - }); - array.push({ - name: "Equatorial Guinea", - alpha_2: "GQ", - alpha_3: "GNQ", - un_code: 226 - }); - array.push({ - name: "Eritrea", - alpha_2: "ER", - alpha_3: "ERI", - un_code: 232 - }); - array.push({ - name: "Estonia", - alpha_2: "EE", - alpha_3: "EST", - un_code: 233 - }); - array.push({ - name: "Ethiopia", - alpha_2: "ET", - alpha_3: "ETH", - un_code: 231 - }); - array.push({ - name: "Falkland Islands (Malvinas)", - alpha_2: "FK", - alpha_3: "FLK", - un_code: 238 - }); - array.push({ - name: "Faroe Islands", - alpha_2: "FO", - alpha_3: "FRO", - un_code: 234 - }); - array.push({ - name: "Fiji", - alpha_2: "FJ", - alpha_3: "FJI", - un_code: 242 - }); - array.push({ - name: "Finland", - alpha_2: "FI", - alpha_3: "FIN", - un_code: 246 - }); - array.push({ - name: "France", - alpha_2: "FR", - alpha_3: "FRA", - un_code: 250 - }); - array.push({ - name: "French Guiana", - alpha_2: "GF", - alpha_3: "GUF", - un_code: 254 - }); - array.push({ - name: "French Polynesia", - alpha_2: "PF", - alpha_3: "PYF", - un_code: 258 - }); - array.push({ - name: "French Southern Territories", - alpha_2: "TF", - alpha_3: "ATF", - un_code: 260 - }); - array.push({ - name: "Gabon", - alpha_2: "GA", - alpha_3: "GAB", - un_code: 266 - }); - array.push({ - name: "Gambia", - alpha_2: "GM", - alpha_3: "GMB", - un_code: 270 - }); - array.push({ - name: "Georgia", - alpha_2: "GE", - alpha_3: "GEO", - un_code: 268 - }); - array.push({ - name: "Germany", - alpha_2: "DE", - alpha_3: "DEU", - un_code: 276 - }); - array.push({ - name: "Ghana", - alpha_2: "GH", - alpha_3: "GHA", - un_code: 288 - }); - array.push({ - name: "Gibraltar", - alpha_2: "GI", - alpha_3: "GIB", - un_code: 292 - }); - array.push({ - name: "Greece", - alpha_2: "GR", - alpha_3: "GRC", - un_code: 300 - }); - array.push({ - name: "Greenland", - alpha_2: "GL", - alpha_3: "GRL", - un_code: 304 - }); - array.push({ - name: "Grenada", - alpha_2: "GD", - alpha_3: "GRD", - un_code: 308 - }); - array.push({ - name: "Guadeloupe", - alpha_2: "GP", - alpha_3: "GLP", - un_code: 312 - }); - array.push({ - name: "Guam", - alpha_2: "GU", - alpha_3: "GUM", - un_code: 316 - }); - array.push({ - name: "Guatemala", - alpha_2: "GT", - alpha_3: "GTM", - un_code: 320 - }); - array.push({ - name: "Guernsey", - alpha_2: "GG", - alpha_3: "GGY", - un_code: 831 - }); - array.push({ - name: "Guinea", - alpha_2: "GN", - alpha_3: "GIN", - un_code: 324 - }); - array.push({ - name: "Guinea-Bissau", - alpha_2: "GW", - alpha_3: "GNB", - un_code: 624 - }); - array.push({ - name: "Guyana", - alpha_2: "GY", - alpha_3: "GUY", - un_code: 328 - }); - array.push({ - name: "Haiti", - alpha_2: "HT", - alpha_3: "HTI", - un_code: 332 - }); - array.push({ - name: "Heard and Mcdonald Islands", - alpha_2: "HM", - alpha_3: "HMD", - un_code: 334 - }); - array.push({ - name: "Holy See (Vatican City State)", - alpha_2: "VA", - alpha_3: "VAT", - un_code: 336 - }); - array.push({ - name: "Honduras", - alpha_2: "HN", - alpha_3: "HND", - un_code: 340 - }); - array.push({ - name: "Hungary", - alpha_2: "HU", - alpha_3: "HUN", - un_code: 348 - }); - array.push({ - name: "Iceland", - alpha_2: "IS", - alpha_3: "ISL", - un_code: 352 - }); - array.push({ - name: "India", - alpha_2: "IN", - alpha_3: "IND", - un_code: 356 - }); - array.push({ - name: "Indonesia", - alpha_2: "ID", - alpha_3: "IDN", - un_code: 360 - }); - array.push({ - name: "Iran, Islamic Republic of", - alpha_2: "IR", - alpha_3: "IRN", - un_code: 364 - }); - array.push({ - name: "Iraq", - alpha_2: "IQ", - alpha_3: "IRQ", - un_code: 368 - }); - array.push({ - name: "Ireland", - alpha_2: "IE", - alpha_3: "IRL", - un_code: 372 - }); - array.push({ - name: "Isle of Man", - alpha_2: "IM", - alpha_3: "IMN", - un_code: 833 - }); - array.push({ - name: "Israel", - alpha_2: "IL", - alpha_3: "ISR", - un_code: 376 - }); - array.push({ - name: "Italy", - alpha_2: "IT", - alpha_3: "ITA", - un_code: 380 - }); - array.push({ - name: "Jamaica", - alpha_2: "JM", - alpha_3: "JAM", - un_code: 388 - }); - array.push({ - name: "Japan", - alpha_2: "JP", - alpha_3: "JPN", - un_code: 392 - }); - array.push({ - name: "Jersey", - alpha_2: "JE", - alpha_3: "JEY", - un_code: 832 - }); - array.push({ - name: "Jordan", - alpha_2: "JO", - alpha_3: "JOR", - un_code: 400 - }); - array.push({ - name: "Kazakhstan", - alpha_2: "KZ", - alpha_3: "KAZ", - un_code: 398 - }); - array.push({ - name: "Kenya", - alpha_2: "KE", - alpha_3: "KEN", - un_code: 404 - }); - array.push({ - name: "Kiribati", - alpha_2: "KI", - alpha_3: "KIR", - un_code: 296 - }); - array.push({ - name: "Korea (North)", - alpha_2: "KP", - alpha_3: "PRK", - un_code: 408 - }); - array.push({ - name: "Korea (South)", - alpha_2: "KR", - alpha_3: "KOR", - un_code: 410 - }); - array.push({ - name: "Kuwait", - alpha_2: "KW", - alpha_3: "KWT", - un_code: 414 - }); - array.push({ - name: "Kyrgyzstan", - alpha_2: "KG", - alpha_3: "KGZ", - un_code: 417 - }); - array.push({ - name: "Lao PDR", - alpha_2: "LA", - alpha_3: "LAO", - un_code: 418 - }); - array.push({ - name: "Latvia", - alpha_2: "LV", - alpha_3: "LVA", - un_code: 428 - }); - array.push({ - name: "Lebanon", - alpha_2: "LB", - alpha_3: "LBN", - un_code: 422 - }); - array.push({ - name: "Lesotho", - alpha_2: "LS", - alpha_3: "LSO", - un_code: 426 - }); - array.push({ - name: "Liberia", - alpha_2: "LR", - alpha_3: "LBR", - un_code: 430 - }); - array.push({ - name: "Libya", - alpha_2: "LY", - alpha_3: "LBY", - un_code: 434 - }); - array.push({ - name: "Liechtenstein", - alpha_2: "LI", - alpha_3: "LIE", - un_code: 438 - }); - array.push({ - name: "Lithuania", - alpha_2: "LT", - alpha_3: "LTU", - un_code: 440 - }); - array.push({ - name: "Luxembourg", - alpha_2: "LU", - alpha_3: "LUX", - un_code: 442 - }); - array.push({ - name: "Macedonia, Republic of", - alpha_2: "MK", - alpha_3: "MKD", - un_code: 807 - }); - array.push({ - name: "Madagascar", - alpha_2: "MG", - alpha_3: "MDG", - un_code: 450 - }); - array.push({ - name: "Malawi", - alpha_2: "MW", - alpha_3: "MWI", - un_code: 454 - }); - array.push({ - name: "Malaysia", - alpha_2: "MY", - alpha_3: "MYS", - un_code: 458 - }); - array.push({ - name: "Maldives", - alpha_2: "MV", - alpha_3: "MDV", - un_code: 462 - }); - array.push({ - name: "Mali", - alpha_2: "ML", - alpha_3: "MLI", - un_code: 466 - }); - array.push({ - name: "Malta", - alpha_2: "MT", - alpha_3: "MLT", - un_code: 470 - }); - array.push({ - name: "Marshall Islands", - alpha_2: "MH", - alpha_3: "MHL", - un_code: 584 - }); - array.push({ - name: "Martinique", - alpha_2: "MQ", - alpha_3: "MTQ", - un_code: 474 - }); - array.push({ - name: "Mauritania", - alpha_2: "MR", - alpha_3: "MRT", - un_code: 478 - }); - array.push({ - name: "Mauritius", - alpha_2: "MU", - alpha_3: "MUS", - un_code: 480 - }); - array.push({ - name: "Mayotte", - alpha_2: "YT", - alpha_3: "MYT", - un_code: 175 - }); - array.push({ - name: "Mexico", - alpha_2: "MX", - alpha_3: "MEX", - un_code: 484 - }); - array.push({ - name: "Micronesia, Federated States of", - alpha_2: "FM", - alpha_3: "FSM", - un_code: 583 - }); - array.push({ - name: "Moldova", - alpha_2: "MD", - alpha_3: "MDA", - un_code: 498 - }); - array.push({ - name: "Monaco", - alpha_2: "MC", - alpha_3: "MCO", - un_code: 492 - }); - array.push({ - name: "Mongolia", - alpha_2: "MN", - alpha_3: "MNG", - un_code: 496 - }); - array.push({ - name: "Montenegro", - alpha_2: "ME", - alpha_3: "MNE", - un_code: 499 - }); - array.push({ - name: "Montserrat", - alpha_2: "MS", - alpha_3: "MSR", - un_code: 500 - }); - array.push({ - name: "Morocco", - alpha_2: "MA", - alpha_3: "MAR", - un_code: 504 - }); - array.push({ - name: "Mozambique", - alpha_2: "MZ", - alpha_3: "MOZ", - un_code: 508 - }); - array.push({ - name: "Myanmar", - alpha_2: "MM", - alpha_3: "MMR", - un_code: 104 - }); - array.push({ - name: "Namibia", - alpha_2: "NA", - alpha_3: "NAM", - un_code: 516 - }); - array.push({ - name: "Nauru", - alpha_2: "NR", - alpha_3: "NRU", - un_code: 520 - }); - array.push({ - name: "Nepal", - alpha_2: "NP", - alpha_3: "NPL", - un_code: 524 - }); - array.push({ - name: "Netherlands", - alpha_2: "NL", - alpha_3: "NLD", - un_code: 528 - }); - array.push({ - name: "Netherlands Antilles", - alpha_2: "AN", - alpha_3: "ANT", - un_code: 530 - }); - array.push({ - name: "New Caledonia", - alpha_2: "NC", - alpha_3: "NCL", - un_code: 540 - }); - array.push({ - name: "New Zealand", - alpha_2: "NZ", - alpha_3: "NZL", - un_code: 554 - }); - array.push({ - name: "Nicaragua", - alpha_2: "NI", - alpha_3: "NIC", - un_code: 558 - }); - array.push({ - name: "Niger", - alpha_2: "NE", - alpha_3: "NER", - un_code: 562 - }); - array.push({ - name: "Nigeria", - alpha_2: "NG", - alpha_3: "NGA", - un_code: 566 - }); - array.push({ - name: "Niue", - alpha_2: "NU", - alpha_3: "NIU", - un_code: 570 - }); - array.push({ - name: "Norfolk Island", - alpha_2: "NF", - alpha_3: "NFK", - un_code: 574 - }); - array.push({ - name: "Northern Mariana Islands", - alpha_2: "MP", - alpha_3: "MNP", - un_code: 580 - }); - array.push({ - name: "Norway", - alpha_2: "NO", - alpha_3: "NOR", - un_code: 578 - }); - array.push({ - name: "Oman", - alpha_2: "OM", - alpha_3: "OMN", - un_code: 512 - }); - array.push({ - name: "Pakistan", - alpha_2: "PK", - alpha_3: "PAK", - un_code: 586 - }); - array.push({ - name: "Palau", - alpha_2: "PW", - alpha_3: "PLW", - un_code: 585 - }); - array.push({ - name: "Palestinian Territory", - alpha_2: "PS", - alpha_3: "PSE", - un_code: 275 - }); - array.push({ - name: "Panama", - alpha_2: "PA", - alpha_3: "PAN", - un_code: 591 - }); - array.push({ - name: "Papua New Guinea", - alpha_2: "PG", - alpha_3: "PNG", - un_code: 598 - }); - array.push({ - name: "Paraguay", - alpha_2: "PY", - alpha_3: "PRY", - un_code: 600 - }); - array.push({ - name: "Peru", - alpha_2: "PE", - alpha_3: "PER", - un_code: 604 - }); - array.push({ - name: "Philippines", - alpha_2: "PH", - alpha_3: "PHL", - un_code: 608 - }); - array.push({ - name: "Pitcairn", - alpha_2: "PN", - alpha_3: "PCN", - un_code: 612 - }); - array.push({ - name: "Poland", - alpha_2: "PL", - alpha_3: "POL", - un_code: 616 - }); - array.push({ - name: "Portugal", - alpha_2: "PT", - alpha_3: "PRT", - un_code: 620 - }); - array.push({ - name: "Puerto Rico", - alpha_2: "PR", - alpha_3: "PRI", - un_code: 630 - }); - array.push({ - name: "Qatar", - alpha_2: "QA", - alpha_3: "QAT", - un_code: 634 - }); - array.push({ - name: "Réunion", - alpha_2: "RE", - alpha_3: "REU", - un_code: 638 - }); - array.push({ - name: "Romania", - alpha_2: "RO", - alpha_3: "ROU", - un_code: 642 - }); - array.push({ - name: "Russian Federation", - alpha_2: "RU", - alpha_3: "RUS", - un_code: 643 - }); - array.push({ - name: "Rwanda", - alpha_2: "RW", - alpha_3: "RWA", - un_code: 646 - }); - array.push({ - name: "Saint-Barthélemy", - alpha_2: "BL", - alpha_3: "BLM", - un_code: 652 - }); - array.push({ - name: "Saint Helena", - alpha_2: "SH", - alpha_3: "SHN", - un_code: 654 - }); - array.push({ - name: "Saint Kitts and Nevis", - alpha_2: "KN", - alpha_3: "KNA", - un_code: 659 - }); - array.push({ - name: "Saint Lucia", - alpha_2: "LC", - alpha_3: "LCA", - un_code: 662 - }); - array.push({ - name: "Saint-Martin (French part)", - alpha_2: "MF", - alpha_3: "MAF", - un_code: 663 - }); - array.push({ - name: "Saint Pierre and Miquelon", - alpha_2: "PM", - alpha_3: "SPM", - un_code: 666 - }); - array.push({ - name: "Saint Vincent and Grenadines", - alpha_2: "VC", - alpha_3: "VCT", - un_code: 670 - }); - array.push({ - name: "Samoa", - alpha_2: "WS", - alpha_3: "WSM", - un_code: 882 - }); - array.push({ - name: "San Marino", - alpha_2: "SM", - alpha_3: "SMR", - un_code: 674 - }); - array.push({ - name: "Sao Tome and Principe", - alpha_2: "ST", - alpha_3: "STP", - un_code: 678 - }); - array.push({ - name: "Saudi Arabia", - alpha_2: "SA", - alpha_3: "SAU", - un_code: 682 - }); - array.push({ - name: "Senegal", - alpha_2: "SN", - alpha_3: "SEN", - un_code: 686 - }); - array.push({ - name: "Serbia", - alpha_2: "RS", - alpha_3: "SRB", - un_code: 688 - }); - array.push({ - name: "Seychelles", - alpha_2: "SC", - alpha_3: "SYC", - un_code: 690 - }); - array.push({ - name: "Sierra Leone", - alpha_2: "SL", - alpha_3: "SLE", - un_code: 694 - }); - array.push({ - name: "Singapore", - alpha_2: "SG", - alpha_3: "SGP", - un_code: 702 - }); - array.push({ - name: "Slovakia", - alpha_2: "SK", - alpha_3: "SVK", - un_code: 703 - }); - array.push({ - name: "Slovenia", - alpha_2: "SI", - alpha_3: "SVN", - un_code: 705 - }); - array.push({ - name: "Solomon Islands", - alpha_2: "SB", - alpha_3: "SLB", - un_code: 90 - }); - array.push({ - name: "Somalia", - alpha_2: "SO", - alpha_3: "SOM", - un_code: 706 - }); - array.push({ - name: "South Africa", - alpha_2: "ZA", - alpha_3: "ZAF", - un_code: 710 - }); - array.push({ - name: "South Georgia and the South Sandwich Islands", - alpha_2: "GS", - alpha_3: "SGS", - un_code: 239 - }); - array.push({ - name: "South Sudan", - alpha_2: "SS", - alpha_3: "SSD", - un_code: 728 - }); - array.push({ - name: "Spain", - alpha_2: "ES", - alpha_3: "ESP", - un_code: 724 - }); - array.push({ - name: "Sri Lanka", - alpha_2: "LK", - alpha_3: "LKA", - un_code: 144 - }); - array.push({ - name: "Sudan", - alpha_2: "SD", - alpha_3: "SDN", - un_code: 736 - }); - array.push({ - name: "Suriname", - alpha_2: "SR", - alpha_3: "SUR", - un_code: 740 - }); - array.push({ - name: "Svalbard and Jan Mayen Islands", - alpha_2: "SJ", - alpha_3: "SJM", - un_code: 744 - }); - array.push({ - name: "Swaziland", - alpha_2: "SZ", - alpha_3: "SWZ", - un_code: 748 - }); - array.push({ - name: "Sweden", - alpha_2: "SE", - alpha_3: "SWE", - un_code: 752 - }); - array.push({ - name: "Switzerland", - alpha_2: "CH", - alpha_3: "CHE", - un_code: 756 - }); - array.push({ - name: "Syrian Arab Republic (Syria)", - alpha_2: "SY", - alpha_3: "SYR", - un_code: 760 - }); - array.push({ - name: "Taiwan, Republic of China", - alpha_2: "TW", - alpha_3: "TWN", - un_code: 158 - }); - array.push({ - name: "Tajikistan", - alpha_2: "TJ", - alpha_3: "TJK", - un_code: 762 - }); - array.push({ - name: "Tanzania, United Republic of", - alpha_2: "TZ", - alpha_3: "TZA", - un_code: 834 - }); - array.push({ - name: "Thailand", - alpha_2: "TH", - alpha_3: "THA", - un_code: 764 - }); - array.push({ - name: "Timor-Leste", - alpha_2: "TL", - alpha_3: "TLS", - un_code: 626 - }); - array.push({ - name: "Togo", - alpha_2: "TG", - alpha_3: "TGO", - un_code: 768 - }); - array.push({ - name: "Tokelau", - alpha_2: "TK", - alpha_3: "TKL", - un_code: 772 - }); - array.push({ - name: "Tonga", - alpha_2: "TO", - alpha_3: "TON", - un_code: 776 - }); - array.push({ - name: "Trinidad and Tobago", - alpha_2: "TT", - alpha_3: "TTO", - un_code: 780 - }); - array.push({ - name: "Tunisia", - alpha_2: "TN", - alpha_3: "TUN", - un_code: 788 - }); - array.push({ - name: "Turkey", - alpha_2: "TR", - alpha_3: "TUR", - un_code: 792 - }); - array.push({ - name: "Turkmenistan", - alpha_2: "TM", - alpha_3: "TKM", - un_code: 795 - }); - array.push({ - name: "Turks and Caicos Islands", - alpha_2: "TC", - alpha_3: "TCA", - un_code: 796 - }); - array.push({ - name: "Tuvalu", - alpha_2: "TV", - alpha_3: "TUV", - un_code: 798 - }); - array.push({ - name: "Uganda", - alpha_2: "UG", - alpha_3: "UGA", - un_code: 800 - }); - array.push({ - name: "Ukraine", - alpha_2: "UA", - alpha_3: "UKR", - un_code: 804 - }); - array.push({ - name: "United Arab Emirates", - alpha_2: "AE", - alpha_3: "ARE", - un_code: 784 - }); - array.push({ - name: "United Kingdom", - alpha_2: "GB", - alpha_3: "GBR", - un_code: 826 - }); - array.push({ - name: "United States of America", - alpha_2: "US", - alpha_3: "USA", - un_code: 840 - }); - array.push({ - name: "US Minor Outlying Islands", - alpha_2: "UM", - alpha_3: "UMI", - un_code: 581 - }); - array.push({ - name: "Uruguay", - alpha_2: "UY", - alpha_3: "URY", - un_code: 858 - }); - array.push({ - name: "Uzbekistan", - alpha_2: "UZ", - alpha_3: "UZB", - un_code: 860 - }); - array.push({ - name: "Vanuatu", - alpha_2: "VU", - alpha_3: "VUT", - un_code: 548 - }); - array.push({ - name: "Venezuela (Bolivarian Republic)", - alpha_2: "VE", - alpha_3: "VEN", - un_code: 862 - }); - array.push({ - name: "Viet Nam", - alpha_2: "VN", - alpha_3: "VNM", - un_code: 704 - }); - array.push({ - name: "Virgin Islands, US", - alpha_2: "VI", - alpha_3: "VIR", - un_code: 850 - }); - array.push({ - name: "Wallis and Futuna Islands", - alpha_2: "WF", - alpha_3: "WLF", - un_code: 876 - }); - array.push({ - name: "Western Sahara", - alpha_2: "EH", - alpha_3: "ESH", - un_code: 732 - }); - array.push({ - name: "Yemen", - alpha_2: "YE", - alpha_3: "YEM", - un_code: 887 - }); - array.push({ - name: "Zambia", - alpha_2: "ZM", - alpha_3: "ZMB", - un_code: 894 - }); - array.push({ - name: "Zimbabwe", - alpha_2: "ZW", - alpha_3: "ZWE", - un_code: 716 - }); - }; - - export function country_name(alpha_code: string, fallback?: string) { - return (alpha_2_map[alpha_code.toUpperCase()] || {name: fallback || tr("unknown country")}).name; - } - - fill_country_infos(country_infos); - for(const country of country_infos) - alpha_2_map[country.alpha_2] = country; +interface CountryInfo { + name: string; + alpha_2: string; + alpha_3: string; + un_code: number; } +const country_infos: CountryInfo[] = []; +const alpha_2_map: {[name: string]:CountryInfo} = {}; + +const fill_country_infos = (array: CountryInfo[]) => { + array.push({ + name: "Afghanistan", + alpha_2: "AF", + alpha_3: "AFG", + un_code: 4 + }); + array.push({ + name: "Aland Islands", + alpha_2: "AX", + alpha_3: "ALA", + un_code: 248 + }); + array.push({ + name: "Albania", + alpha_2: "AL", + alpha_3: "ALB", + un_code: 8 + }); + array.push({ + name: "Algeria", + alpha_2: "DZ", + alpha_3: "DZA", + un_code: 12 + }); + array.push({ + name: "American Samoa", + alpha_2: "AS", + alpha_3: "ASM", + un_code: 16 + }); + array.push({ + name: "Andorra", + alpha_2: "AD", + alpha_3: "AND", + un_code: 20 + }); + array.push({ + name: "Angola", + alpha_2: "AO", + alpha_3: "AGO", + un_code: 24 + }); + array.push({ + name: "Anguilla", + alpha_2: "AI", + alpha_3: "AIA", + un_code: 660 + }); + array.push({ + name: "Antarctica", + alpha_2: "AQ", + alpha_3: "ATA", + un_code: 10 + }); + array.push({ + name: "Antigua and Barbuda", + alpha_2: "AG", + alpha_3: "ATG", + un_code: 28 + }); + array.push({ + name: "Argentina", + alpha_2: "AR", + alpha_3: "ARG", + un_code: 32 + }); + array.push({ + name: "Armenia", + alpha_2: "AM", + alpha_3: "ARM", + un_code: 51 + }); + array.push({ + name: "Aruba", + alpha_2: "AW", + alpha_3: "ABW", + un_code: 533 + }); + array.push({ + name: "Australia", + alpha_2: "AU", + alpha_3: "AUS", + un_code: 36 + }); + array.push({ + name: "Austria", + alpha_2: "AT", + alpha_3: "AUT", + un_code: 40 + }); + array.push({ + name: "Azerbaijan", + alpha_2: "AZ", + alpha_3: "AZE", + un_code: 31 + }); + array.push({ + name: "Bahamas", + alpha_2: "BS", + alpha_3: "BHS", + un_code: 44 + }); + array.push({ + name: "Bahrain", + alpha_2: "BH", + alpha_3: "BHR", + un_code: 48 + }); + array.push({ + name: "Bangladesh", + alpha_2: "BD", + alpha_3: "BGD", + un_code: 50 + }); + array.push({ + name: "Barbados", + alpha_2: "BB", + alpha_3: "BRB", + un_code: 52 + }); + array.push({ + name: "Belarus", + alpha_2: "BY", + alpha_3: "BLR", + un_code: 112 + }); + array.push({ + name: "Belgium", + alpha_2: "BE", + alpha_3: "BEL", + un_code: 56 + }); + array.push({ + name: "Belize", + alpha_2: "BZ", + alpha_3: "BLZ", + un_code: 84 + }); + array.push({ + name: "Benin", + alpha_2: "BJ", + alpha_3: "BEN", + un_code: 204 + }); + array.push({ + name: "Bermuda", + alpha_2: "BM", + alpha_3: "BMU", + un_code: 60 + }); + array.push({ + name: "Bhutan", + alpha_2: "BT", + alpha_3: "BTN", + un_code: 64 + }); + array.push({ + name: "Bolivia", + alpha_2: "BO", + alpha_3: "BOL", + un_code: 68 + }); + array.push({ + name: "Bosnia and Herzegovina", + alpha_2: "BA", + alpha_3: "BIH", + un_code: 70 + }); + array.push({ + name: "Botswana", + alpha_2: "BW", + alpha_3: "BWA", + un_code: 72 + }); + array.push({ + name: "Bouvet Island", + alpha_2: "BV", + alpha_3: "BVT", + un_code: 74 + }); + array.push({ + name: "Brazil", + alpha_2: "BR", + alpha_3: "BRA", + un_code: 76 + }); + array.push({ + name: "British Virgin Islands", + alpha_2: "VG", + alpha_3: "VGB", + un_code: 92 + }); + array.push({ + name: "British Indian Ocean Territory", + alpha_2: "IO", + alpha_3: "IOT", + un_code: 86 + }); + array.push({ + name: "Brunei Darussalam", + alpha_2: "BN", + alpha_3: "BRN", + un_code: 96 + }); + array.push({ + name: "Bulgaria", + alpha_2: "BG", + alpha_3: "BGR", + un_code: 100 + }); + array.push({ + name: "Burkina Faso", + alpha_2: "BF", + alpha_3: "BFA", + un_code: 854 + }); + array.push({ + name: "Burundi", + alpha_2: "BI", + alpha_3: "BDI", + un_code: 108 + }); + array.push({ + name: "Cambodia", + alpha_2: "KH", + alpha_3: "KHM", + un_code: 116 + }); + array.push({ + name: "Cameroon", + alpha_2: "CM", + alpha_3: "CMR", + un_code: 120 + }); + array.push({ + name: "Canada", + alpha_2: "CA", + alpha_3: "CAN", + un_code: 124 + }); + array.push({ + name: "Cape Verde", + alpha_2: "CV", + alpha_3: "CPV", + un_code: 132 + }); + array.push({ + name: "Cayman Islands", + alpha_2: "KY", + alpha_3: "CYM", + un_code: 136 + }); + array.push({ + name: "Central African Republic", + alpha_2: "CF", + alpha_3: "CAF", + un_code: 140 + }); + array.push({ + name: "Chad", + alpha_2: "TD", + alpha_3: "TCD", + un_code: 148 + }); + array.push({ + name: "Chile", + alpha_2: "CL", + alpha_3: "CHL", + un_code: 152 + }); + array.push({ + name: "China", + alpha_2: "CN", + alpha_3: "CHN", + un_code: 156 + }); + array.push({ + name: "Hong Kong, SAR China", + alpha_2: "HK", + alpha_3: "HKG", + un_code: 344 + }); + array.push({ + name: "Macao, SAR China", + alpha_2: "MO", + alpha_3: "MAC", + un_code: 446 + }); + array.push({ + name: "Christmas Island", + alpha_2: "CX", + alpha_3: "CXR", + un_code: 162 + }); + array.push({ + name: "Cocos (Keeling) Islands", + alpha_2: "CC", + alpha_3: "CCK", + un_code: 166 + }); + array.push({ + name: "Colombia", + alpha_2: "CO", + alpha_3: "COL", + un_code: 170 + }); + array.push({ + name: "Comoros", + alpha_2: "KM", + alpha_3: "COM", + un_code: 174 + }); + array.push({ + name: "Congo (Brazzaville)", + alpha_2: "CG", + alpha_3: "COG", + un_code: 178 + }); + array.push({ + name: "Congo, (Kinshasa)", + alpha_2: "CD", + alpha_3: "COD", + un_code: 180 + }); + array.push({ + name: "Cook Islands", + alpha_2: "CK", + alpha_3: "COK", + un_code: 184 + }); + array.push({ + name: "Costa Rica", + alpha_2: "CR", + alpha_3: "CRI", + un_code: 188 + }); + array.push({ + name: "Côte d'Ivoire", + alpha_2: "CI", + alpha_3: "CIV", + un_code: 384 + }); + array.push({ + name: "Croatia", + alpha_2: "HR", + alpha_3: "HRV", + un_code: 191 + }); + array.push({ + name: "Cuba", + alpha_2: "CU", + alpha_3: "CUB", + un_code: 192 + }); + array.push({ + name: "Cyprus", + alpha_2: "CY", + alpha_3: "CYP", + un_code: 196 + }); + array.push({ + name: "Czech Republic", + alpha_2: "CZ", + alpha_3: "CZE", + un_code: 203 + }); + array.push({ + name: "Denmark", + alpha_2: "DK", + alpha_3: "DNK", + un_code: 208 + }); + array.push({ + name: "Djibouti", + alpha_2: "DJ", + alpha_3: "DJI", + un_code: 262 + }); + array.push({ + name: "Dominica", + alpha_2: "DM", + alpha_3: "DMA", + un_code: 212 + }); + array.push({ + name: "Dominican Republic", + alpha_2: "DO", + alpha_3: "DOM", + un_code: 214 + }); + array.push({ + name: "Ecuador", + alpha_2: "EC", + alpha_3: "ECU", + un_code: 218 + }); + array.push({ + name: "Egypt", + alpha_2: "EG", + alpha_3: "EGY", + un_code: 818 + }); + array.push({ + name: "El Salvador", + alpha_2: "SV", + alpha_3: "SLV", + un_code: 222 + }); + array.push({ + name: "Equatorial Guinea", + alpha_2: "GQ", + alpha_3: "GNQ", + un_code: 226 + }); + array.push({ + name: "Eritrea", + alpha_2: "ER", + alpha_3: "ERI", + un_code: 232 + }); + array.push({ + name: "Estonia", + alpha_2: "EE", + alpha_3: "EST", + un_code: 233 + }); + array.push({ + name: "Ethiopia", + alpha_2: "ET", + alpha_3: "ETH", + un_code: 231 + }); + array.push({ + name: "Falkland Islands (Malvinas)", + alpha_2: "FK", + alpha_3: "FLK", + un_code: 238 + }); + array.push({ + name: "Faroe Islands", + alpha_2: "FO", + alpha_3: "FRO", + un_code: 234 + }); + array.push({ + name: "Fiji", + alpha_2: "FJ", + alpha_3: "FJI", + un_code: 242 + }); + array.push({ + name: "Finland", + alpha_2: "FI", + alpha_3: "FIN", + un_code: 246 + }); + array.push({ + name: "France", + alpha_2: "FR", + alpha_3: "FRA", + un_code: 250 + }); + array.push({ + name: "French Guiana", + alpha_2: "GF", + alpha_3: "GUF", + un_code: 254 + }); + array.push({ + name: "French Polynesia", + alpha_2: "PF", + alpha_3: "PYF", + un_code: 258 + }); + array.push({ + name: "French Southern Territories", + alpha_2: "TF", + alpha_3: "ATF", + un_code: 260 + }); + array.push({ + name: "Gabon", + alpha_2: "GA", + alpha_3: "GAB", + un_code: 266 + }); + array.push({ + name: "Gambia", + alpha_2: "GM", + alpha_3: "GMB", + un_code: 270 + }); + array.push({ + name: "Georgia", + alpha_2: "GE", + alpha_3: "GEO", + un_code: 268 + }); + array.push({ + name: "Germany", + alpha_2: "DE", + alpha_3: "DEU", + un_code: 276 + }); + array.push({ + name: "Ghana", + alpha_2: "GH", + alpha_3: "GHA", + un_code: 288 + }); + array.push({ + name: "Gibraltar", + alpha_2: "GI", + alpha_3: "GIB", + un_code: 292 + }); + array.push({ + name: "Greece", + alpha_2: "GR", + alpha_3: "GRC", + un_code: 300 + }); + array.push({ + name: "Greenland", + alpha_2: "GL", + alpha_3: "GRL", + un_code: 304 + }); + array.push({ + name: "Grenada", + alpha_2: "GD", + alpha_3: "GRD", + un_code: 308 + }); + array.push({ + name: "Guadeloupe", + alpha_2: "GP", + alpha_3: "GLP", + un_code: 312 + }); + array.push({ + name: "Guam", + alpha_2: "GU", + alpha_3: "GUM", + un_code: 316 + }); + array.push({ + name: "Guatemala", + alpha_2: "GT", + alpha_3: "GTM", + un_code: 320 + }); + array.push({ + name: "Guernsey", + alpha_2: "GG", + alpha_3: "GGY", + un_code: 831 + }); + array.push({ + name: "Guinea", + alpha_2: "GN", + alpha_3: "GIN", + un_code: 324 + }); + array.push({ + name: "Guinea-Bissau", + alpha_2: "GW", + alpha_3: "GNB", + un_code: 624 + }); + array.push({ + name: "Guyana", + alpha_2: "GY", + alpha_3: "GUY", + un_code: 328 + }); + array.push({ + name: "Haiti", + alpha_2: "HT", + alpha_3: "HTI", + un_code: 332 + }); + array.push({ + name: "Heard and Mcdonald Islands", + alpha_2: "HM", + alpha_3: "HMD", + un_code: 334 + }); + array.push({ + name: "Holy See (Vatican City State)", + alpha_2: "VA", + alpha_3: "VAT", + un_code: 336 + }); + array.push({ + name: "Honduras", + alpha_2: "HN", + alpha_3: "HND", + un_code: 340 + }); + array.push({ + name: "Hungary", + alpha_2: "HU", + alpha_3: "HUN", + un_code: 348 + }); + array.push({ + name: "Iceland", + alpha_2: "IS", + alpha_3: "ISL", + un_code: 352 + }); + array.push({ + name: "India", + alpha_2: "IN", + alpha_3: "IND", + un_code: 356 + }); + array.push({ + name: "Indonesia", + alpha_2: "ID", + alpha_3: "IDN", + un_code: 360 + }); + array.push({ + name: "Iran, Islamic Republic of", + alpha_2: "IR", + alpha_3: "IRN", + un_code: 364 + }); + array.push({ + name: "Iraq", + alpha_2: "IQ", + alpha_3: "IRQ", + un_code: 368 + }); + array.push({ + name: "Ireland", + alpha_2: "IE", + alpha_3: "IRL", + un_code: 372 + }); + array.push({ + name: "Isle of Man", + alpha_2: "IM", + alpha_3: "IMN", + un_code: 833 + }); + array.push({ + name: "Israel", + alpha_2: "IL", + alpha_3: "ISR", + un_code: 376 + }); + array.push({ + name: "Italy", + alpha_2: "IT", + alpha_3: "ITA", + un_code: 380 + }); + array.push({ + name: "Jamaica", + alpha_2: "JM", + alpha_3: "JAM", + un_code: 388 + }); + array.push({ + name: "Japan", + alpha_2: "JP", + alpha_3: "JPN", + un_code: 392 + }); + array.push({ + name: "Jersey", + alpha_2: "JE", + alpha_3: "JEY", + un_code: 832 + }); + array.push({ + name: "Jordan", + alpha_2: "JO", + alpha_3: "JOR", + un_code: 400 + }); + array.push({ + name: "Kazakhstan", + alpha_2: "KZ", + alpha_3: "KAZ", + un_code: 398 + }); + array.push({ + name: "Kenya", + alpha_2: "KE", + alpha_3: "KEN", + un_code: 404 + }); + array.push({ + name: "Kiribati", + alpha_2: "KI", + alpha_3: "KIR", + un_code: 296 + }); + array.push({ + name: "Korea (North)", + alpha_2: "KP", + alpha_3: "PRK", + un_code: 408 + }); + array.push({ + name: "Korea (South)", + alpha_2: "KR", + alpha_3: "KOR", + un_code: 410 + }); + array.push({ + name: "Kuwait", + alpha_2: "KW", + alpha_3: "KWT", + un_code: 414 + }); + array.push({ + name: "Kyrgyzstan", + alpha_2: "KG", + alpha_3: "KGZ", + un_code: 417 + }); + array.push({ + name: "Lao PDR", + alpha_2: "LA", + alpha_3: "LAO", + un_code: 418 + }); + array.push({ + name: "Latvia", + alpha_2: "LV", + alpha_3: "LVA", + un_code: 428 + }); + array.push({ + name: "Lebanon", + alpha_2: "LB", + alpha_3: "LBN", + un_code: 422 + }); + array.push({ + name: "Lesotho", + alpha_2: "LS", + alpha_3: "LSO", + un_code: 426 + }); + array.push({ + name: "Liberia", + alpha_2: "LR", + alpha_3: "LBR", + un_code: 430 + }); + array.push({ + name: "Libya", + alpha_2: "LY", + alpha_3: "LBY", + un_code: 434 + }); + array.push({ + name: "Liechtenstein", + alpha_2: "LI", + alpha_3: "LIE", + un_code: 438 + }); + array.push({ + name: "Lithuania", + alpha_2: "LT", + alpha_3: "LTU", + un_code: 440 + }); + array.push({ + name: "Luxembourg", + alpha_2: "LU", + alpha_3: "LUX", + un_code: 442 + }); + array.push({ + name: "Macedonia, Republic of", + alpha_2: "MK", + alpha_3: "MKD", + un_code: 807 + }); + array.push({ + name: "Madagascar", + alpha_2: "MG", + alpha_3: "MDG", + un_code: 450 + }); + array.push({ + name: "Malawi", + alpha_2: "MW", + alpha_3: "MWI", + un_code: 454 + }); + array.push({ + name: "Malaysia", + alpha_2: "MY", + alpha_3: "MYS", + un_code: 458 + }); + array.push({ + name: "Maldives", + alpha_2: "MV", + alpha_3: "MDV", + un_code: 462 + }); + array.push({ + name: "Mali", + alpha_2: "ML", + alpha_3: "MLI", + un_code: 466 + }); + array.push({ + name: "Malta", + alpha_2: "MT", + alpha_3: "MLT", + un_code: 470 + }); + array.push({ + name: "Marshall Islands", + alpha_2: "MH", + alpha_3: "MHL", + un_code: 584 + }); + array.push({ + name: "Martinique", + alpha_2: "MQ", + alpha_3: "MTQ", + un_code: 474 + }); + array.push({ + name: "Mauritania", + alpha_2: "MR", + alpha_3: "MRT", + un_code: 478 + }); + array.push({ + name: "Mauritius", + alpha_2: "MU", + alpha_3: "MUS", + un_code: 480 + }); + array.push({ + name: "Mayotte", + alpha_2: "YT", + alpha_3: "MYT", + un_code: 175 + }); + array.push({ + name: "Mexico", + alpha_2: "MX", + alpha_3: "MEX", + un_code: 484 + }); + array.push({ + name: "Micronesia, Federated States of", + alpha_2: "FM", + alpha_3: "FSM", + un_code: 583 + }); + array.push({ + name: "Moldova", + alpha_2: "MD", + alpha_3: "MDA", + un_code: 498 + }); + array.push({ + name: "Monaco", + alpha_2: "MC", + alpha_3: "MCO", + un_code: 492 + }); + array.push({ + name: "Mongolia", + alpha_2: "MN", + alpha_3: "MNG", + un_code: 496 + }); + array.push({ + name: "Montenegro", + alpha_2: "ME", + alpha_3: "MNE", + un_code: 499 + }); + array.push({ + name: "Montserrat", + alpha_2: "MS", + alpha_3: "MSR", + un_code: 500 + }); + array.push({ + name: "Morocco", + alpha_2: "MA", + alpha_3: "MAR", + un_code: 504 + }); + array.push({ + name: "Mozambique", + alpha_2: "MZ", + alpha_3: "MOZ", + un_code: 508 + }); + array.push({ + name: "Myanmar", + alpha_2: "MM", + alpha_3: "MMR", + un_code: 104 + }); + array.push({ + name: "Namibia", + alpha_2: "NA", + alpha_3: "NAM", + un_code: 516 + }); + array.push({ + name: "Nauru", + alpha_2: "NR", + alpha_3: "NRU", + un_code: 520 + }); + array.push({ + name: "Nepal", + alpha_2: "NP", + alpha_3: "NPL", + un_code: 524 + }); + array.push({ + name: "Netherlands", + alpha_2: "NL", + alpha_3: "NLD", + un_code: 528 + }); + array.push({ + name: "Netherlands Antilles", + alpha_2: "AN", + alpha_3: "ANT", + un_code: 530 + }); + array.push({ + name: "New Caledonia", + alpha_2: "NC", + alpha_3: "NCL", + un_code: 540 + }); + array.push({ + name: "New Zealand", + alpha_2: "NZ", + alpha_3: "NZL", + un_code: 554 + }); + array.push({ + name: "Nicaragua", + alpha_2: "NI", + alpha_3: "NIC", + un_code: 558 + }); + array.push({ + name: "Niger", + alpha_2: "NE", + alpha_3: "NER", + un_code: 562 + }); + array.push({ + name: "Nigeria", + alpha_2: "NG", + alpha_3: "NGA", + un_code: 566 + }); + array.push({ + name: "Niue", + alpha_2: "NU", + alpha_3: "NIU", + un_code: 570 + }); + array.push({ + name: "Norfolk Island", + alpha_2: "NF", + alpha_3: "NFK", + un_code: 574 + }); + array.push({ + name: "Northern Mariana Islands", + alpha_2: "MP", + alpha_3: "MNP", + un_code: 580 + }); + array.push({ + name: "Norway", + alpha_2: "NO", + alpha_3: "NOR", + un_code: 578 + }); + array.push({ + name: "Oman", + alpha_2: "OM", + alpha_3: "OMN", + un_code: 512 + }); + array.push({ + name: "Pakistan", + alpha_2: "PK", + alpha_3: "PAK", + un_code: 586 + }); + array.push({ + name: "Palau", + alpha_2: "PW", + alpha_3: "PLW", + un_code: 585 + }); + array.push({ + name: "Palestinian Territory", + alpha_2: "PS", + alpha_3: "PSE", + un_code: 275 + }); + array.push({ + name: "Panama", + alpha_2: "PA", + alpha_3: "PAN", + un_code: 591 + }); + array.push({ + name: "Papua New Guinea", + alpha_2: "PG", + alpha_3: "PNG", + un_code: 598 + }); + array.push({ + name: "Paraguay", + alpha_2: "PY", + alpha_3: "PRY", + un_code: 600 + }); + array.push({ + name: "Peru", + alpha_2: "PE", + alpha_3: "PER", + un_code: 604 + }); + array.push({ + name: "Philippines", + alpha_2: "PH", + alpha_3: "PHL", + un_code: 608 + }); + array.push({ + name: "Pitcairn", + alpha_2: "PN", + alpha_3: "PCN", + un_code: 612 + }); + array.push({ + name: "Poland", + alpha_2: "PL", + alpha_3: "POL", + un_code: 616 + }); + array.push({ + name: "Portugal", + alpha_2: "PT", + alpha_3: "PRT", + un_code: 620 + }); + array.push({ + name: "Puerto Rico", + alpha_2: "PR", + alpha_3: "PRI", + un_code: 630 + }); + array.push({ + name: "Qatar", + alpha_2: "QA", + alpha_3: "QAT", + un_code: 634 + }); + array.push({ + name: "Réunion", + alpha_2: "RE", + alpha_3: "REU", + un_code: 638 + }); + array.push({ + name: "Romania", + alpha_2: "RO", + alpha_3: "ROU", + un_code: 642 + }); + array.push({ + name: "Russian Federation", + alpha_2: "RU", + alpha_3: "RUS", + un_code: 643 + }); + array.push({ + name: "Rwanda", + alpha_2: "RW", + alpha_3: "RWA", + un_code: 646 + }); + array.push({ + name: "Saint-Barthélemy", + alpha_2: "BL", + alpha_3: "BLM", + un_code: 652 + }); + array.push({ + name: "Saint Helena", + alpha_2: "SH", + alpha_3: "SHN", + un_code: 654 + }); + array.push({ + name: "Saint Kitts and Nevis", + alpha_2: "KN", + alpha_3: "KNA", + un_code: 659 + }); + array.push({ + name: "Saint Lucia", + alpha_2: "LC", + alpha_3: "LCA", + un_code: 662 + }); + array.push({ + name: "Saint-Martin (French part)", + alpha_2: "MF", + alpha_3: "MAF", + un_code: 663 + }); + array.push({ + name: "Saint Pierre and Miquelon", + alpha_2: "PM", + alpha_3: "SPM", + un_code: 666 + }); + array.push({ + name: "Saint Vincent and Grenadines", + alpha_2: "VC", + alpha_3: "VCT", + un_code: 670 + }); + array.push({ + name: "Samoa", + alpha_2: "WS", + alpha_3: "WSM", + un_code: 882 + }); + array.push({ + name: "San Marino", + alpha_2: "SM", + alpha_3: "SMR", + un_code: 674 + }); + array.push({ + name: "Sao Tome and Principe", + alpha_2: "ST", + alpha_3: "STP", + un_code: 678 + }); + array.push({ + name: "Saudi Arabia", + alpha_2: "SA", + alpha_3: "SAU", + un_code: 682 + }); + array.push({ + name: "Senegal", + alpha_2: "SN", + alpha_3: "SEN", + un_code: 686 + }); + array.push({ + name: "Serbia", + alpha_2: "RS", + alpha_3: "SRB", + un_code: 688 + }); + array.push({ + name: "Seychelles", + alpha_2: "SC", + alpha_3: "SYC", + un_code: 690 + }); + array.push({ + name: "Sierra Leone", + alpha_2: "SL", + alpha_3: "SLE", + un_code: 694 + }); + array.push({ + name: "Singapore", + alpha_2: "SG", + alpha_3: "SGP", + un_code: 702 + }); + array.push({ + name: "Slovakia", + alpha_2: "SK", + alpha_3: "SVK", + un_code: 703 + }); + array.push({ + name: "Slovenia", + alpha_2: "SI", + alpha_3: "SVN", + un_code: 705 + }); + array.push({ + name: "Solomon Islands", + alpha_2: "SB", + alpha_3: "SLB", + un_code: 90 + }); + array.push({ + name: "Somalia", + alpha_2: "SO", + alpha_3: "SOM", + un_code: 706 + }); + array.push({ + name: "South Africa", + alpha_2: "ZA", + alpha_3: "ZAF", + un_code: 710 + }); + array.push({ + name: "South Georgia and the South Sandwich Islands", + alpha_2: "GS", + alpha_3: "SGS", + un_code: 239 + }); + array.push({ + name: "South Sudan", + alpha_2: "SS", + alpha_3: "SSD", + un_code: 728 + }); + array.push({ + name: "Spain", + alpha_2: "ES", + alpha_3: "ESP", + un_code: 724 + }); + array.push({ + name: "Sri Lanka", + alpha_2: "LK", + alpha_3: "LKA", + un_code: 144 + }); + array.push({ + name: "Sudan", + alpha_2: "SD", + alpha_3: "SDN", + un_code: 736 + }); + array.push({ + name: "Suriname", + alpha_2: "SR", + alpha_3: "SUR", + un_code: 740 + }); + array.push({ + name: "Svalbard and Jan Mayen Islands", + alpha_2: "SJ", + alpha_3: "SJM", + un_code: 744 + }); + array.push({ + name: "Swaziland", + alpha_2: "SZ", + alpha_3: "SWZ", + un_code: 748 + }); + array.push({ + name: "Sweden", + alpha_2: "SE", + alpha_3: "SWE", + un_code: 752 + }); + array.push({ + name: "Switzerland", + alpha_2: "CH", + alpha_3: "CHE", + un_code: 756 + }); + array.push({ + name: "Syrian Arab Republic (Syria)", + alpha_2: "SY", + alpha_3: "SYR", + un_code: 760 + }); + array.push({ + name: "Taiwan, Republic of China", + alpha_2: "TW", + alpha_3: "TWN", + un_code: 158 + }); + array.push({ + name: "Tajikistan", + alpha_2: "TJ", + alpha_3: "TJK", + un_code: 762 + }); + array.push({ + name: "Tanzania, United Republic of", + alpha_2: "TZ", + alpha_3: "TZA", + un_code: 834 + }); + array.push({ + name: "Thailand", + alpha_2: "TH", + alpha_3: "THA", + un_code: 764 + }); + array.push({ + name: "Timor-Leste", + alpha_2: "TL", + alpha_3: "TLS", + un_code: 626 + }); + array.push({ + name: "Togo", + alpha_2: "TG", + alpha_3: "TGO", + un_code: 768 + }); + array.push({ + name: "Tokelau", + alpha_2: "TK", + alpha_3: "TKL", + un_code: 772 + }); + array.push({ + name: "Tonga", + alpha_2: "TO", + alpha_3: "TON", + un_code: 776 + }); + array.push({ + name: "Trinidad and Tobago", + alpha_2: "TT", + alpha_3: "TTO", + un_code: 780 + }); + array.push({ + name: "Tunisia", + alpha_2: "TN", + alpha_3: "TUN", + un_code: 788 + }); + array.push({ + name: "Turkey", + alpha_2: "TR", + alpha_3: "TUR", + un_code: 792 + }); + array.push({ + name: "Turkmenistan", + alpha_2: "TM", + alpha_3: "TKM", + un_code: 795 + }); + array.push({ + name: "Turks and Caicos Islands", + alpha_2: "TC", + alpha_3: "TCA", + un_code: 796 + }); + array.push({ + name: "Tuvalu", + alpha_2: "TV", + alpha_3: "TUV", + un_code: 798 + }); + array.push({ + name: "Uganda", + alpha_2: "UG", + alpha_3: "UGA", + un_code: 800 + }); + array.push({ + name: "Ukraine", + alpha_2: "UA", + alpha_3: "UKR", + un_code: 804 + }); + array.push({ + name: "United Arab Emirates", + alpha_2: "AE", + alpha_3: "ARE", + un_code: 784 + }); + array.push({ + name: "United Kingdom", + alpha_2: "GB", + alpha_3: "GBR", + un_code: 826 + }); + array.push({ + name: "United States of America", + alpha_2: "US", + alpha_3: "USA", + un_code: 840 + }); + array.push({ + name: "US Minor Outlying Islands", + alpha_2: "UM", + alpha_3: "UMI", + un_code: 581 + }); + array.push({ + name: "Uruguay", + alpha_2: "UY", + alpha_3: "URY", + un_code: 858 + }); + array.push({ + name: "Uzbekistan", + alpha_2: "UZ", + alpha_3: "UZB", + un_code: 860 + }); + array.push({ + name: "Vanuatu", + alpha_2: "VU", + alpha_3: "VUT", + un_code: 548 + }); + array.push({ + name: "Venezuela (Bolivarian Republic)", + alpha_2: "VE", + alpha_3: "VEN", + un_code: 862 + }); + array.push({ + name: "Viet Nam", + alpha_2: "VN", + alpha_3: "VNM", + un_code: 704 + }); + array.push({ + name: "Virgin Islands, US", + alpha_2: "VI", + alpha_3: "VIR", + un_code: 850 + }); + array.push({ + name: "Wallis and Futuna Islands", + alpha_2: "WF", + alpha_3: "WLF", + un_code: 876 + }); + array.push({ + name: "Western Sahara", + alpha_2: "EH", + alpha_3: "ESH", + un_code: 732 + }); + array.push({ + name: "Yemen", + alpha_2: "YE", + alpha_3: "YEM", + un_code: 887 + }); + array.push({ + name: "Zambia", + alpha_2: "ZM", + alpha_3: "ZMB", + un_code: 894 + }); + array.push({ + name: "Zimbabwe", + alpha_2: "ZW", + alpha_3: "ZWE", + un_code: 716 + }); +}; + +export function country_name(alpha_code: string, fallback?: string) { + return (alpha_2_map[alpha_code.toUpperCase()] || {name: fallback || tr("unknown country")}).name; +} + +fill_country_infos(country_infos); +for(const country of country_infos) + alpha_2_map[country.alpha_2] = country; diff --git a/shared/js/i18n/localize.ts b/shared/js/i18n/localize.ts index 1e469c39..11ca2368 100644 --- a/shared/js/i18n/localize.ts +++ b/shared/js/i18n/localize.ts @@ -1,324 +1,317 @@ -function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {guid} from "tc-shared/crypto/uid"; +import {StaticSettings} from "tc-shared/settings"; +import {createErrorModal} from "tc-shared/ui/elements/Modal"; +import * as loader from "tc-loader"; +import {formatMessage} from "tc-shared/ui/frames/chat"; + +export interface TranslationKey { + message: string; + line?: number; + character?: number; + filename?: string; } -namespace i18n { - export interface TranslationKey { - message: string; - line?: number; - character?: number; - filename?: string; +export interface Translation { + key: TranslationKey; + translated: string; + flags?: string[]; +} + +export interface Contributor { + name: string; + email: string; +} + +export interface TranslationFile { + path: string; + full_url: string; + + translations: Translation[]; +} + +export interface RepositoryTranslation { + key: string; + path: string; + + country_code: string; + name: string; + contributors: Contributor[]; +} + +export interface TranslationRepository { + unique_id: string; + url: string; + name?: string; + contact?: string; + translations?: RepositoryTranslation[]; + load_timestamp?: number; +} + +let translations: Translation[] = []; +let fast_translate: { [key:string]:string; } = {}; +export function tr(message: string, key?: string) { + const sloppy = fast_translate[message]; + if(sloppy) return sloppy; + + log.info(LogCategory.I18N, "Translating \"%s\". Default: \"%s\"", key, message); + + let translated = message; + for(const translation of translations) { + if(translation.key.message == message) { + translated = translation.translated; + break; + } } - export interface Translation { - key: TranslationKey; - translated: string; - flags?: string[]; - } + fast_translate[message] = translated; + return translated; +} - export interface Contributor { - name: string; - email: string; - } +export function tra(message: string, ...args: any[]) { + message = tr(message); + return formatMessage(message, ...args); +} - export interface TranslationFile { - path: string; - full_url: string; +async function load_translation_file(url: string, path: string) : Promise { + return new Promise((resolve, reject) => { + $.ajax({ + url: url, + async: true, + success: result => { + try { + const file = (typeof(result) === "string" ? JSON.parse(result) : result) as TranslationFile; + if(!file) { + reject("Invalid json"); + return; + } - translations: Translation[]; - } + file.full_url = url; + file.path = path; - export interface RepositoryTranslation { - key: string; - path: string; - - country_code: string; - name: string; - contributors: Contributor[]; - } - - export interface TranslationRepository { - unique_id: string; - url: string; - name?: string; - contact?: string; - translations?: RepositoryTranslation[]; - load_timestamp?: number; - } - - let translations: Translation[] = []; - let fast_translate: { [key:string]:string; } = {}; - export function tr(message: string, key?: string) { - const sloppy = fast_translate[message]; - if(sloppy) return sloppy; - - log.info(LogCategory.I18N, "Translating \"%s\". Default: \"%s\"", key, message); - - let translated = message; - for(const translation of translations) { - if(translation.key.message == message) { - translated = translation.translated; - break; + //TODO: Validate file + resolve(file); + } catch(error) { + log.warn(LogCategory.I18N, tr("Failed to load translation file %s. Failed to parse or process json: %o"), url, error); + reject(tr("Failed to process or parse json!")); + } + }, + error: (xhr, error) => { + reject(tr("Failed to load file: ") + error); } + }) + }); +} + +export function load_file(url: string, path: string) : Promise { + return load_translation_file(url, path).then(async result => { + /* TODO: Improve this test?!*/ + try { + tr("Dummy translation test"); + } catch(error) { + throw "dummy test failed"; } - fast_translate[message] = translated; - return translated; - } + log.info(LogCategory.I18N, tr("Successfully initialized up translation file from %s"), url); + translations = result.translations; + }).catch(error => { + log.warn(LogCategory.I18N, tr("Failed to load translation file from \"%s\". Error: %o"), url, error); + return Promise.reject(error); + }); +} - export function tra(message: string, ...args: any[]) { - message = tr(message); - return MessageHelper.formatMessage(message, ...args); - } - - async function load_translation_file(url: string, path: string) : Promise { - return new Promise((resolve, reject) => { +async function load_repository0(repo: TranslationRepository, reload: boolean) { + if(!repo.load_timestamp || repo.load_timestamp < 1000 || reload) { + const info_json = await new Promise((resolve, reject) => { $.ajax({ - url: url, + url: repo.url + "/info.json", async: true, + cache: !reload, success: result => { - try { - const file = (typeof(result) === "string" ? JSON.parse(result) : result) as TranslationFile; - if(!file) { - reject("Invalid json"); - return; - } - - file.full_url = url; - file.path = path; - - //TODO: Validate file - resolve(file); - } catch(error) { - log.warn(LogCategory.I18N, tr("Failed to load translation file %s. Failed to parse or process json: %o"), url, error); - reject(tr("Failed to process or parse json!")); + const file = (typeof(result) === "string" ? JSON.parse(result) : result) as TranslationFile; + if(!file) { + reject("Invalid json"); + return; } + + resolve(file); }, error: (xhr, error) => { reject(tr("Failed to load file: ") + error); } }) }); + + Object.assign(repo, info_json); } - export function load_file(url: string, path: string) : Promise { - return load_translation_file(url, path).then(async result => { - /* TODO: Improve this test?!*/ - try { - tr("Dummy translation test"); - } catch(error) { - throw "dummy test failed"; - } + if(!repo.unique_id) + repo.unique_id = guid(); - log.info(LogCategory.I18N, tr("Successfully initialized up translation file from %s"), url); - translations = result.translations; - }).catch(error => { - log.warn(LogCategory.I18N, tr("Failed to load translation file from \"%s\". Error: %o"), url, error); - return Promise.reject(error); - }); + repo.translations = repo.translations || []; + repo.load_timestamp = Date.now(); +} + +export async function load_repository(url: string) : Promise { + const result = {} as TranslationRepository; + result.url = url; + await load_repository0(result, false); + return result; +} + +export namespace config { + export interface TranslationConfig { + current_repository_url?: string; + current_language?: string; + + current_translation_url?: string; + current_translation_path?: string; } - async function load_repository0(repo: TranslationRepository, reload: boolean) { - if(!repo.load_timestamp || repo.load_timestamp < 1000 || reload) { - const info_json = await new Promise((resolve, reject) => { - $.ajax({ - url: repo.url + "/info.json", - async: true, - cache: !reload, - success: result => { - const file = (typeof(result) === "string" ? JSON.parse(result) : result) as TranslationFile; - if(!file) { - reject("Invalid json"); - return; - } + export interface RepositoryConfig { + repositories?: { + url?: string; + repository?: TranslationRepository; + }[]; + } - resolve(file); - }, - error: (xhr, error) => { - reject(tr("Failed to load file: ") + error); - } - }) + const repository_config_key = "i18n.repository"; + let _cached_repository_config: RepositoryConfig; + export function repository_config() { + if(_cached_repository_config) + return _cached_repository_config; + + const config_string = localStorage.getItem(repository_config_key); + let config: RepositoryConfig; + try { + config = config_string ? JSON.parse(config_string) : {}; + } catch(error) { + log.error(LogCategory.I18N, tr("Failed to parse repository config: %o"), error); + } + config.repositories = config.repositories || []; + for(const repo of config.repositories) + (repo.repository || {load_timestamp: 0}).load_timestamp = 0; + + if(config.repositories.length == 0) { + //Add the default TeaSpeak repository + load_repository(StaticSettings.instance.static("i18n.default_repository", "https://web.teaspeak.de/i18n/")).then(repo => { + log.info(LogCategory.I18N, tr("Successfully added default repository from \"%s\"."), repo.url); + register_repository(repo); + }).catch(error => { + log.warn(LogCategory.I18N, tr("Failed to add default repository. Error: %o"), error); }); - - Object.assign(repo, info_json); } - if(!repo.unique_id) - repo.unique_id = guid(); - - repo.translations = repo.translations || []; - repo.load_timestamp = Date.now(); + return _cached_repository_config = config; } - export async function load_repository(url: string) : Promise { - const result = {} as TranslationRepository; - result.url = url; - await load_repository0(result, false); - return result; + export function save_repository_config() { + localStorage.setItem(repository_config_key, JSON.stringify(_cached_repository_config)); } - export namespace config { - export interface TranslationConfig { - current_repository_url?: string; - current_language?: string; + const translation_config_key = "i18n.translation"; + let _cached_translation_config: TranslationConfig; - current_translation_url?: string; - current_translation_path?: string; - } - - export interface RepositoryConfig { - repositories?: { - url?: string; - repository?: TranslationRepository; - }[]; - } - - const repository_config_key = "i18n.repository"; - let _cached_repository_config: RepositoryConfig; - export function repository_config() { - if(_cached_repository_config) - return _cached_repository_config; - - const config_string = localStorage.getItem(repository_config_key); - let config: RepositoryConfig; - try { - config = config_string ? JSON.parse(config_string) : {}; - } catch(error) { - log.error(LogCategory.I18N, tr("Failed to parse repository config: %o"), error); - } - config.repositories = config.repositories || []; - for(const repo of config.repositories) - (repo.repository || {load_timestamp: 0}).load_timestamp = 0; - - if(config.repositories.length == 0) { - //Add the default TeaSpeak repository - load_repository(StaticSettings.instance.static("i18n.default_repository", "https://web.teaspeak.de/i18n/")).then(repo => { - log.info(LogCategory.I18N, tr("Successfully added default repository from \"%s\"."), repo.url); - register_repository(repo); - }).catch(error => { - log.warn(LogCategory.I18N, tr("Failed to add default repository. Error: %o"), error); - }); - } - - return _cached_repository_config = config; - } - - export function save_repository_config() { - localStorage.setItem(repository_config_key, JSON.stringify(_cached_repository_config)); - } - - const translation_config_key = "i18n.translation"; - let _cached_translation_config: TranslationConfig; - - export function translation_config() : TranslationConfig { - if(_cached_translation_config) - return _cached_translation_config; - - const config_string = localStorage.getItem(translation_config_key); - try { - _cached_translation_config = config_string ? JSON.parse(config_string) : {}; - } catch(error) { - log.error(LogCategory.I18N, tr("Failed to initialize translation config. Using default one. Error: %o"), error); - _cached_translation_config = {} as any; - } + export function translation_config() : TranslationConfig { + if(_cached_translation_config) return _cached_translation_config; + + const config_string = localStorage.getItem(translation_config_key); + try { + _cached_translation_config = config_string ? JSON.parse(config_string) : {}; + } catch(error) { + log.error(LogCategory.I18N, tr("Failed to initialize translation config. Using default one. Error: %o"), error); + _cached_translation_config = {} as any; } - - export function save_translation_config() { - localStorage.setItem(translation_config_key, JSON.stringify(_cached_translation_config)); - } + return _cached_translation_config; } - export function register_repository(repository: TranslationRepository) { - if(!repository) return; - - for(const repo of config.repository_config().repositories) - if(repo.url == repository.url) return; - - config.repository_config().repositories.push(repository); - config.save_repository_config(); - } - - export function registered_repositories() : TranslationRepository[] { - return config.repository_config().repositories.map(e => e.repository || {url: e.url, load_timestamp: 0} as TranslationRepository); - } - - export function delete_repository(repository: TranslationRepository) { - if(!repository) return; - - for(const repo of [...config.repository_config().repositories]) - if(repo.url == repository.url) { - config.repository_config().repositories.remove(repo); - } - config.save_repository_config(); - } - - export async function iterate_repositories(callback_entry: (repository: TranslationRepository) => any) { - const promises = []; - - for(const repository of registered_repositories()) { - promises.push(load_repository0(repository, false).then(() => callback_entry(repository)).catch(error => { - log.warn(LogCategory.I18N, "Failed to fetch repository %s. error: %o", repository.url, error); - })); - } - - await Promise.all(promises); - } - - export function select_translation(repository: TranslationRepository, entry: RepositoryTranslation) { - const cfg = config.translation_config(); - - if(entry && repository) { - cfg.current_language = entry.name; - cfg.current_repository_url = repository.url; - cfg.current_translation_url = repository.url + entry.path; - cfg.current_translation_path = entry.path; - } else { - cfg.current_language = undefined; - cfg.current_repository_url = undefined; - cfg.current_translation_url = undefined; - cfg.current_translation_path = undefined; - } - - config.save_translation_config(); - } - - /* ATTENTION: This method is called before most other library inizialisations! */ - export async function initialize() { - const rcfg = config.repository_config(); /* initialize */ - const cfg = config.translation_config(); - - if(cfg.current_translation_url) { - try { - await load_file(cfg.current_translation_url, cfg.current_translation_path); - } catch (error) { - console.error(tr("Failed to initialize selected translation: %o"), error); - const show_error = () => { - createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open() - }; - if(loader.running()) - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - priority: 10, - function: async () => show_error(), - name: "I18N error display" - }); - else - show_error(); - } - } - // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/de_DE.translation"); - // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/test.json"); + export function save_translation_config() { + localStorage.setItem(translation_config_key, JSON.stringify(_cached_translation_config)); } } -// @ts-ignore -const tr: typeof i18n.tr = i18n.tr; -const tra: typeof i18n.tra = i18n.tra; +export function register_repository(repository: TranslationRepository) { + if(!repository) return; -(window as any).tr = i18n.tr; -(window as any).tra = i18n.tra; \ No newline at end of file + for(const repo of config.repository_config().repositories) + if(repo.url == repository.url) return; + + config.repository_config().repositories.push(repository); + config.save_repository_config(); +} + +export function registered_repositories() : TranslationRepository[] { + return config.repository_config().repositories.map(e => e.repository || {url: e.url, load_timestamp: 0} as TranslationRepository); +} + +export function delete_repository(repository: TranslationRepository) { + if(!repository) return; + + for(const repo of [...config.repository_config().repositories]) + if(repo.url == repository.url) { + config.repository_config().repositories.remove(repo); + } + config.save_repository_config(); +} + +export async function iterate_repositories(callback_entry: (repository: TranslationRepository) => any) { + const promises = []; + + for(const repository of registered_repositories()) { + promises.push(load_repository0(repository, false).then(() => callback_entry(repository)).catch(error => { + log.warn(LogCategory.I18N, "Failed to fetch repository %s. error: %o", repository.url, error); + })); + } + + await Promise.all(promises); +} + +export function select_translation(repository: TranslationRepository, entry: RepositoryTranslation) { + const cfg = config.translation_config(); + + if(entry && repository) { + cfg.current_language = entry.name; + cfg.current_repository_url = repository.url; + cfg.current_translation_url = repository.url + entry.path; + cfg.current_translation_path = entry.path; + } else { + cfg.current_language = undefined; + cfg.current_repository_url = undefined; + cfg.current_translation_url = undefined; + cfg.current_translation_path = undefined; + } + + config.save_translation_config(); +} + +/* ATTENTION: This method is called before most other library initialisations! */ +export async function initialize() { + const rcfg = config.repository_config(); /* initialize */ + const cfg = config.translation_config(); + + if(cfg.current_translation_url) { + try { + await load_file(cfg.current_translation_url, cfg.current_translation_path); + } catch (error) { + console.error(tr("Failed to initialize selected translation: %o"), error); + const show_error = () => { + createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open() + }; + if(loader.running()) + loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + priority: 10, + function: async () => show_error(), + name: "I18N error display" + }); + else + show_error(); + } + } + // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/de_DE.translation"); + // await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/test.json"); +} + +window.tr = tr; +window.tra = tra; \ No newline at end of file diff --git a/shared/js/log.ts b/shared/js/log.ts index 4ea9d119..21c4768a 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -1,6 +1,8 @@ //Used by CertAccept popup +import {settings} from "tc-shared/settings"; +import * as loader from "tc-loader"; -enum LogCategory { +export enum LogCategory { CHANNEL, CHANNEL_PROPERTIES, /* separating channel and channel properties because on channel init logging is a big bottleneck */ CLIENT, @@ -19,233 +21,238 @@ enum LogCategory { DNS } -namespace log { - export enum LogType { - TRACE, - DEBUG, - INFO, - WARNING, - ERROR +export enum LogType { + TRACE, + DEBUG, + INFO, + WARNING, + ERROR +} + +let category_mapping = new Map([ + [LogCategory.CHANNEL, "Channel "], + [LogCategory.CHANNEL_PROPERTIES, "Channel "], + [LogCategory.CLIENT, "Client "], + [LogCategory.SERVER, "Server "], + [LogCategory.BOOKMARKS, "Bookmark "], + [LogCategory.PERMISSIONS, "Permission "], + [LogCategory.GENERAL, "General "], + [LogCategory.NETWORKING, "Network "], + [LogCategory.VOICE, "Voice "], + [LogCategory.AUDIO, "Audio "], + [LogCategory.CHANNEL, "Chat "], + [LogCategory.I18N, "I18N "], + [LogCategory.IDENTITIES, "Identities "], + [LogCategory.IPC, "IPC "], + [LogCategory.STATISTICS, "Statistics "], + [LogCategory.DNS, "DNS "] +]); + +export let enabled_mapping = new Map([ + [LogCategory.CHANNEL, true], + [LogCategory.CHANNEL_PROPERTIES, false], + [LogCategory.CLIENT, true], + [LogCategory.SERVER, true], + [LogCategory.BOOKMARKS, true], + [LogCategory.PERMISSIONS, true], + [LogCategory.GENERAL, true], + [LogCategory.NETWORKING, true], + [LogCategory.VOICE, true], + [LogCategory.AUDIO, true], + [LogCategory.CHAT, true], + [LogCategory.I18N, false], + [LogCategory.IDENTITIES, true], + [LogCategory.IPC, true], + [LogCategory.STATISTICS, true], + [LogCategory.DNS, true] +]); + +//Values will be overridden by initialize() +export let level_mapping = new Map([ + [LogType.TRACE, true], + [LogType.DEBUG, true], + [LogType.INFO, true], + [LogType.WARNING, true], + [LogType.ERROR, true] +]); + +enum GroupMode { + NATIVE, + PREFIX +} +const group_mode: GroupMode = GroupMode.PREFIX; + +//Category Example: ?log.i18n.enabled=0 +//Level Example A: ?log.level.trace.enabled=0 +//Level Example B: ?log.level=0 +export function initialize(default_level: LogType) { + for(const category of Object.keys(LogCategory).map(e => parseInt(e))) { + if(isNaN(category)) continue; + const category_name = LogCategory[category].toLowerCase(); + enabled_mapping.set(category, settings.static_global("log." + category_name.toLowerCase() + ".enabled", enabled_mapping.get(category))); } - let category_mapping = new Map([ - [LogCategory.CHANNEL, "Channel "], - [LogCategory.CHANNEL_PROPERTIES, "Channel "], - [LogCategory.CLIENT, "Client "], - [LogCategory.SERVER, "Server "], - [LogCategory.BOOKMARKS, "Bookmark "], - [LogCategory.PERMISSIONS, "Permission "], - [LogCategory.GENERAL, "General "], - [LogCategory.NETWORKING, "Network "], - [LogCategory.VOICE, "Voice "], - [LogCategory.AUDIO, "Audio "], - [LogCategory.CHANNEL, "Chat "], - [LogCategory.I18N, "I18N "], - [LogCategory.IDENTITIES, "Identities "], - [LogCategory.IPC, "IPC "], - [LogCategory.STATISTICS, "Statistics "], - [LogCategory.DNS, "DNS "] - ]); + const base_level = settings.static_global("log.level", default_level); - export let enabled_mapping = new Map([ - [LogCategory.CHANNEL, true], - [LogCategory.CHANNEL_PROPERTIES, false], - [LogCategory.CLIENT, true], - [LogCategory.SERVER, true], - [LogCategory.BOOKMARKS, true], - [LogCategory.PERMISSIONS, true], - [LogCategory.GENERAL, true], - [LogCategory.NETWORKING, true], - [LogCategory.VOICE, true], - [LogCategory.AUDIO, true], - [LogCategory.CHAT, true], - [LogCategory.I18N, false], - [LogCategory.IDENTITIES, true], - [LogCategory.IPC, true], - [LogCategory.STATISTICS, true], - [LogCategory.DNS, true] - ]); + for(const level of Object.keys(LogType).map(e => parseInt(e))) { + if(isNaN(level)) continue; - //Values will be overridden by initialize() - export let level_mapping = new Map([ - [LogType.TRACE, true], - [LogType.DEBUG, true], - [LogType.INFO, true], - [LogType.WARNING, true], - [LogType.ERROR, true] - ]); - - enum GroupMode { - NATIVE, - PREFIX - } - const group_mode: GroupMode = GroupMode.PREFIX; - - //Category Example: ?log.i18n.enabled=0 - //Level Example A: ?log.level.trace.enabled=0 - //Level Example B: ?log.level=0 - export function initialize(default_level: LogType) { - for(const category of Object.keys(LogCategory).map(e => parseInt(e))) { - if(isNaN(category)) continue; - const category_name = LogCategory[category].toLowerCase(); - enabled_mapping.set(category, settings.static_global("log." + category_name.toLowerCase() + ".enabled", enabled_mapping.get(category))); - } - - const base_level = settings.static_global("log.level", default_level); - - for(const level of Object.keys(LogType).map(e => parseInt(e))) { - if(isNaN(level)) continue; - - const level_name = LogType[level].toLowerCase(); - level_mapping.set(level, settings.static_global("log." + level_name + ".enabled", level >= base_level)); - } - } - - function logDirect(type: LogType, message: string, ...optionalParams: any[]) { - if(!level_mapping.get(type)) - return; - - switch (type) { - case LogType.TRACE: - case LogType.DEBUG: - console.debug(message, ...optionalParams); - break; - case LogType.INFO: - console.log(message, ...optionalParams); - break; - case LogType.WARNING: - console.warn(message, ...optionalParams); - break; - case LogType.ERROR: - console.error(message, ...optionalParams); - break; - } - } - - export function log(type: LogType, category: LogCategory, message: string, ...optionalParams: any[]) { - if(!enabled_mapping.get(category)) return; - - optionalParams.unshift(category_mapping.get(category)); - message = "[%s] " + message; - logDirect(type, message, ...optionalParams); - } - - export function trace(category: LogCategory, message: string, ...optionalParams: any[]) { - log(LogType.TRACE, category, message, ...optionalParams); - } - - export function debug(category: LogCategory, message: string, ...optionalParams: any[]) { - log(LogType.DEBUG, category, message, ...optionalParams); - } - - export function info(category: LogCategory, message: string, ...optionalParams: any[]) { - log(LogType.INFO, category, message, ...optionalParams); - } - - export function warn(category: LogCategory, message: string, ...optionalParams: any[]) { - log(LogType.WARNING, category, message, ...optionalParams); - } - - export function error(category: LogCategory, message: string, ...optionalParams: any[]) { - log(LogType.ERROR, category, message, ...optionalParams); - } - - export function group(level: LogType, category: LogCategory, name: string, ...optionalParams: any[]) : Group { - name = "[%s] " + name; - optionalParams.unshift(category_mapping.get(category)); - - return new Group(group_mode, level, category, name, optionalParams); - } - - export function table(level: LogType, category: LogCategory, title: string, arguments: any) { - if(group_mode == GroupMode.NATIVE) { - console.groupCollapsed(title); - console.table(arguments); - console.groupEnd(); - } else { - if(!enabled_mapping.get(category) || !level_mapping.get(level)) - return; - logDirect(level, tr("Snipped table \"%s\""), title); - } - } - - export class Group { - readonly mode: GroupMode; - readonly level: LogType; - readonly category: LogCategory; - readonly enabled: boolean; - - owner: Group = undefined; - - private readonly name: string; - private readonly optionalParams: any[][]; - private _collapsed: boolean = false; - private initialized = false; - private _log_prefix: string; - - constructor(mode: GroupMode, level: LogType, category: LogCategory, name: string, optionalParams: any[][], owner: Group = undefined) { - this.level = level; - this.mode = mode; - this.category = category; - this.name = name; - this.optionalParams = optionalParams; - this.enabled = enabled_mapping.get(category); - } - - group(level: LogType, name: string, ...optionalParams: any[]) : Group { - return new Group(this.mode, level, this.category, name, optionalParams, this); - } - - collapsed(flag: boolean = true) : this { - this._collapsed = flag; - return this; - } - - log(message: string, ...optionalParams: any[]) : this { - if(!this.enabled) - return this; - - if(!this.initialized) { - if(this.mode == GroupMode.NATIVE) { - if(this._collapsed && console.groupCollapsed) - console.groupCollapsed(this.name, ...this.optionalParams); - else - console.group(this.name, ...this.optionalParams); - } else { - this._log_prefix = " "; - let parent = this.owner; - while(parent) { - if(parent.mode == GroupMode.PREFIX) - this._log_prefix = this._log_prefix + parent._log_prefix; - else - break; - } - } - this.initialized = true; - } - if(this.mode == GroupMode.NATIVE) - logDirect(this.level, message, ...optionalParams); - else { - logDirect(this.level, "[%s] " + this._log_prefix + message, category_mapping.get(this.category), ...optionalParams); - } - return this; - } - - end() { - if(this.initialized) { - if(this.mode == GroupMode.NATIVE) - console.groupEnd(); - } - } - - get prefix() : string { - return this._log_prefix; - } - - set prefix(prefix: string) { - this._log_prefix = prefix; - } + const level_name = LogType[level].toLowerCase(); + level_mapping.set(level, settings.static_global("log." + level_name + ".enabled", level >= base_level)); } } -import LogType = log.LogType; \ No newline at end of file +function logDirect(type: LogType, message: string, ...optionalParams: any[]) { + if(!level_mapping.get(type)) + return; + + switch (type) { + case LogType.TRACE: + case LogType.DEBUG: + console.debug(message, ...optionalParams); + break; + case LogType.INFO: + console.log(message, ...optionalParams); + break; + case LogType.WARNING: + console.warn(message, ...optionalParams); + break; + case LogType.ERROR: + console.error(message, ...optionalParams); + break; + } +} + +export function log(type: LogType, category: LogCategory, message: string, ...optionalParams: any[]) { + if(!enabled_mapping.get(category)) return; + + optionalParams.unshift(category_mapping.get(category)); + message = "[%s] " + message; + logDirect(type, message, ...optionalParams); +} + +export function trace(category: LogCategory, message: string, ...optionalParams: any[]) { + log(LogType.TRACE, category, message, ...optionalParams); +} + +export function debug(category: LogCategory, message: string, ...optionalParams: any[]) { + log(LogType.DEBUG, category, message, ...optionalParams); +} + +export function info(category: LogCategory, message: string, ...optionalParams: any[]) { + log(LogType.INFO, category, message, ...optionalParams); +} + +export function warn(category: LogCategory, message: string, ...optionalParams: any[]) { + log(LogType.WARNING, category, message, ...optionalParams); +} + +export function error(category: LogCategory, message: string, ...optionalParams: any[]) { + log(LogType.ERROR, category, message, ...optionalParams); +} + +export function group(level: LogType, category: LogCategory, name: string, ...optionalParams: any[]) : Group { + name = "[%s] " + name; + optionalParams.unshift(category_mapping.get(category)); + + return new Group(group_mode, level, category, name, optionalParams); +} + +export function table(level: LogType, category: LogCategory, title: string, args: any) { + if(group_mode == GroupMode.NATIVE) { + console.groupCollapsed(title); + console.table(args); + console.groupEnd(); + } else { + if(!enabled_mapping.get(category) || !level_mapping.get(level)) + return; + logDirect(level, tr("Snipped table \"%s\""), title); + } +} + +export class Group { + readonly mode: GroupMode; + readonly level: LogType; + readonly category: LogCategory; + readonly enabled: boolean; + + owner: Group = undefined; + + private readonly name: string; + private readonly optionalParams: any[][]; + private _collapsed: boolean = false; + private initialized = false; + private _log_prefix: string; + + constructor(mode: GroupMode, level: LogType, category: LogCategory, name: string, optionalParams: any[][], owner: Group = undefined) { + this.level = level; + this.mode = mode; + this.category = category; + this.name = name; + this.optionalParams = optionalParams; + this.enabled = enabled_mapping.get(category); + } + + group(level: LogType, name: string, ...optionalParams: any[]) : Group { + return new Group(this.mode, level, this.category, name, optionalParams, this); + } + + collapsed(flag: boolean = true) : this { + this._collapsed = flag; + return this; + } + + log(message: string, ...optionalParams: any[]) : this { + if(!this.enabled) + return this; + + if(!this.initialized) { + if(this.mode == GroupMode.NATIVE) { + if(this._collapsed && console.groupCollapsed) + console.groupCollapsed(this.name, ...this.optionalParams); + else + console.group(this.name, ...this.optionalParams); + } else { + this._log_prefix = " "; + let parent = this.owner; + while(parent) { + if(parent.mode == GroupMode.PREFIX) + this._log_prefix = this._log_prefix + parent._log_prefix; + else + break; + } + } + this.initialized = true; + } + if(this.mode == GroupMode.NATIVE) + logDirect(this.level, message, ...optionalParams); + else { + logDirect(this.level, "[%s] " + this._log_prefix + message, category_mapping.get(this.category), ...optionalParams); + } + return this; + } + + end() { + if(this.initialized) { + if(this.mode == GroupMode.NATIVE) + console.groupEnd(); + } + } + + get prefix() : string { + return this._log_prefix; + } + + set prefix(prefix: string) { + this._log_prefix = prefix; + } +} + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "log enabled initialisation", + function: async () => initialize(loader.version().debug_mode ? LogType.TRACE : LogType.INFO), + priority: 150 +}); + +/* initialize global logging system, use by the loader for example */ +window.log = module.exports; \ No newline at end of file diff --git a/shared/js/main.ts b/shared/js/main.ts index f2e910bc..2c0b1121 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -1,31 +1,41 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// +import * as loader from "tc-loader"; +import {settings, Settings} from "tc-shared/settings"; +import * as profiles from "tc-shared/profiles/ConnectionProfile"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as bipc from "./BrowserIPC"; +import * as sound from "./sound/Sounds"; +import * as i18n from "./i18n/localize"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {createInfoModal} from "tc-shared/ui/elements/Modal"; +import {tra} from "./i18n/localize"; +import {RequestFileUpload} from "tc-shared/FileManager"; +import * as stats from "./stats"; +import * as fidentity from "./profiles/identities/TeaForumIdentity"; +import {default_recorder, RecorderProfile, set_default_recorder} from "tc-shared/voice/RecorderProfile"; +import * as cmanager from "tc-shared/ui/frames/connection_handlers"; +import {server_connections, ServerConnectionManager} from "tc-shared/ui/frames/connection_handlers"; +import * as control_bar from "tc-shared/ui/frames/ControlBar"; +import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect"; +import * as top_menu from "./ui/frames/MenuBar"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {openModalNewcomer} from "tc-shared/ui/modal/ModalNewcomer"; +import * as aplayer from "tc-backend/audio/player"; +import * as arecorder from "tc-backend/audio/recorder"; +import * as ppt from "tc-backend/ppt"; -import spawnYesNo = Modals.spawnYesNo; +/* required import for init */ +require("./proto").initialize(); +require("./ui/elements/ContextDivider").initialize(); const js_render = window.jsrender || $; const native_client = window.require !== undefined; -function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { - if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) - return constraints => navigator.mediaDevices.getUserMedia(constraints); - - const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - if(!_callbacked_function) - return undefined; - - return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); -} - -interface Window { - open_connected_question: () => Promise; +declare global { + interface Window { + open_connected_question: () => Promise; + } } function setup_close() { @@ -50,7 +60,7 @@ function setup_close() { })); const exit = () => { - const {remote} = require('electron'); + const {remote} = window.require('electron'); remote.getCurrentWindow().close(); }; @@ -133,7 +143,7 @@ async function initialize_app() { try { //Initialize main template const main = $("#tmpl_main").renderTag({ multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), - app_version: app.ui_version() + app_version: loader.version().ui }).dividerfy(); $("body").append(main); @@ -143,21 +153,21 @@ async function initialize_app() { return; } - control_bar = new ControlBar($("#control_bar")); /* setup the control bar */ + control_bar.set_control_bar(new control_bar.ControlBar($("#control_bar"))); /* setup the control bar */ - if(!audio.player.initialize()) + if(!aplayer.initialize()) console.warn(tr("Failed to initialize audio controller!")); - audio.player.on_ready(() => { - if(audio.player.set_master_volume) - audio.player.on_ready(() => audio.player.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER) / 100)); + aplayer.on_ready(() => { + if(aplayer.set_master_volume) + aplayer.on_ready(() => aplayer.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER) / 100)); else - log.warn(LogCategory.GENERAL, tr("Client does not support audio.player.set_master_volume()... May client is too old?")); - if(audio.recorder.device_refresh_available()) - audio.recorder.refresh_devices(); + log.warn(LogCategory.GENERAL, tr("Client does not support aplayer.set_master_volume()... May client is too old?")); + if(arecorder.device_refresh_available()) + arecorder.refresh_devices(); }); - default_recorder = new RecorderProfile("default"); + set_default_recorder(new RecorderProfile("default")); default_recorder.initialize().catch(error => { log.error(LogCategory.AUDIO, tr("Failed to initialize default recorder: %o"), error); }); @@ -180,78 +190,6 @@ async function initialize_app() { setup_close(); } -function str2ab8(str) { - const buf = new ArrayBuffer(str.length); - const bufView = new Uint8Array(buf); - for (let i = 0, strLen = str.length; i < strLen; i++) { - bufView[i] = str.charCodeAt(i); - } - return buf; -} - -/* FIXME Dont use atob, because it sucks for non UTF-8 tings */ -function arrayBufferBase64(base64: string) { - base64 = atob(base64); - const buf = new ArrayBuffer(base64.length); - const bufView = new Uint8Array(buf); - for (let i = 0, strLen = base64.length; i < strLen; i++) { - bufView[i] = base64.charCodeAt(i); - } - return buf; -} - -function base64_encode_ab(source: ArrayBufferLike) { - const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - let base64 = ""; - - const bytes = new Uint8Array(source); - const byte_length = bytes.byteLength; - const byte_reminder = byte_length % 3; - const main_length = byte_length - byte_reminder; - - let a, b, c, d; - let chunk; - - // Main loop deals with bytes in chunks of 3 - for (let i = 0; i < main_length; i = i + 3) { - // Combine the three bytes into a single integer - chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; - - // Use bitmasks to extract 6-bit segments from the triplet - a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 - b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 - c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 - d = (chunk & 63) >> 0; // 63 = (2^6 - 1) << 0 - - // Convert the raw binary segments to the appropriate ASCII encoding - base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; - } - - // Deal with the remaining bytes and padding - if (byte_reminder == 1) { - chunk = bytes[main_length]; - - a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 - - // Set the 4 least significant bits to zero - b = (chunk & 3) << 4; // 3 = 2^2 - 1 - - base64 += encodings[a] + encodings[b] + '=='; - } else if (byte_reminder == 2) { - chunk = (bytes[main_length] << 8) | bytes[main_length + 1]; - - a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 - b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 - - // Set the 2 least significant bits to zero - c = (chunk & 15) << 2; // 15 = 2^4 - 1 - - base64 += encodings[a] + encodings[b] + encodings[c] + '='; - } - - return base64 -} - /* class TestProxy extends bipc.MethodProxy { constructor(params: bipc.MethodProxyConnectParameters) { @@ -313,7 +251,7 @@ function handle_connect_request(properties: bipc.connect.ConnectRequestData, con }); server_connections.set_active_connection_handler(connection); } else { - Modals.spawnConnectModal({},{ + spawnConnectModal({},{ url: properties.address, enforce: true }, { @@ -356,14 +294,14 @@ function main() { top_menu.initialize(); - server_connections = new ServerConnectionManager($("#connection-handlers")); - control_bar.initialise(); /* before connection handler to allow property apply */ + cmanager.initialize(new ServerConnectionManager($("#connection-handlers"))); + control_bar.control_bar.initialise(); /* before connection handler to allow property apply */ const initial_handler = server_connections.spawn_server_connection_handler(); initial_handler.acquire_recorder(default_recorder, false); - control_bar.set_connection_handler(initial_handler); + control_bar.control_bar.set_connection_handler(initial_handler); /** Setup the XF forum identity **/ - profiles.identities.update_forum(); + fidentity.update_forum(); let _resize_timeout: NodeJS.Timer; $(window).on('resize', event => { @@ -481,7 +419,7 @@ function main() { /* for testing */ if(settings.static_global(Settings.KEY_USER_IS_NEW)) { - const modal = Modals.openModalNewcomer(); + const modal = openModalNewcomer(); modal.close_listener.push(() => settings.changeGlobal(Settings.KEY_USER_IS_NEW, false)); } } @@ -492,12 +430,12 @@ const task_teaweb_starter: loader.Task = { try { await initialize_app(); main(); - if(!audio.player.initialized()) { + if(!aplayer.initialized()) { log.info(LogCategory.VOICE, tr("Initialize audio controller later!")); - if(!audio.player.initializeFromGesture) { - console.error(tr("Missing audio.player.initializeFromGesture")); + if(!aplayer.initializeFromGesture) { + console.error(tr("Missing aplayer.initializeFromGesture")); } else - $(document).one('click', event => audio.player.initializeFromGesture()); + $(document).one('click', event => aplayer.initializeFromGesture()); } } catch (ex) { console.error(ex.stack); @@ -543,7 +481,7 @@ const task_connect_handler: loader.Task = { "You could now close this page."; createInfoModal( tr("Connecting successfully within other instance"), - MessageHelper.formatMessage(tr(message), connect_data.address), + formatMessage(tr(message), connect_data.address), { closeable: false, footer: undefined @@ -610,7 +548,7 @@ const task_certificate_callback: loader.Task = { "This page will close in {0} seconds."; createInfoModal( tr("Certificate acccepted successfully"), - MessageHelper.formatMessage(tr(message), seconds_tag), + formatMessage(tr(message), seconds_tag), { closeable: false, footer: undefined @@ -650,7 +588,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { try { await initialize(); - if(app.is_web()) { + if(loader.version().type == "web") { loader.register_task(loader.Stage.LOADED, task_certificate_callback); } else { loader.register_task(loader.Stage.LOADED, task_teaweb_starter); @@ -667,3 +605,15 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { priority: 1000 }); +loader.register_task(loader.Stage.LOADED, { + name: "error task", + function: async () => { + if(Settings.instance.static(Settings.KEY_LOAD_DUMMY_ERROR, false)) { + loader.critical_error("The tea is cold!", "Argh, this is evil! Cold tea dosn't taste good."); + throw "The tea is cold!"; + } + }, + priority: 20 +}); + +export = {}; \ No newline at end of file diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index c781f468..2c27181f 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -1,17 +1,24 @@ -/// +import {LaterPromise} from "tc-shared/utils/LaterPromise"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {PermissionManager, PermissionValue} from "tc-shared/permission/PermissionManager"; +import {ServerCommand} from "tc-shared/connection/ConnectionBase"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler"; -enum GroupType { +export enum GroupType { QUERY, TEMPLATE, NORMAL } -enum GroupTarget { +export enum GroupTarget { SERVER, CHANNEL } -class GroupProperties { +export class GroupProperties { iconid: number = 0; sortid: number = 0; @@ -19,12 +26,12 @@ class GroupProperties { namemode: number = 0; } -class GroupPermissionRequest { +export class GroupPermissionRequest { group_id: number; promise: LaterPromise; } -class Group { +export class Group { properties: GroupProperties = new GroupProperties(); readonly handle: GroupManager; @@ -63,7 +70,7 @@ class Group { } } -class GroupManager extends connection.AbstractCommandHandler { +export class GroupManager extends AbstractCommandHandler { readonly handle: ConnectionHandler; serverGroups: Group[] = []; @@ -83,7 +90,7 @@ class GroupManager extends connection.AbstractCommandHandler { this.channelGroups = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyservergrouplist": case "notifychannelgrouplist": @@ -160,7 +167,7 @@ class GroupManager extends connection.AbstractCommandHandler { } let group = new Group(this,parseInt(target == GroupTarget.SERVER ? groupData["sgid"] : groupData["cgid"]), target, type, groupData["name"]); - for(let key in groupData as any) { + for(let key in Object.keys(groupData)) { if(key == "sgid") continue; if(key == "cgid") continue; if(key == "type") continue; diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 9b558d1c..2942df71 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -1,358 +1,13 @@ -/// -/// -/// +import * as log from "tc-shared/log"; +import {LogCategory, LogType} from "tc-shared/log"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {LaterPromise} from "tc-shared/utils/LaterPromise"; +import {ServerCommand} from "tc-shared/connection/ConnectionBase"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler"; -enum PermissionType { - B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", - B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view", - B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view", - B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list", - B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list", - B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list", - B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find", - B_VIRTUALSERVER_CREATE = "b_virtualserver_create", - B_VIRTUALSERVER_DELETE = "b_virtualserver_delete", - B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any", - B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any", - B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id", - B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template", - B_SERVERQUERY_LOGIN = "b_serverquery_login", - B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send", - B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view", - B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add", - B_SERVERINSTANCE_STOP = "b_serverinstance_stop", - B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings", - B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup", - B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates", - B_VIRTUALSERVER_SELECT = "b_virtualserver_select", - B_VIRTUALSERVER_SELECT_GODMODE = "b_virtualserver_select_godmode", - B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view", - B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view", - B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list", - B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search", - B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list", - B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search", - B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist", - B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch", - B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo", - B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find", - B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search", - B_VIRTUALSERVER_START = "b_virtualserver_start", - B_VIRTUALSERVER_STOP = "b_virtualserver_stop", - B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list", - B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add", - B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use", - B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete", - B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view", - B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add", - B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password", - B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register", - B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister", - B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create", - B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy", - B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset", - B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name", - B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage", - B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients", - B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots", - B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password", - B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup", - B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP = "b_virtualserver_modify_default_musicgroup", - B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup", - B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup", - B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence", - B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain", - B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood", - B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings", - B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas", - B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage", - B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner", - B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton", - B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port", - B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host", - B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_default_messages", - B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart", - B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level", - B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator", - B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings", - B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version", - B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id", - B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist", - B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode", - B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords", - B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own", - B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default", - B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit", - B_VIRTUALSERVER_MODIFY_COUNTRY_CODE = "b_virtualserver_modify_country_code", - I_CHANNEL_MIN_DEPTH = "i_channel_min_depth", - I_CHANNEL_MAX_DEPTH = "i_channel_max_depth", - B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end", - I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power", - I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power", - B_CHANNEL_INFO_VIEW = "b_channel_info_view", - B_CHANNEL_CREATE_CHILD = "b_channel_create_child", - B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent", - B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent", - B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary", - B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private", - B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic", - B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description", - B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice", - B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic", - I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality", - I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min", - I_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_LENGTH = "i_channel_create_modify_conversation_history_length", - B_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_UNLIMITED = "b_channel_create_modify_conversation_history_unlimited", - B_CHANNEL_CREATE_MODIFY_CONVERSATION_PRIVATE = "b_channel_create_modify_conversation_private", - B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients", - B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients", - B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder", - B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default", - B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power", - B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password", - I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay", - B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent", - B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default", - B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent", - B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent", - B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary", - B_CHANNEL_MODIFY_NAME = "b_channel_modify_name", - B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic", - B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description", - B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password", - B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec", - B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality", - B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor", - B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients", - B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients", - B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder", - B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power", - I_CHANNEL_MODIFY_POWER = "i_channel_modify_power", - I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power", - B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted", - B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay", - B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent", - B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent", - B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary", - B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force", - I_CHANNEL_DELETE_POWER = "i_channel_delete_power", - B_CHANNEL_CONVERSATION_MESSAGE_DELETE = "b_channel_conversation_message_delete", - I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power", - B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent", - B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent", - B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary", - B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password", - B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients", - B_CHANNEL_IGNORE_VIEW_POWER = "b_channel_ignore_view_power", - I_CHANNEL_JOIN_POWER = "i_channel_join_power", - I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power", - B_CHANNEL_IGNORE_JOIN_POWER = "b_channel_ignore_join_power", - B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER = "b_channel_ignore_description_view_power", - I_CHANNEL_VIEW_POWER = "i_channel_view_power", - I_CHANNEL_NEEDED_VIEW_POWER = "i_channel_needed_view_power", - I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power", - I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power", - I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power", - I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power", - I_ICON_ID = "i_icon_id", - I_MAX_ICON_FILESIZE = "i_max_icon_filesize", - I_MAX_PLAYLIST_SIZE = "i_max_playlist_size", - I_MAX_PLAYLISTS = "i_max_playlists", - B_ICON_MANAGE = "b_icon_manage", - B_GROUP_IS_PERMANENT = "b_group_is_permanent", - I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type", - I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value", - I_GROUP_SORT_ID = "i_group_sort_id", - I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree", - B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create", - B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list", - B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list", - B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list", - B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create", - B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list", - B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list", - B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list", - B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list", - B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list", - B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list", - B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST = "b_virtualserver_playlist_permission_list", - I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power", - I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power", - I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power", - I_SERVER_GROUP_SELF_ADD_POWER = "i_server_group_self_add_power", - I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power", - I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power", - I_SERVER_GROUP_SELF_REMOVE_POWER = "i_server_group_self_remove_power", - I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power", - I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power", - I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power", - I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power", - I_CHANNEL_GROUP_SELF_ADD_POWER = "i_channel_group_self_add_power", - I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power", - I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power", - I_CHANNEL_GROUP_SELF_REMOVE_POWER = "i_channel_group_self_remove_power", - I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power", - I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power", - I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power", - I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power", - I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power", - I_GROUP_MODIFY_POWER = "i_group_modify_power", - I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power", - I_PERMISSION_MODIFY_POWER = "i_permission_modify_power", - B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore", - B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete", - B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete", - I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power", - I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power", - I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid", - I_CLIENT_MAX_CLONES_IP = "i_client_max_clones_ip", - I_CLIENT_MAX_CLONES_HWID = "i_client_max_clones_hwid", - I_CLIENT_MAX_IDLETIME = "i_client_max_idletime", - I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize", - I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions", - I_CLIENT_MAX_CHANNELS = "i_client_max_channels", - I_CLIENT_MAX_TEMPORARY_CHANNELS = "i_client_max_temporary_channels", - I_CLIENT_MAX_SEMI_CHANNELS = "i_client_max_semi_channels", - I_CLIENT_MAX_PERMANENT_CHANNELS = "i_client_max_permanent_channels", - B_CLIENT_USE_PRIORITY_SPEAKER = "b_client_use_priority_speaker", - B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions", - B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk", - B_CLIENT_IGNORE_BANS = "b_client_ignore_bans", - B_CLIENT_IGNORE_VPN = "b_client_ignore_vpn", - B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood", - B_CLIENT_ENFORCE_VALID_HWID = "b_client_enforce_valid_hwid", - B_CLIENT_ALLOW_INVALID_PACKET = "b_client_allow_invalid_packet", - B_CLIENT_ALLOW_INVALID_BADGES = "b_client_allow_invalid_badges", - B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command", - B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot", - B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander", - B_CLIENT_REQUEST_TALKER = "b_client_request_talker", - B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other", - B_CLIENT_IS_STICKY = "b_client_is_sticky", - B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky", - B_CLIENT_MUSIC_CREATE_PERMANENT = "b_client_music_create_permanent", - B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT = "b_client_music_create_semi_permanent", - B_CLIENT_MUSIC_CREATE_TEMPORARY = "b_client_music_create_temporary", - B_CLIENT_MUSIC_MODIFY_PERMANENT = "b_client_music_modify_permanent", - B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT = "b_client_music_modify_semi_permanent", - B_CLIENT_MUSIC_MODIFY_TEMPORARY = "b_client_music_modify_temporary", - I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME = "i_client_music_create_modify_max_volume", - I_CLIENT_MUSIC_LIMIT = "i_client_music_limit", - I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power", - I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power", - I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power", - I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power", - I_CLIENT_MUSIC_MODIFY_POWER = "i_client_music_modify_power", - I_CLIENT_MUSIC_NEEDED_MODIFY_POWER = "i_client_music_needed_modify_power", - I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power", - I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power", - B_PLAYLIST_CREATE = "b_playlist_create", - I_PLAYLIST_VIEW_POWER = "i_playlist_view_power", - I_PLAYLIST_NEEDED_VIEW_POWER = "i_playlist_needed_view_power", - I_PLAYLIST_MODIFY_POWER = "i_playlist_modify_power", - I_PLAYLIST_NEEDED_MODIFY_POWER = "i_playlist_needed_modify_power", - I_PLAYLIST_PERMISSION_MODIFY_POWER = "i_playlist_permission_modify_power", - I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER = "i_playlist_needed_permission_modify_power", - I_PLAYLIST_DELETE_POWER = "i_playlist_delete_power", - I_PLAYLIST_NEEDED_DELETE_POWER = "i_playlist_needed_delete_power", - I_PLAYLIST_SONG_ADD_POWER = "i_playlist_song_add_power", - I_PLAYLIST_SONG_NEEDED_ADD_POWER = "i_playlist_song_needed_add_power", - I_PLAYLIST_SONG_REMOVE_POWER = "i_playlist_song_remove_power", - I_PLAYLIST_SONG_NEEDED_REMOVE_POWER = "i_playlist_song_needed_remove_power", - B_CLIENT_INFO_VIEW = "b_client_info_view", - B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view", - B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own", - B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view", - I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power", - I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power", - B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view", - B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list", - B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list", - I_CLIENT_MUSIC_INFO = "i_client_music_info", - I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info", - I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power", - I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power", - I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power", - I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power", - I_CLIENT_BAN_POWER = "i_client_ban_power", - I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power", - I_CLIENT_MOVE_POWER = "i_client_move_power", - I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power", - I_CLIENT_COMPLAIN_POWER = "i_client_complain_power", - I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power", - B_CLIENT_COMPLAIN_LIST = "b_client_complain_list", - B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own", - B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete", - B_CLIENT_BAN_LIST = "b_client_ban_list", - B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global", - B_CLIENT_BAN_TRIGGER_LIST = "b_client_ban_trigger_list", - B_CLIENT_BAN_CREATE = "b_client_ban_create", - B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global", - B_CLIENT_BAN_NAME = "b_client_ban_name", - B_CLIENT_BAN_IP = "b_client_ban_ip", - B_CLIENT_BAN_HWID = "b_client_ban_hwid", - B_CLIENT_BAN_EDIT = "b_client_ban_edit", - B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global", - B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own", - B_CLIENT_BAN_DELETE = "b_client_ban_delete", - B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global", - B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global", - I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime", - I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power", - I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power", - B_CLIENT_EVEN_TEXTMESSAGE_SEND = "b_client_even_textmessage_send", - B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send", - B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send", - B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send", - I_CLIENT_TALK_POWER = "i_client_talk_power", - I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power", - I_CLIENT_POKE_POWER = "i_client_poke_power", - I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power", - B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker", - I_CLIENT_WHISPER_POWER = "i_client_whisper_power", - I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power", - B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description", - B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description", - B_CLIENT_USE_BBCODE_ANY = "b_client_use_bbcode_any", - B_CLIENT_USE_BBCODE_URL = "b_client_use_bbcode_url", - B_CLIENT_USE_BBCODE_IMAGE = "b_client_use_bbcode_image", - B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties", - B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties", - B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login", - B_CLIENT_QUERY_CREATE = "b_client_query_create", - B_CLIENT_QUERY_LIST = "b_client_query_list", - B_CLIENT_QUERY_LIST_OWN = "b_client_query_list_own", - B_CLIENT_QUERY_RENAME = "b_client_query_rename", - B_CLIENT_QUERY_RENAME_OWN = "b_client_query_rename_own", - B_CLIENT_QUERY_CHANGE_PASSWORD = "b_client_query_change_password", - B_CLIENT_QUERY_CHANGE_OWN_PASSWORD = "b_client_query_change_own_password", - B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL = "b_client_query_change_password_global", - B_CLIENT_QUERY_DELETE = "b_client_query_delete", - B_CLIENT_QUERY_DELETE_OWN = "b_client_query_delete_own", - B_FT_IGNORE_PASSWORD = "b_ft_ignore_password", - B_FT_TRANSFER_LIST = "b_ft_transfer_list", - I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power", - I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power", - I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power", - I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power", - I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power", - I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power", - I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power", - I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power", - I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power", - I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power", - I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power", - I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power", - I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client", - I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client" -} - -class PermissionInfo { +export class PermissionInfo { name: string; id: number; description: string; @@ -363,21 +18,21 @@ class PermissionInfo { } } -class PermissionGroup { +export class PermissionGroup { begin: number; end: number; deep: number; name: string; } -class GroupedPermissions { +export class GroupedPermissions { group: PermissionGroup; permissions: PermissionInfo[]; children: GroupedPermissions[]; parent: GroupedPermissions; } -class PermissionValue { +export class PermissionValue { readonly type: PermissionInfo; value: number; flag_skip: boolean; @@ -411,75 +66,74 @@ class PermissionValue { } } -class NeededPermissionValue extends PermissionValue { +export class NeededPermissionValue extends PermissionValue { constructor(type, value) { super(type, value); } } -namespace permissions { - export type PermissionRequestKeys = { - client_id?: number; - channel_id?: number; - playlist_id?: number; +export type PermissionRequestKeys = { + client_id?: number; + channel_id?: number; + playlist_id?: number; +} + +export type PermissionRequest = PermissionRequestKeys & { + timeout_id: any; + promise: LaterPromise; +}; + +export namespace find { + export type Entry = { + type: "server" | "channel" | "client" | "client_channel" | "channel_group" | "server_group"; + value: number; + id: number; } - export type PermissionRequest = PermissionRequestKeys & { - timeout_id: any; - promise: LaterPromise; - }; + export type Client = Entry & { + type: "client", - export namespace find { - export type Entry = { - type: "server" | "channel" | "client" | "client_channel" | "channel_group" | "server_group"; - value: number; - id: number; - } + client_id: number; + } - export type Client = Entry & { - type: "client", + export type Channel = Entry & { + type: "channel", - client_id: number; - } + channel_id: number; + } - export type Channel = Entry & { - type: "channel", + export type Server = Entry & { + type: "server" + } - channel_id: number; - } + export type ClientChannel = Entry & { + type: "client_channel", - export type Server = Entry & { - type: "server" - } + client_id: number; + channel_id: number; + } - export type ClientChannel = Entry & { - type: "client_channel", + export type ChannelGroup = Entry & { + type: "channel_group", - client_id: number; - channel_id: number; - } + group_id: number; + } - export type ChannelGroup = Entry & { - type: "channel_group", + export type ServerGroup = Entry & { + type: "server_group", - group_id: number; - } - - export type ServerGroup = Entry & { - type: "server_group", - - group_id: number; - } + group_id: number; } } -type RequestLists = +export type RequestLists = "requests_channel_permissions" | "requests_client_permissions" | "requests_client_channel_permissions" | "requests_playlist_permissions" | "requests_playlist_client_permissions"; -class PermissionManager extends connection.AbstractCommandHandler { + +export class PermissionManager extends AbstractCommandHandler { readonly handle: ConnectionHandler; permissionList: PermissionInfo[] = []; @@ -488,11 +142,11 @@ class PermissionManager extends connection.AbstractCommandHandler { needed_permission_change_listener: {[permission: string]:(() => any)[]} = {}; - requests_channel_permissions: permissions.PermissionRequest[] = []; - requests_client_permissions: permissions.PermissionRequest[] = []; - requests_client_channel_permissions: permissions.PermissionRequest[] = []; - requests_playlist_permissions: permissions.PermissionRequest[] = []; - requests_playlist_client_permissions: permissions.PermissionRequest[] = []; + requests_channel_permissions: PermissionRequest[] = []; + requests_client_permissions: PermissionRequest[] = []; + requests_client_channel_permissions: PermissionRequest[] = []; + requests_playlist_permissions: PermissionRequest[] = []; + requests_playlist_client_permissions: PermissionRequest[] = []; requests_permfind: { timeout_id: number, @@ -603,7 +257,7 @@ class PermissionManager extends connection.AbstractCommandHandler { this._cacheNeededPermissions = undefined; } - handle_command(command: connection.ServerCommand): boolean { + handle_command(command: ServerCommand): boolean { switch (command.command) { case "notifyclientneededpermissions": this.onNeededPermissions(command.arguments); @@ -775,7 +429,7 @@ class PermissionManager extends connection.AbstractCommandHandler { }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); } - private execute_channel_permission_request(request: permissions.PermissionRequestKeys) { + private execute_channel_permission_request(request: PermissionRequestKeys) { this.handle.serverConnection.send_command("channelpermlist", {"cid": request.channel_id}).catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) this.fullfill_permission_request("requests_channel_permissions", request, "success", []); @@ -785,7 +439,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } requestChannelPermissions(channelId: number) : Promise { - const keys: permissions.PermissionRequestKeys = { + const keys: PermissionRequestKeys = { channel_id: channelId }; return this.execute_permission_request("requests_channel_permissions", keys, this.execute_channel_permission_request.bind(this)); @@ -799,7 +453,7 @@ class PermissionManager extends connection.AbstractCommandHandler { }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); } - private execute_client_permission_request(request: permissions.PermissionRequestKeys) { + private execute_client_permission_request(request: PermissionRequestKeys) { this.handle.serverConnection.send_command("clientpermlist", {cldbid: request.client_id}).catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) this.fullfill_permission_request("requests_client_permissions", request, "success", []); @@ -809,7 +463,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } requestClientPermissions(client_id: number) : Promise { - const keys: permissions.PermissionRequestKeys = { + const keys: PermissionRequestKeys = { client_id: client_id }; return this.execute_permission_request("requests_client_permissions", keys, this.execute_client_permission_request.bind(this)); @@ -826,7 +480,7 @@ class PermissionManager extends connection.AbstractCommandHandler { }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); } - private execute_client_channel_permission_request(request: permissions.PermissionRequestKeys) { + private execute_client_channel_permission_request(request: PermissionRequestKeys) { this.handle.serverConnection.send_command("channelclientpermlist", {cldbid: request.client_id, cid: request.channel_id}) .catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) @@ -837,7 +491,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } requestClientChannelPermissions(client_id: number, channel_id: number) : Promise { - const keys: permissions.PermissionRequestKeys = { + const keys: PermissionRequestKeys = { client_id: client_id }; return this.execute_permission_request("requests_client_channel_permissions", keys, this.execute_client_channel_permission_request.bind(this)); @@ -852,7 +506,7 @@ class PermissionManager extends connection.AbstractCommandHandler { }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); } - private execute_playlist_permission_request(request: permissions.PermissionRequestKeys) { + private execute_playlist_permission_request(request: PermissionRequestKeys) { this.handle.serverConnection.send_command("playlistpermlist", {playlist_id: request.playlist_id}) .catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) @@ -863,7 +517,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } requestPlaylistPermissions(playlist_id: number) : Promise { - const keys: permissions.PermissionRequestKeys = { + const keys: PermissionRequestKeys = { playlist_id: playlist_id }; return this.execute_permission_request("requests_playlist_permissions", keys, this.execute_playlist_permission_request.bind(this)); @@ -880,7 +534,7 @@ class PermissionManager extends connection.AbstractCommandHandler { }, "success", PermissionManager.parse_permission_bulk(json, this.handle.permissions)); } - private execute_playlist_client_permission_request(request: permissions.PermissionRequestKeys) { + private execute_playlist_client_permission_request(request: PermissionRequestKeys) { this.handle.serverConnection.send_command("playlistclientpermlist", {playlist_id: request.playlist_id, cldbid: request.client_id}) .catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) @@ -891,7 +545,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } requestPlaylistClientPermissions(playlist_id: number, client_database_id: number) : Promise { - const keys: permissions.PermissionRequestKeys = { + const keys: PermissionRequestKeys = { playlist_id: playlist_id, client_id: client_database_id }; @@ -907,8 +561,8 @@ class PermissionManager extends connection.AbstractCommandHandler { }; private execute_permission_request(list: RequestLists, - criteria: permissions.PermissionRequestKeys, - execute: (criteria: permissions.PermissionRequestKeys) => any) : Promise { + criteria: PermissionRequestKeys, + execute: (criteria: PermissionRequestKeys) => any) : Promise { for(const request of this[list]) if(this.criteria_equal(request, criteria) && request.promise.time() + 1000 < Date.now()) return request.promise; @@ -922,7 +576,7 @@ class PermissionManager extends connection.AbstractCommandHandler { return result.promise; }; - private fullfill_permission_request(list: RequestLists, criteria: permissions.PermissionRequestKeys, status: "success" | "error", result: any) { + private fullfill_permission_request(list: RequestLists, criteria: PermissionRequestKeys, status: "success" | "error", result: any) { for(const request of this[list]) { if(this.criteria_equal(request, criteria)) { this[list].remove(request); @@ -932,7 +586,7 @@ class PermissionManager extends connection.AbstractCommandHandler { } } - find_permission(...permissions: string[]) : Promise { + find_permission(...permissions: string[]) : Promise { const permission_ids = []; for(const permission of permissions) { const info = this.resolveInfo(permission); @@ -942,11 +596,11 @@ class PermissionManager extends connection.AbstractCommandHandler { } if(!permission_ids.length) return Promise.resolve([]); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const single_handler = { command: "notifypermfind", function: command => { - const result: permissions.find.Entry[] = []; + const result: find.Entry[] = []; for(const entry of command.arguments) { const perm_id = parseInt(entry["p"]); if(permission_ids.indexOf(perm_id) === -1) return; /* not our permfind result */ @@ -960,32 +614,32 @@ class PermissionManager extends connection.AbstractCommandHandler { data = { type: "server_group", group_id: parseInt(entry["id1"]), - } as permissions.find.ServerGroup; + } as find.ServerGroup; break; case 1: data = { type: "client", client_id: parseInt(entry["id2"]), - } as permissions.find.Client; + } as find.Client; break; case 2: data = { type: "channel", channel_id: parseInt(entry["id2"]), - } as permissions.find.Channel; + } as find.Channel; break; case 3: data = { type: "channel_group", group_id: parseInt(entry["id1"]), - } as permissions.find.ChannelGroup; + } as find.ChannelGroup; break; case 4: data = { type: "client_channel", client_id: parseInt(entry["id1"]), channel_id: parseInt(entry["id1"]), - } as permissions.find.ClientChannel; + } as find.ClientChannel; break; default: continue; diff --git a/shared/js/permission/PermissionType.ts b/shared/js/permission/PermissionType.ts new file mode 100644 index 00000000..99ae8d07 --- /dev/null +++ b/shared/js/permission/PermissionType.ts @@ -0,0 +1,350 @@ +export enum PermissionType { + B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", + B_SERVERINSTANCE_VERSION_VIEW = "b_serverinstance_version_view", + B_SERVERINSTANCE_INFO_VIEW = "b_serverinstance_info_view", + B_SERVERINSTANCE_VIRTUALSERVER_LIST = "b_serverinstance_virtualserver_list", + B_SERVERINSTANCE_BINDING_LIST = "b_serverinstance_binding_list", + B_SERVERINSTANCE_PERMISSION_LIST = "b_serverinstance_permission_list", + B_SERVERINSTANCE_PERMISSION_FIND = "b_serverinstance_permission_find", + B_VIRTUALSERVER_CREATE = "b_virtualserver_create", + B_VIRTUALSERVER_DELETE = "b_virtualserver_delete", + B_VIRTUALSERVER_START_ANY = "b_virtualserver_start_any", + B_VIRTUALSERVER_STOP_ANY = "b_virtualserver_stop_any", + B_VIRTUALSERVER_CHANGE_MACHINE_ID = "b_virtualserver_change_machine_id", + B_VIRTUALSERVER_CHANGE_TEMPLATE = "b_virtualserver_change_template", + B_SERVERQUERY_LOGIN = "b_serverquery_login", + B_SERVERINSTANCE_TEXTMESSAGE_SEND = "b_serverinstance_textmessage_send", + B_SERVERINSTANCE_LOG_VIEW = "b_serverinstance_log_view", + B_SERVERINSTANCE_LOG_ADD = "b_serverinstance_log_add", + B_SERVERINSTANCE_STOP = "b_serverinstance_stop", + B_SERVERINSTANCE_MODIFY_SETTINGS = "b_serverinstance_modify_settings", + B_SERVERINSTANCE_MODIFY_QUERYGROUP = "b_serverinstance_modify_querygroup", + B_SERVERINSTANCE_MODIFY_TEMPLATES = "b_serverinstance_modify_templates", + B_VIRTUALSERVER_SELECT = "b_virtualserver_select", + B_VIRTUALSERVER_SELECT_GODMODE = "b_virtualserver_select_godmode", + B_VIRTUALSERVER_INFO_VIEW = "b_virtualserver_info_view", + B_VIRTUALSERVER_CONNECTIONINFO_VIEW = "b_virtualserver_connectioninfo_view", + B_VIRTUALSERVER_CHANNEL_LIST = "b_virtualserver_channel_list", + B_VIRTUALSERVER_CHANNEL_SEARCH = "b_virtualserver_channel_search", + B_VIRTUALSERVER_CLIENT_LIST = "b_virtualserver_client_list", + B_VIRTUALSERVER_CLIENT_SEARCH = "b_virtualserver_client_search", + B_VIRTUALSERVER_CLIENT_DBLIST = "b_virtualserver_client_dblist", + B_VIRTUALSERVER_CLIENT_DBSEARCH = "b_virtualserver_client_dbsearch", + B_VIRTUALSERVER_CLIENT_DBINFO = "b_virtualserver_client_dbinfo", + B_VIRTUALSERVER_PERMISSION_FIND = "b_virtualserver_permission_find", + B_VIRTUALSERVER_CUSTOM_SEARCH = "b_virtualserver_custom_search", + B_VIRTUALSERVER_START = "b_virtualserver_start", + B_VIRTUALSERVER_STOP = "b_virtualserver_stop", + B_VIRTUALSERVER_TOKEN_LIST = "b_virtualserver_token_list", + B_VIRTUALSERVER_TOKEN_ADD = "b_virtualserver_token_add", + B_VIRTUALSERVER_TOKEN_USE = "b_virtualserver_token_use", + B_VIRTUALSERVER_TOKEN_DELETE = "b_virtualserver_token_delete", + B_VIRTUALSERVER_LOG_VIEW = "b_virtualserver_log_view", + B_VIRTUALSERVER_LOG_ADD = "b_virtualserver_log_add", + B_VIRTUALSERVER_JOIN_IGNORE_PASSWORD = "b_virtualserver_join_ignore_password", + B_VIRTUALSERVER_NOTIFY_REGISTER = "b_virtualserver_notify_register", + B_VIRTUALSERVER_NOTIFY_UNREGISTER = "b_virtualserver_notify_unregister", + B_VIRTUALSERVER_SNAPSHOT_CREATE = "b_virtualserver_snapshot_create", + B_VIRTUALSERVER_SNAPSHOT_DEPLOY = "b_virtualserver_snapshot_deploy", + B_VIRTUALSERVER_PERMISSION_RESET = "b_virtualserver_permission_reset", + B_VIRTUALSERVER_MODIFY_NAME = "b_virtualserver_modify_name", + B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE = "b_virtualserver_modify_welcomemessage", + B_VIRTUALSERVER_MODIFY_MAXCLIENTS = "b_virtualserver_modify_maxclients", + B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS = "b_virtualserver_modify_reserved_slots", + B_VIRTUALSERVER_MODIFY_PASSWORD = "b_virtualserver_modify_password", + B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP = "b_virtualserver_modify_default_servergroup", + B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP = "b_virtualserver_modify_default_musicgroup", + B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP = "b_virtualserver_modify_default_channelgroup", + B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP = "b_virtualserver_modify_default_channeladmingroup", + B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE = "b_virtualserver_modify_channel_forced_silence", + B_VIRTUALSERVER_MODIFY_COMPLAIN = "b_virtualserver_modify_complain", + B_VIRTUALSERVER_MODIFY_ANTIFLOOD = "b_virtualserver_modify_antiflood", + B_VIRTUALSERVER_MODIFY_FT_SETTINGS = "b_virtualserver_modify_ft_settings", + B_VIRTUALSERVER_MODIFY_FT_QUOTAS = "b_virtualserver_modify_ft_quotas", + B_VIRTUALSERVER_MODIFY_HOSTMESSAGE = "b_virtualserver_modify_hostmessage", + B_VIRTUALSERVER_MODIFY_HOSTBANNER = "b_virtualserver_modify_hostbanner", + B_VIRTUALSERVER_MODIFY_HOSTBUTTON = "b_virtualserver_modify_hostbutton", + B_VIRTUALSERVER_MODIFY_PORT = "b_virtualserver_modify_port", + B_VIRTUALSERVER_MODIFY_HOST = "b_virtualserver_modify_host", + B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES = "b_virtualserver_modify_default_messages", + B_VIRTUALSERVER_MODIFY_AUTOSTART = "b_virtualserver_modify_autostart", + B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL = "b_virtualserver_modify_needed_identity_security_level", + B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR = "b_virtualserver_modify_priority_speaker_dimm_modificator", + B_VIRTUALSERVER_MODIFY_LOG_SETTINGS = "b_virtualserver_modify_log_settings", + B_VIRTUALSERVER_MODIFY_MIN_CLIENT_VERSION = "b_virtualserver_modify_min_client_version", + B_VIRTUALSERVER_MODIFY_ICON_ID = "b_virtualserver_modify_icon_id", + B_VIRTUALSERVER_MODIFY_WEBLIST = "b_virtualserver_modify_weblist", + B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE = "b_virtualserver_modify_codec_encryption_mode", + B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS = "b_virtualserver_modify_temporary_passwords", + B_VIRTUALSERVER_MODIFY_TEMPORARY_PASSWORDS_OWN = "b_virtualserver_modify_temporary_passwords_own", + B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT = "b_virtualserver_modify_channel_temp_delete_delay_default", + B_VIRTUALSERVER_MODIFY_MUSIC_BOT_LIMIT = "b_virtualserver_modify_music_bot_limit", + B_VIRTUALSERVER_MODIFY_COUNTRY_CODE = "b_virtualserver_modify_country_code", + I_CHANNEL_MIN_DEPTH = "i_channel_min_depth", + I_CHANNEL_MAX_DEPTH = "i_channel_max_depth", + B_CHANNEL_GROUP_INHERITANCE_END = "b_channel_group_inheritance_end", + I_CHANNEL_PERMISSION_MODIFY_POWER = "i_channel_permission_modify_power", + I_CHANNEL_NEEDED_PERMISSION_MODIFY_POWER = "i_channel_needed_permission_modify_power", + B_CHANNEL_INFO_VIEW = "b_channel_info_view", + B_CHANNEL_CREATE_CHILD = "b_channel_create_child", + B_CHANNEL_CREATE_PERMANENT = "b_channel_create_permanent", + B_CHANNEL_CREATE_SEMI_PERMANENT = "b_channel_create_semi_permanent", + B_CHANNEL_CREATE_TEMPORARY = "b_channel_create_temporary", + B_CHANNEL_CREATE_PRIVATE = "b_channel_create_private", + B_CHANNEL_CREATE_WITH_TOPIC = "b_channel_create_with_topic", + B_CHANNEL_CREATE_WITH_DESCRIPTION = "b_channel_create_with_description", + B_CHANNEL_CREATE_WITH_PASSWORD = "b_channel_create_with_password", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8 = "b_channel_create_modify_with_codec_speex8", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16 = "b_channel_create_modify_with_codec_speex16", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32 = "b_channel_create_modify_with_codec_speex32", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48 = "b_channel_create_modify_with_codec_celtmono48", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE = "b_channel_create_modify_with_codec_opusvoice", + B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC = "b_channel_create_modify_with_codec_opusmusic", + I_CHANNEL_CREATE_MODIFY_WITH_CODEC_MAXQUALITY = "i_channel_create_modify_with_codec_maxquality", + I_CHANNEL_CREATE_MODIFY_WITH_CODEC_LATENCY_FACTOR_MIN = "i_channel_create_modify_with_codec_latency_factor_min", + I_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_LENGTH = "i_channel_create_modify_conversation_history_length", + B_CHANNEL_CREATE_MODIFY_CONVERSATION_HISTORY_UNLIMITED = "b_channel_create_modify_conversation_history_unlimited", + B_CHANNEL_CREATE_MODIFY_CONVERSATION_PRIVATE = "b_channel_create_modify_conversation_private", + B_CHANNEL_CREATE_WITH_MAXCLIENTS = "b_channel_create_with_maxclients", + B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS = "b_channel_create_with_maxfamilyclients", + B_CHANNEL_CREATE_WITH_SORTORDER = "b_channel_create_with_sortorder", + B_CHANNEL_CREATE_WITH_DEFAULT = "b_channel_create_with_default", + B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER = "b_channel_create_with_needed_talk_power", + B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD = "b_channel_create_modify_with_force_password", + I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY = "i_channel_create_modify_with_temp_delete_delay", + B_CHANNEL_MODIFY_PARENT = "b_channel_modify_parent", + B_CHANNEL_MODIFY_MAKE_DEFAULT = "b_channel_modify_make_default", + B_CHANNEL_MODIFY_MAKE_PERMANENT = "b_channel_modify_make_permanent", + B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT = "b_channel_modify_make_semi_permanent", + B_CHANNEL_MODIFY_MAKE_TEMPORARY = "b_channel_modify_make_temporary", + B_CHANNEL_MODIFY_NAME = "b_channel_modify_name", + B_CHANNEL_MODIFY_TOPIC = "b_channel_modify_topic", + B_CHANNEL_MODIFY_DESCRIPTION = "b_channel_modify_description", + B_CHANNEL_MODIFY_PASSWORD = "b_channel_modify_password", + B_CHANNEL_MODIFY_CODEC = "b_channel_modify_codec", + B_CHANNEL_MODIFY_CODEC_QUALITY = "b_channel_modify_codec_quality", + B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR = "b_channel_modify_codec_latency_factor", + B_CHANNEL_MODIFY_MAXCLIENTS = "b_channel_modify_maxclients", + B_CHANNEL_MODIFY_MAXFAMILYCLIENTS = "b_channel_modify_maxfamilyclients", + B_CHANNEL_MODIFY_SORTORDER = "b_channel_modify_sortorder", + B_CHANNEL_MODIFY_NEEDED_TALK_POWER = "b_channel_modify_needed_talk_power", + I_CHANNEL_MODIFY_POWER = "i_channel_modify_power", + I_CHANNEL_NEEDED_MODIFY_POWER = "i_channel_needed_modify_power", + B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED = "b_channel_modify_make_codec_encrypted", + B_CHANNEL_MODIFY_TEMP_DELETE_DELAY = "b_channel_modify_temp_delete_delay", + B_CHANNEL_DELETE_PERMANENT = "b_channel_delete_permanent", + B_CHANNEL_DELETE_SEMI_PERMANENT = "b_channel_delete_semi_permanent", + B_CHANNEL_DELETE_TEMPORARY = "b_channel_delete_temporary", + B_CHANNEL_DELETE_FLAG_FORCE = "b_channel_delete_flag_force", + I_CHANNEL_DELETE_POWER = "i_channel_delete_power", + B_CHANNEL_CONVERSATION_MESSAGE_DELETE = "b_channel_conversation_message_delete", + I_CHANNEL_NEEDED_DELETE_POWER = "i_channel_needed_delete_power", + B_CHANNEL_JOIN_PERMANENT = "b_channel_join_permanent", + B_CHANNEL_JOIN_SEMI_PERMANENT = "b_channel_join_semi_permanent", + B_CHANNEL_JOIN_TEMPORARY = "b_channel_join_temporary", + B_CHANNEL_JOIN_IGNORE_PASSWORD = "b_channel_join_ignore_password", + B_CHANNEL_JOIN_IGNORE_MAXCLIENTS = "b_channel_join_ignore_maxclients", + B_CHANNEL_IGNORE_VIEW_POWER = "b_channel_ignore_view_power", + I_CHANNEL_JOIN_POWER = "i_channel_join_power", + I_CHANNEL_NEEDED_JOIN_POWER = "i_channel_needed_join_power", + B_CHANNEL_IGNORE_JOIN_POWER = "b_channel_ignore_join_power", + B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER = "b_channel_ignore_description_view_power", + I_CHANNEL_VIEW_POWER = "i_channel_view_power", + I_CHANNEL_NEEDED_VIEW_POWER = "i_channel_needed_view_power", + I_CHANNEL_SUBSCRIBE_POWER = "i_channel_subscribe_power", + I_CHANNEL_NEEDED_SUBSCRIBE_POWER = "i_channel_needed_subscribe_power", + I_CHANNEL_DESCRIPTION_VIEW_POWER = "i_channel_description_view_power", + I_CHANNEL_NEEDED_DESCRIPTION_VIEW_POWER = "i_channel_needed_description_view_power", + I_ICON_ID = "i_icon_id", + I_MAX_ICON_FILESIZE = "i_max_icon_filesize", + I_MAX_PLAYLIST_SIZE = "i_max_playlist_size", + I_MAX_PLAYLISTS = "i_max_playlists", + B_ICON_MANAGE = "b_icon_manage", + B_GROUP_IS_PERMANENT = "b_group_is_permanent", + I_GROUP_AUTO_UPDATE_TYPE = "i_group_auto_update_type", + I_GROUP_AUTO_UPDATE_MAX_VALUE = "i_group_auto_update_max_value", + I_GROUP_SORT_ID = "i_group_sort_id", + I_GROUP_SHOW_NAME_IN_TREE = "i_group_show_name_in_tree", + B_VIRTUALSERVER_SERVERGROUP_CREATE = "b_virtualserver_servergroup_create", + B_VIRTUALSERVER_SERVERGROUP_LIST = "b_virtualserver_servergroup_list", + B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST = "b_virtualserver_servergroup_permission_list", + B_VIRTUALSERVER_SERVERGROUP_CLIENT_LIST = "b_virtualserver_servergroup_client_list", + B_VIRTUALSERVER_CHANNELGROUP_CREATE = "b_virtualserver_channelgroup_create", + B_VIRTUALSERVER_CHANNELGROUP_LIST = "b_virtualserver_channelgroup_list", + B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST = "b_virtualserver_channelgroup_permission_list", + B_VIRTUALSERVER_CHANNELGROUP_CLIENT_LIST = "b_virtualserver_channelgroup_client_list", + B_VIRTUALSERVER_CLIENT_PERMISSION_LIST = "b_virtualserver_client_permission_list", + B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST = "b_virtualserver_channel_permission_list", + B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST = "b_virtualserver_channelclient_permission_list", + B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST = "b_virtualserver_playlist_permission_list", + I_SERVER_GROUP_MODIFY_POWER = "i_server_group_modify_power", + I_SERVER_GROUP_NEEDED_MODIFY_POWER = "i_server_group_needed_modify_power", + I_SERVER_GROUP_MEMBER_ADD_POWER = "i_server_group_member_add_power", + I_SERVER_GROUP_SELF_ADD_POWER = "i_server_group_self_add_power", + I_SERVER_GROUP_NEEDED_MEMBER_ADD_POWER = "i_server_group_needed_member_add_power", + I_SERVER_GROUP_MEMBER_REMOVE_POWER = "i_server_group_member_remove_power", + I_SERVER_GROUP_SELF_REMOVE_POWER = "i_server_group_self_remove_power", + I_SERVER_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_server_group_needed_member_remove_power", + I_CHANNEL_GROUP_MODIFY_POWER = "i_channel_group_modify_power", + I_CHANNEL_GROUP_NEEDED_MODIFY_POWER = "i_channel_group_needed_modify_power", + I_CHANNEL_GROUP_MEMBER_ADD_POWER = "i_channel_group_member_add_power", + I_CHANNEL_GROUP_SELF_ADD_POWER = "i_channel_group_self_add_power", + I_CHANNEL_GROUP_NEEDED_MEMBER_ADD_POWER = "i_channel_group_needed_member_add_power", + I_CHANNEL_GROUP_MEMBER_REMOVE_POWER = "i_channel_group_member_remove_power", + I_CHANNEL_GROUP_SELF_REMOVE_POWER = "i_channel_group_self_remove_power", + I_CHANNEL_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_channel_group_needed_member_remove_power", + I_GROUP_MEMBER_ADD_POWER = "i_group_member_add_power", + I_GROUP_NEEDED_MEMBER_ADD_POWER = "i_group_needed_member_add_power", + I_GROUP_MEMBER_REMOVE_POWER = "i_group_member_remove_power", + I_GROUP_NEEDED_MEMBER_REMOVE_POWER = "i_group_needed_member_remove_power", + I_GROUP_MODIFY_POWER = "i_group_modify_power", + I_GROUP_NEEDED_MODIFY_POWER = "i_group_needed_modify_power", + I_PERMISSION_MODIFY_POWER = "i_permission_modify_power", + B_PERMISSION_MODIFY_POWER_IGNORE = "b_permission_modify_power_ignore", + B_VIRTUALSERVER_SERVERGROUP_DELETE = "b_virtualserver_servergroup_delete", + B_VIRTUALSERVER_CHANNELGROUP_DELETE = "b_virtualserver_channelgroup_delete", + I_CLIENT_PERMISSION_MODIFY_POWER = "i_client_permission_modify_power", + I_CLIENT_NEEDED_PERMISSION_MODIFY_POWER = "i_client_needed_permission_modify_power", + I_CLIENT_MAX_CLONES_UID = "i_client_max_clones_uid", + I_CLIENT_MAX_CLONES_IP = "i_client_max_clones_ip", + I_CLIENT_MAX_CLONES_HWID = "i_client_max_clones_hwid", + I_CLIENT_MAX_IDLETIME = "i_client_max_idletime", + I_CLIENT_MAX_AVATAR_FILESIZE = "i_client_max_avatar_filesize", + I_CLIENT_MAX_CHANNEL_SUBSCRIPTIONS = "i_client_max_channel_subscriptions", + I_CLIENT_MAX_CHANNELS = "i_client_max_channels", + I_CLIENT_MAX_TEMPORARY_CHANNELS = "i_client_max_temporary_channels", + I_CLIENT_MAX_SEMI_CHANNELS = "i_client_max_semi_channels", + I_CLIENT_MAX_PERMANENT_CHANNELS = "i_client_max_permanent_channels", + B_CLIENT_USE_PRIORITY_SPEAKER = "b_client_use_priority_speaker", + B_CLIENT_SKIP_CHANNELGROUP_PERMISSIONS = "b_client_skip_channelgroup_permissions", + B_CLIENT_FORCE_PUSH_TO_TALK = "b_client_force_push_to_talk", + B_CLIENT_IGNORE_BANS = "b_client_ignore_bans", + B_CLIENT_IGNORE_VPN = "b_client_ignore_vpn", + B_CLIENT_IGNORE_ANTIFLOOD = "b_client_ignore_antiflood", + B_CLIENT_ENFORCE_VALID_HWID = "b_client_enforce_valid_hwid", + B_CLIENT_ALLOW_INVALID_PACKET = "b_client_allow_invalid_packet", + B_CLIENT_ALLOW_INVALID_BADGES = "b_client_allow_invalid_badges", + B_CLIENT_ISSUE_CLIENT_QUERY_COMMAND = "b_client_issue_client_query_command", + B_CLIENT_USE_RESERVED_SLOT = "b_client_use_reserved_slot", + B_CLIENT_USE_CHANNEL_COMMANDER = "b_client_use_channel_commander", + B_CLIENT_REQUEST_TALKER = "b_client_request_talker", + B_CLIENT_AVATAR_DELETE_OTHER = "b_client_avatar_delete_other", + B_CLIENT_IS_STICKY = "b_client_is_sticky", + B_CLIENT_IGNORE_STICKY = "b_client_ignore_sticky", + B_CLIENT_MUSIC_CREATE_PERMANENT = "b_client_music_create_permanent", + B_CLIENT_MUSIC_CREATE_SEMI_PERMANENT = "b_client_music_create_semi_permanent", + B_CLIENT_MUSIC_CREATE_TEMPORARY = "b_client_music_create_temporary", + B_CLIENT_MUSIC_MODIFY_PERMANENT = "b_client_music_modify_permanent", + B_CLIENT_MUSIC_MODIFY_SEMI_PERMANENT = "b_client_music_modify_semi_permanent", + B_CLIENT_MUSIC_MODIFY_TEMPORARY = "b_client_music_modify_temporary", + I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME = "i_client_music_create_modify_max_volume", + I_CLIENT_MUSIC_LIMIT = "i_client_music_limit", + I_CLIENT_MUSIC_NEEDED_DELETE_POWER = "i_client_music_needed_delete_power", + I_CLIENT_MUSIC_DELETE_POWER = "i_client_music_delete_power", + I_CLIENT_MUSIC_PLAY_POWER = "i_client_music_play_power", + I_CLIENT_MUSIC_NEEDED_PLAY_POWER = "i_client_music_needed_play_power", + I_CLIENT_MUSIC_MODIFY_POWER = "i_client_music_modify_power", + I_CLIENT_MUSIC_NEEDED_MODIFY_POWER = "i_client_music_needed_modify_power", + I_CLIENT_MUSIC_RENAME_POWER = "i_client_music_rename_power", + I_CLIENT_MUSIC_NEEDED_RENAME_POWER = "i_client_music_needed_rename_power", + B_PLAYLIST_CREATE = "b_playlist_create", + I_PLAYLIST_VIEW_POWER = "i_playlist_view_power", + I_PLAYLIST_NEEDED_VIEW_POWER = "i_playlist_needed_view_power", + I_PLAYLIST_MODIFY_POWER = "i_playlist_modify_power", + I_PLAYLIST_NEEDED_MODIFY_POWER = "i_playlist_needed_modify_power", + I_PLAYLIST_PERMISSION_MODIFY_POWER = "i_playlist_permission_modify_power", + I_PLAYLIST_NEEDED_PERMISSION_MODIFY_POWER = "i_playlist_needed_permission_modify_power", + I_PLAYLIST_DELETE_POWER = "i_playlist_delete_power", + I_PLAYLIST_NEEDED_DELETE_POWER = "i_playlist_needed_delete_power", + I_PLAYLIST_SONG_ADD_POWER = "i_playlist_song_add_power", + I_PLAYLIST_SONG_NEEDED_ADD_POWER = "i_playlist_song_needed_add_power", + I_PLAYLIST_SONG_REMOVE_POWER = "i_playlist_song_remove_power", + I_PLAYLIST_SONG_NEEDED_REMOVE_POWER = "i_playlist_song_needed_remove_power", + B_CLIENT_INFO_VIEW = "b_client_info_view", + B_CLIENT_PERMISSIONOVERVIEW_VIEW = "b_client_permissionoverview_view", + B_CLIENT_PERMISSIONOVERVIEW_OWN = "b_client_permissionoverview_own", + B_CLIENT_REMOTEADDRESS_VIEW = "b_client_remoteaddress_view", + I_CLIENT_SERVERQUERY_VIEW_POWER = "i_client_serverquery_view_power", + I_CLIENT_NEEDED_SERVERQUERY_VIEW_POWER = "i_client_needed_serverquery_view_power", + B_CLIENT_CUSTOM_INFO_VIEW = "b_client_custom_info_view", + B_CLIENT_MUSIC_CHANNEL_LIST = "b_client_music_channel_list", + B_CLIENT_MUSIC_SERVER_LIST = "b_client_music_server_list", + I_CLIENT_MUSIC_INFO = "i_client_music_info", + I_CLIENT_MUSIC_NEEDED_INFO = "i_client_music_needed_info", + I_CLIENT_KICK_FROM_SERVER_POWER = "i_client_kick_from_server_power", + I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER = "i_client_needed_kick_from_server_power", + I_CLIENT_KICK_FROM_CHANNEL_POWER = "i_client_kick_from_channel_power", + I_CLIENT_NEEDED_KICK_FROM_CHANNEL_POWER = "i_client_needed_kick_from_channel_power", + I_CLIENT_BAN_POWER = "i_client_ban_power", + I_CLIENT_NEEDED_BAN_POWER = "i_client_needed_ban_power", + I_CLIENT_MOVE_POWER = "i_client_move_power", + I_CLIENT_NEEDED_MOVE_POWER = "i_client_needed_move_power", + I_CLIENT_COMPLAIN_POWER = "i_client_complain_power", + I_CLIENT_NEEDED_COMPLAIN_POWER = "i_client_needed_complain_power", + B_CLIENT_COMPLAIN_LIST = "b_client_complain_list", + B_CLIENT_COMPLAIN_DELETE_OWN = "b_client_complain_delete_own", + B_CLIENT_COMPLAIN_DELETE = "b_client_complain_delete", + B_CLIENT_BAN_LIST = "b_client_ban_list", + B_CLIENT_BAN_LIST_GLOBAL = "b_client_ban_list_global", + B_CLIENT_BAN_TRIGGER_LIST = "b_client_ban_trigger_list", + B_CLIENT_BAN_CREATE = "b_client_ban_create", + B_CLIENT_BAN_CREATE_GLOBAL = "b_client_ban_create_global", + B_CLIENT_BAN_NAME = "b_client_ban_name", + B_CLIENT_BAN_IP = "b_client_ban_ip", + B_CLIENT_BAN_HWID = "b_client_ban_hwid", + B_CLIENT_BAN_EDIT = "b_client_ban_edit", + B_CLIENT_BAN_EDIT_GLOBAL = "b_client_ban_edit_global", + B_CLIENT_BAN_DELETE_OWN = "b_client_ban_delete_own", + B_CLIENT_BAN_DELETE = "b_client_ban_delete", + B_CLIENT_BAN_DELETE_OWN_GLOBAL = "b_client_ban_delete_own_global", + B_CLIENT_BAN_DELETE_GLOBAL = "b_client_ban_delete_global", + I_CLIENT_BAN_MAX_BANTIME = "i_client_ban_max_bantime", + I_CLIENT_PRIVATE_TEXTMESSAGE_POWER = "i_client_private_textmessage_power", + I_CLIENT_NEEDED_PRIVATE_TEXTMESSAGE_POWER = "i_client_needed_private_textmessage_power", + B_CLIENT_EVEN_TEXTMESSAGE_SEND = "b_client_even_textmessage_send", + B_CLIENT_SERVER_TEXTMESSAGE_SEND = "b_client_server_textmessage_send", + B_CLIENT_CHANNEL_TEXTMESSAGE_SEND = "b_client_channel_textmessage_send", + B_CLIENT_OFFLINE_TEXTMESSAGE_SEND = "b_client_offline_textmessage_send", + I_CLIENT_TALK_POWER = "i_client_talk_power", + I_CLIENT_NEEDED_TALK_POWER = "i_client_needed_talk_power", + I_CLIENT_POKE_POWER = "i_client_poke_power", + I_CLIENT_NEEDED_POKE_POWER = "i_client_needed_poke_power", + B_CLIENT_SET_FLAG_TALKER = "b_client_set_flag_talker", + I_CLIENT_WHISPER_POWER = "i_client_whisper_power", + I_CLIENT_NEEDED_WHISPER_POWER = "i_client_needed_whisper_power", + B_CLIENT_MODIFY_DESCRIPTION = "b_client_modify_description", + B_CLIENT_MODIFY_OWN_DESCRIPTION = "b_client_modify_own_description", + B_CLIENT_USE_BBCODE_ANY = "b_client_use_bbcode_any", + B_CLIENT_USE_BBCODE_URL = "b_client_use_bbcode_url", + B_CLIENT_USE_BBCODE_IMAGE = "b_client_use_bbcode_image", + B_CLIENT_MODIFY_DBPROPERTIES = "b_client_modify_dbproperties", + B_CLIENT_DELETE_DBPROPERTIES = "b_client_delete_dbproperties", + B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN = "b_client_create_modify_serverquery_login", + B_CLIENT_QUERY_CREATE = "b_client_query_create", + B_CLIENT_QUERY_LIST = "b_client_query_list", + B_CLIENT_QUERY_LIST_OWN = "b_client_query_list_own", + B_CLIENT_QUERY_RENAME = "b_client_query_rename", + B_CLIENT_QUERY_RENAME_OWN = "b_client_query_rename_own", + B_CLIENT_QUERY_CHANGE_PASSWORD = "b_client_query_change_password", + B_CLIENT_QUERY_CHANGE_OWN_PASSWORD = "b_client_query_change_own_password", + B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL = "b_client_query_change_password_global", + B_CLIENT_QUERY_DELETE = "b_client_query_delete", + B_CLIENT_QUERY_DELETE_OWN = "b_client_query_delete_own", + B_FT_IGNORE_PASSWORD = "b_ft_ignore_password", + B_FT_TRANSFER_LIST = "b_ft_transfer_list", + I_FT_FILE_UPLOAD_POWER = "i_ft_file_upload_power", + I_FT_NEEDED_FILE_UPLOAD_POWER = "i_ft_needed_file_upload_power", + I_FT_FILE_DOWNLOAD_POWER = "i_ft_file_download_power", + I_FT_NEEDED_FILE_DOWNLOAD_POWER = "i_ft_needed_file_download_power", + I_FT_FILE_DELETE_POWER = "i_ft_file_delete_power", + I_FT_NEEDED_FILE_DELETE_POWER = "i_ft_needed_file_delete_power", + I_FT_FILE_RENAME_POWER = "i_ft_file_rename_power", + I_FT_NEEDED_FILE_RENAME_POWER = "i_ft_needed_file_rename_power", + I_FT_FILE_BROWSE_POWER = "i_ft_file_browse_power", + I_FT_NEEDED_FILE_BROWSE_POWER = "i_ft_needed_file_browse_power", + I_FT_DIRECTORY_CREATE_POWER = "i_ft_directory_create_power", + I_FT_NEEDED_DIRECTORY_CREATE_POWER = "i_ft_needed_directory_create_power", + I_FT_QUOTA_MB_DOWNLOAD_PER_CLIENT = "i_ft_quota_mb_download_per_client", + I_FT_QUOTA_MB_UPLOAD_PER_CLIENT = "i_ft_quota_mb_upload_per_client" +} +export default PermissionType; \ No newline at end of file diff --git a/shared/js/profiles/ConnectionProfile.ts b/shared/js/profiles/ConnectionProfile.ts index f30cbe54..b09649f2 100644 --- a/shared/js/profiles/ConnectionProfile.ts +++ b/shared/js/profiles/ConnectionProfile.ts @@ -1,251 +1,257 @@ -namespace profiles { - export class ConnectionProfile { - id: string; +import {decode_identity, IdentitifyType, Identity} from "tc-shared/profiles/Identity"; +import {guid} from "tc-shared/crypto/uid"; +import {TeaForumIdentity} from "tc-shared/profiles/identities/TeaForumIdentity"; +import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; +import {createErrorModal} from "tc-shared/ui/elements/Modal"; +import {formatMessage} from "tc-shared/ui/frames/chat"; - profile_name: string; - default_username: string; - default_password: string; +export class ConnectionProfile { + id: string; - selected_identity_type: string = "unset"; - identities: { [key: string]: identities.Identity } = {}; + profile_name: string; + default_username: string; + default_password: string; - constructor(id: string) { - this.id = id; - } + selected_identity_type: string = "unset"; + identities: { [key: string]: Identity } = {}; - connect_username(): string { - if (this.default_username && this.default_username !== "Another TeaSpeak user") - return this.default_username; + constructor(id: string) { + this.id = id; + } - let selected = this.selected_identity(); - let name = selected ? selected.fallback_name() : undefined; - return name || "Another TeaSpeak user"; - } + connect_username(): string { + if (this.default_username && this.default_username !== "Another TeaSpeak user") + return this.default_username; - selected_identity(current_type?: identities.IdentitifyType): identities.Identity { - if (!current_type) - current_type = this.selected_type(); + let selected = this.selected_identity(); + let name = selected ? selected.fallback_name() : undefined; + return name || "Another TeaSpeak user"; + } - if (current_type === undefined) - return undefined; - - if (current_type == identities.IdentitifyType.TEAFORO) { - return identities.static_forum_identity(); - } else if (current_type == identities.IdentitifyType.TEAMSPEAK || current_type == identities.IdentitifyType.NICKNAME) { - return this.identities[identities.IdentitifyType[current_type].toLowerCase()]; - } + selected_identity(current_type?: IdentitifyType): Identity { + if (!current_type) + current_type = this.selected_type(); + if (current_type === undefined) return undefined; + + if (current_type == IdentitifyType.TEAFORO) { + return TeaForumIdentity.identity(); + } else if (current_type == IdentitifyType.TEAMSPEAK || current_type == IdentitifyType.NICKNAME) { + return this.identities[IdentitifyType[current_type].toLowerCase()]; } - selected_type?(): identities.IdentitifyType { - return this.selected_identity_type ? identities.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; - } - - set_identity(type: identities.IdentitifyType, identity: identities.Identity) { - this.identities[identities.IdentitifyType[type].toLowerCase()] = identity; - } - - spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { - const identity = this.selected_identity(); - if (!identity) - return undefined; - return identity.spawn_identity_handshake_handler(connection); - } - - encode?(): string { - const identity_data = {}; - for (const key in this.identities) - if (this.identities[key]) - identity_data[key] = this.identities[key].encode(); - - return JSON.stringify({ - version: 1, - username: this.default_username, - password: this.default_password, - profile_name: this.profile_name, - identity_type: this.selected_identity_type, - identity_data: identity_data, - id: this.id - }); - } - - valid(): boolean { - const identity = this.selected_identity(); - if (!identity || !identity.valid()) return false; - - return true; - } + return undefined; } - async function decode_profile(data): Promise { - data = JSON.parse(data); - if (data.version !== 1) - return "invalid version"; - - const result: ConnectionProfile = new ConnectionProfile(data.id); - result.default_username = data.username; - result.default_password = data.password; - result.profile_name = data.profile_name; - result.selected_identity_type = (data.identity_type || "").toLowerCase(); - - if (data.identity_data) { - for (const key in data.identity_data) { - const type = identities.IdentitifyType[key.toUpperCase() as string]; - const _data = data.identity_data[key]; - if (type == undefined) continue; - - const identity = await identities.decode_identity(type, _data); - if (identity == undefined) continue; - - result.identities[key.toLowerCase()] = identity; - } - } - - return result; + selected_type?(): IdentitifyType { + return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; } - interface ProfilesData { - version: number; - profiles: string[]; + set_identity(type: IdentitifyType, identity: Identity) { + this.identities[IdentitifyType[type].toLowerCase()] = identity; } - let available_profiles: ConnectionProfile[] = []; - - export async function load() { - available_profiles = []; - - const profiles_json = localStorage.getItem("profiles"); - let profiles_data: ProfilesData = (() => { - try { - return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; - } catch (error) { - debugger; - console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); - createErrorModal(tr("Profile data invalid"), MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); - return {version: 0}; - } - })(); - - if (profiles_data.version === 0) { - profiles_data = { - version: 1, - profiles: [] - }; - } - if (profiles_data.version == 1) { - for (const profile_data of profiles_data.profiles) { - const profile = await decode_profile(profile_data); - if (typeof (profile) === 'string') { - console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); - continue; - } - available_profiles.push(profile); - } - } - - if (!find_profile("default")) { //Create a default profile and teaforo profile - { - const profile = create_new_profile("default", "default"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "Default Profile"; - - /* generate default identity */ - try { - const identity = await identities.TeaSpeakIdentity.generate_new(); - let active = true; - setTimeout(() => { - active = false; - }, 1000); - await identity.improve_level(8, 1, () => active); - profile.set_identity(identities.IdentitifyType.TEAMSPEAK, identity); - profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAMSPEAK]; - } catch (error) { - createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); - } - } - - { /* forum identity (works only when connected to the forum) */ - const profile = create_new_profile("TeaSpeak Forum", "teaforo"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "TeaSpeak Forum profile"; - - profile.set_identity(identities.IdentitifyType.TEAFORO, identities.static_forum_identity()); - profile.selected_identity_type = identities.IdentitifyType[identities.IdentitifyType.TEAFORO]; - } - - save(); - } + spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler { + const identity = this.selected_identity(); + if (!identity) + return undefined; + return identity.spawn_identity_handshake_handler(connection); } - export function create_new_profile(name: string, id?: string): ConnectionProfile { - const profile = new ConnectionProfile(id || guid()); - profile.profile_name = name; - profile.default_username = ""; - available_profiles.push(profile); - return profile; - } + encode?(): string { + const identity_data = {}; + for (const key in this.identities) + if (this.identities[key]) + identity_data[key] = this.identities[key].encode(); - let _requires_save = false; - - export function save() { - const profiles: string[] = []; - for (const profile of available_profiles) - profiles.push(profile.encode()); - - const data = JSON.stringify({ + return JSON.stringify({ version: 1, - profiles: profiles + username: this.default_username, + password: this.default_password, + profile_name: this.profile_name, + identity_type: this.selected_identity_type, + identity_data: identity_data, + id: this.id }); - localStorage.setItem("profiles", data); } - export function mark_need_save() { - _requires_save = true; + valid(): boolean { + const identity = this.selected_identity(); + + return !!identity && identity.valid(); } +} - export function requires_save(): boolean { - return _requires_save; - } +async function decode_profile(data): Promise { + data = JSON.parse(data); + if (data.version !== 1) + return "invalid version"; - export function profiles(): ConnectionProfile[] { - return available_profiles; - } + const result: ConnectionProfile = new ConnectionProfile(data.id); + result.default_username = data.username; + result.default_password = data.password; + result.profile_name = data.profile_name; + result.selected_identity_type = (data.identity_type || "").toLowerCase(); - export function find_profile(id: string): ConnectionProfile | undefined { - for (const profile of profiles()) - if (profile.id == id) - return profile; + if (data.identity_data) { + for (const key of Object.keys(data.identity_data)) { + const type = IdentitifyType[key.toUpperCase() as string]; + const _data = data.identity_data[key]; + if (type == undefined) continue; - return undefined; - } + const identity = await decode_identity(type, _data); + if (identity == undefined) continue; - export function find_profile_by_name(name: string): ConnectionProfile | undefined { - name = name.toLowerCase(); - for (const profile of profiles()) - if ((profile.profile_name || "").toLowerCase() == name) - return profile; - - return undefined; - } - - - export function default_profile(): ConnectionProfile { - return find_profile("default"); - } - - export function set_default_profile(profile: ConnectionProfile) { - const old_default = default_profile(); - if (old_default && old_default != profile) { - old_default.id = guid(); + result.identities[key.toLowerCase()] = identity; } - profile.id = "default"; - return old_default; } - export function delete_profile(profile: ConnectionProfile) { - available_profiles.remove(profile); + return result; +} + +interface ProfilesData { + version: number; + profiles: string[]; +} + +let available_profiles: ConnectionProfile[] = []; + +export async function load() { + available_profiles = []; + + const profiles_json = localStorage.getItem("profiles"); + let profiles_data: ProfilesData = (() => { + try { + return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any; + } catch (error) { + debugger; + console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); + createErrorModal(tr("Profile data invalid"), formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); + return {version: 0}; + } + })(); + + if (profiles_data.version === 0) { + profiles_data = { + version: 1, + profiles: [] + }; } + if (profiles_data.version == 1) { + for (const profile_data of profiles_data.profiles) { + const profile = await decode_profile(profile_data); + if (typeof (profile) === 'string') { + console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); + continue; + } + available_profiles.push(profile); + } + } + + if (!find_profile("default")) { //Create a default profile and teaforo profile + { + const profile = create_new_profile("default", "default"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "Default Profile"; + + /* generate default identity */ + try { + const identity = await TeaSpeakIdentity.generate_new(); + let active = true; + setTimeout(() => { + active = false; + }, 1000); + await identity.improve_level(8, 1, () => active); + profile.set_identity(IdentitifyType.TEAMSPEAK, identity); + profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAMSPEAK]; + } catch (error) { + createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); + } + } + + { /* forum identity (works only when connected to the forum) */ + const profile = create_new_profile("TeaSpeak Forum", "teaforo"); + profile.default_password = ""; + profile.default_username = ""; + profile.profile_name = "TeaSpeak Forum profile"; + + profile.set_identity(IdentitifyType.TEAFORO, TeaForumIdentity.identity()); + profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO]; + } + + save(); + } +} + +export function create_new_profile(name: string, id?: string): ConnectionProfile { + const profile = new ConnectionProfile(id || guid()); + profile.profile_name = name; + profile.default_username = ""; + available_profiles.push(profile); + return profile; +} + +let _requires_save = false; + +export function save() { + const profiles: string[] = []; + for (const profile of available_profiles) + profiles.push(profile.encode()); + + const data = JSON.stringify({ + version: 1, + profiles: profiles + }); + localStorage.setItem("profiles", data); +} + +export function mark_need_save() { + _requires_save = true; +} + +export function requires_save(): boolean { + return _requires_save; +} + +export function profiles(): ConnectionProfile[] { + return available_profiles; +} + +export function find_profile(id: string): ConnectionProfile | undefined { + for (const profile of profiles()) + if (profile.id == id) + return profile; + + return undefined; +} + +export function find_profile_by_name(name: string): ConnectionProfile | undefined { + name = name.toLowerCase(); + for (const profile of profiles()) + if ((profile.profile_name || "").toLowerCase() == name) + return profile; + + return undefined; +} + + +export function default_profile(): ConnectionProfile { + return find_profile("default"); +} + +export function set_default_profile(profile: ConnectionProfile) { + const old_default = default_profile(); + if (old_default && old_default != profile) { + old_default.id = guid(); + } + profile.id = "default"; + return old_default; +} + +export function delete_profile(profile: ConnectionProfile) { + available_profiles.remove(profile); } \ No newline at end of file diff --git a/shared/js/profiles/Identity.ts b/shared/js/profiles/Identity.ts index c658f46d..9f79adef 100644 --- a/shared/js/profiles/Identity.ts +++ b/shared/js/profiles/Identity.ts @@ -1,110 +1,119 @@ -namespace profiles.identities { - export enum IdentitifyType { - TEAFORO, - TEAMSPEAK, - NICKNAME +import {AbstractServerConnection, ServerCommand} from "tc-shared/connection/ConnectionBase"; +import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; +import {AbstractCommandHandler} from "tc-shared/connection/AbstractCommandHandler"; + +export enum IdentitifyType { + TEAFORO, + TEAMSPEAK, + NICKNAME +} + +export interface Identity { + fallback_name(): string | undefined ; + uid() : string; + type() : IdentitifyType; + + valid() : boolean; + + encode?() : string; + decode(data: string) : Promise; + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler; +} + +/* avoid circular dependencies here */ +export async function decode_identity(type: IdentitifyType, data: string) : Promise { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + const nidentity = require("tc-shared/profiles/identities/NameIdentity"); + identity = new nidentity.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + const fidentity = require("tc-shared/profiles/identities/TeaForumIdentity"); + identity = new fidentity.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + const tidentity = require("tc-shared/profiles/identities/TeamSpeakIdentity"); + identity = new tidentity.TeaSpeakIdentity(undefined, undefined); + break; + } + if(!identity) + return undefined; + + try { + await identity.decode(data) + } catch(error) { + /* todo better error handling! */ + console.error(error); + return undefined; } - export interface Identity { - fallback_name(): string | undefined ; - uid() : string; - type() : IdentitifyType; + return identity; +} - valid() : boolean; +export function create_identity(type: IdentitifyType) { + let identity: Identity; + switch (type) { + case IdentitifyType.NICKNAME: + const nidentity = require("tc-shared/profiles/identities/NameIdentity"); + identity = new nidentity.NameIdentity(); + break; + case IdentitifyType.TEAFORO: + const fidentity = require("tc-shared/profiles/identities/TeaForumIdentity"); + identity = new fidentity.TeaForumIdentity(undefined); + break; + case IdentitifyType.TEAMSPEAK: + const tidentity = require("tc-shared/profiles/identities/TeamSpeakIdentity"); + identity = new tidentity.TeaSpeakIdentity(undefined, undefined); + break; + } + return identity; +} - encode?() : string; - decode(data: string) : Promise; +export class HandshakeCommandHandler extends AbstractCommandHandler { + readonly handle: T; - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler; + constructor(connection: AbstractServerConnection, handle: T) { + super(connection); + this.handle = handle; } - export async function decode_identity(type: IdentitifyType, data: string) : Promise { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - if(!identity) - return undefined; - try { - await identity.decode(data) - } catch(error) { - /* todo better error handling! */ - console.error(error); - return undefined; + handle_command(command: ServerCommand): boolean { + if($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if(command.command == "error") { + return false; + } else { + console.warn(tr("Received unknown command while handshaking (%o)"), command); } + return true; + } +} - return identity; +export abstract class AbstractHandshakeIdentityHandler implements HandshakeIdentityHandler { + connection: AbstractServerConnection; + + protected callbacks: ((success: boolean, message?: string) => any)[] = []; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; } - export function create_identity(type: IdentitifyType) { - let identity: Identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeaSpeakIdentity(undefined, undefined); - break; - } - return identity; + register_callback(callback: (success: boolean, message?: string) => any) { + this.callbacks.push(callback); } - export class HandshakeCommandHandler extends connection.AbstractCommandHandler { - readonly handle: T; + abstract start_handshake(); - constructor(connection: connection.AbstractServerConnection, handle: T) { - super(connection); - this.handle = handle; - } - - - handle_command(command: connection.ServerCommand): boolean { - if($.isFunction(this[command.command])) - this[command.command](command.arguments); - else if(command.command == "error") { - return false; - } else { - console.warn(tr("Received unknown command while handshaking (%o)"), command); - } - return true; - } + protected trigger_success() { + for(const callback of this.callbacks) + callback(true); } - export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { - connection: connection.AbstractServerConnection; - - protected callbacks: ((success: boolean, message?: string) => any)[] = []; - - protected constructor(connection: connection.AbstractServerConnection) { - this.connection = connection; - } - - register_callback(callback: (success: boolean, message?: string) => any) { - this.callbacks.push(callback); - } - - abstract start_handshake(); - - protected trigger_success() { - for(const callback of this.callbacks) - callback(true); - } - - protected trigger_fail(message: string) { - for(const callback of this.callbacks) - callback(false, message); - } + protected trigger_fail(message: string) { + for(const callback of this.callbacks) + callback(false, message); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts index 51cbefc7..c0a14aa2 100644 --- a/shared/js/profiles/identities/NameIdentity.ts +++ b/shared/js/profiles/identities/NameIdentity.ts @@ -1,88 +1,97 @@ -/// +import { + AbstractHandshakeIdentityHandler, + HandshakeCommandHandler, + IdentitifyType, + Identity +} from "tc-shared/profiles/Identity"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; -namespace profiles.identities { - class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: NameIdentity; - handler: HandshakeCommandHandler; +console.error(AbstractHandshakeIdentityHandler); +class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: NameIdentity; + handler: HandshakeCommandHandler; - constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) { - super(connection); - this.identity = identity; + constructor(connection: AbstractServerConnection, identity: NameIdentity) { + super(connection); + this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); - } - - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - client_nickname: this.identity.name() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }).then(() => this.trigger_success()); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); } - export class NameIdentity implements Identity { - private _name: string; + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + client_nickname: this.identity.name() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }).then(() => this.trigger_success()); + } - constructor(name?: string) { - this._name = name; - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } - set_name(name: string) { this._name = name; } + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} - name() : string { return this._name; } +export class NameIdentity implements Identity { + private _name: string; - fallback_name(): string | undefined { - return this._name; - } + constructor(name?: string) { + this._name = name; + } - uid(): string { - return btoa(this._name); //FIXME hash! - } + set_name(name: string) { this._name = name; } - type(): IdentitifyType { - return IdentitifyType.NICKNAME; - } + name() : string { return this._name; } - valid(): boolean { - return this._name != undefined && this._name.length >= 5; - } + fallback_name(): string | undefined { + return this._name; + } - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; + uid(): string { + return btoa(this._name); //FIXME hash! + } - this._name = data["name"]; - return; - } + type(): IdentitifyType { + return IdentitifyType.NICKNAME; + } - encode?() : string { - return JSON.stringify({ - version: 1, - name: this._name - }); - } + valid(): boolean { + return this._name != undefined && this._name.length >= 5; + } - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { - return new NameHandshakeHandler(connection, this); - } + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; + + this._name = data["name"]; + return; + } + + encode?() : string { + return JSON.stringify({ + version: 1, + name: this._name + }); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { + return new NameHandshakeHandler(connection, this); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeaForumIdentity.ts b/shared/js/profiles/identities/TeaForumIdentity.ts index 2f44e31e..0e1ebb51 100644 --- a/shared/js/profiles/identities/TeaForumIdentity.ts +++ b/shared/js/profiles/identities/TeaForumIdentity.ts @@ -1,122 +1,135 @@ -/// +import { + AbstractHandshakeIdentityHandler, + HandshakeCommandHandler, + IdentitifyType, + Identity +} from "tc-shared/profiles/Identity"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; +import * as forum from "./teaspeak-forum"; -namespace profiles.identities { - class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { - readonly identity: TeaForumIdentity; - handler: HandshakeCommandHandler; +class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { + readonly identity: TeaForumIdentity; + handler: HandshakeCommandHandler; - constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); - } - - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - data: this.identity.data().data_json() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - - - private handle_proof(json) { - this.connection.send_command("handshakeindentityproof", { - proof: this.identity.data().data_sign() - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + constructor(connection: AbstractServerConnection, identity: TeaForumIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } - export class TeaForumIdentity implements Identity { - private readonly identity_data: forum.Data; + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + data: this.identity.data().data_json() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); - valid() : boolean { - return !!this.identity_data && !this.identity_data.is_expired(); - } - - constructor(data: forum.Data) { - this.identity_data = data; - } - - data() : forum.Data { - return this.identity_data; - } - - decode(data) : Promise { - data = JSON.parse(data); - if(data.version !== 1) - throw "invalid version"; - - return; - } - - encode() : string { - return JSON.stringify({ - version: 1 - }); - } - - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { - return new TeaForumHandshakeHandler(connection, this); - } - - fallback_name(): string | undefined { - return this.identity_data ? this.identity_data.name() : undefined; - } - - type(): profiles.identities.IdentitifyType { - return IdentitifyType.TEAFORO; - } - - uid(): string { - //FIXME: Real UID! - return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); - } + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); } - let static_identity: TeaForumIdentity; - export function set_static_identity(identity: TeaForumIdentity) { - static_identity = identity; + private handle_proof(json) { + this.connection.send_command("handshakeindentityproof", { + proof: this.identity.data().data_sign() + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); } - export function update_forum() { - if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { - static_identity = new TeaForumIdentity(forum.data()); - } else { - static_identity = undefined; - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); } - export function valid_static_forum_identity() : boolean { - return static_identity && static_identity.valid(); + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} + +export class TeaForumIdentity implements Identity { + private readonly identity_data: forum.Data; + + valid() : boolean { + return !!this.identity_data && !this.identity_data.is_expired(); } - export function static_forum_identity() : TeaForumIdentity | undefined { + constructor(data: forum.Data) { + this.identity_data = data; + } + + data() { + return this.identity_data; + } + + decode(data) : Promise { + data = JSON.parse(data); + if(data.version !== 1) + throw "invalid version"; + + return; + } + + encode() : string { + return JSON.stringify({ + version: 1 + }); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection) : HandshakeIdentityHandler { + return new TeaForumHandshakeHandler(connection, this); + } + + fallback_name(): string | undefined { + return this.identity_data ? this.identity_data.name() : undefined; + } + + type(): IdentitifyType { + return IdentitifyType.TEAFORO; + } + + uid(): string { + //FIXME: Real UID! + return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); + } + + public static identity() { return static_identity; } +} + +let static_identity: TeaForumIdentity; + +export function set_static_identity(identity: TeaForumIdentity) { + static_identity = identity; +} + +export function update_forum() { + if(forum.logged_in() && (!static_identity || static_identity.data() !== forum.data())) { + static_identity = new TeaForumIdentity(forum.data()); + } else { + static_identity = undefined; + } +} + +export function valid_static_forum_identity() : boolean { + return static_identity && static_identity.valid(); +} + +export function static_forum_identity() : TeaForumIdentity | undefined { + return static_identity; } \ No newline at end of file diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts index b7c5be9c..9c70f1f0 100644 --- a/shared/js/profiles/identities/TeamSpeakIdentity.ts +++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts @@ -1,88 +1,121 @@ -/// +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import * as asn1 from "tc-shared/crypto/asn1"; +import * as sha from "tc-shared/crypto/sha"; -namespace profiles.identities { - export namespace CryptoHelper { - export function base64_url_encode(str){ - return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); +import { + AbstractHandshakeIdentityHandler, + HandshakeCommandHandler, + IdentitifyType, + Identity +} from "tc-shared/profiles/Identity"; +import {settings} from "tc-shared/settings"; +import {arrayBufferBase64, base64_encode_ab, str2ab8} from "tc-shared/utils/buffers"; +import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; + +export namespace CryptoHelper { + export function base64_url_encode(str){ + return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); + } + + export function base64_url_decode(str: string, pad?: boolean){ + if(typeof(pad) === 'undefined' || pad) + str = (str + '===').slice(0, str.length + (str.length % 4)); + return str.replace(/-/g, '+').replace(/_/g, '/'); + } + + export function arraybuffer_to_string(buf) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } + + export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { + /* + Tomcrypt public key export: + if (type == PK_PRIVATE) { + flags[0] = 1; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_INTEGER, 1UL, key->k, + LTC_ASN1_EOL, 0UL, NULL); + } else { + flags[0] = 0; + err = der_encode_sequence_multi(out, outlen, + LTC_ASN1_BIT_STRING, 1UL, flags, + LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, + LTC_ASN1_INTEGER, 1UL, key->pubkey.x, + LTC_ASN1_INTEGER, 1UL, key->pubkey.y, + LTC_ASN1_EOL, 0UL, NULL); + } + + */ + + const key_data = await crypto.subtle.exportKey("jwk", crypto_key); + + let index = 0; + const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ + const buffer = new Uint8Array(length); /* fixed ASN1 length */ + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* the flags bit string */ + buffer[index++] = 0x03; /* type */ + buffer[index++] = 0x02; /* length */ + buffer[index++] = 0x07; /* data */ + buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ + } + { /* key size (const 32 for P-256) */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x01; /* length */ + buffer[index++] = 0x20; + } + try { /* Public kex X */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.x, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse x coordinate (invalid base64)"; + throw error; } - export function base64_url_decode(str: string, pad?: boolean){ - if(typeof(pad) === 'undefined' || pad) - str = (str + '===').slice(0, str.length + (str.length % 4)); - return str.replace(/-/g, '+').replace(/_/g, '/'); + try { /* Public kex Y */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + const raw = atob(base64_url_decode(key_data.y, false)); + if(raw.charCodeAt(0) > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = raw.charCodeAt(i); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse y coordinate (invalid base64)"; + throw error; } - export function arraybuffer_to_string(buf) { - return String.fromCharCode.apply(null, new Uint16Array(buf)); - } - - export async function export_ecc_key(crypto_key: CryptoKey, public_key: boolean) { - /* - Tomcrypt public key export: - if (type == PK_PRIVATE) { - flags[0] = 1; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_INTEGER, 1UL, key->k, - LTC_ASN1_EOL, 0UL, NULL); - } else { - flags[0] = 0; - err = der_encode_sequence_multi(out, outlen, - LTC_ASN1_BIT_STRING, 1UL, flags, - LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, - LTC_ASN1_INTEGER, 1UL, key->pubkey.x, - LTC_ASN1_INTEGER, 1UL, key->pubkey.y, - LTC_ASN1_EOL, 0UL, NULL); - } - - */ - - const key_data = await crypto.subtle.exportKey("jwk", crypto_key); - - let index = 0; - const length = public_key ? 79 : 114; /* max lengths! Depends on the padding could be less */ - const buffer = new Uint8Array(length); /* fixed ASN1 length */ - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* the flags bit string */ - buffer[index++] = 0x03; /* type */ - buffer[index++] = 0x02; /* length */ - buffer[index++] = 0x07; /* data */ - buffer[index++] = public_key ? 0x00 : 0x80; /* flag 1 or 0 (1 = private key)*/ - } - { /* key size (const 32 for P-256) */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x01; /* length */ - buffer[index++] = 0x20; - } - try { /* Public kex X */ + if(!public_key) { + try { /* Public kex K */ buffer[index++] = 0x02; /* type */ buffer[index++] = 0x20; /* length */ - const raw = atob(base64_url_decode(key_data.x, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse x coordinate (invalid base64)"; - throw error; - } - - try { /* Public kex Y */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.y, false)); + const raw = atob(base64_url_decode(key_data.d, false)); if(raw.charCodeAt(0) > 0x7F) { buffer[index - 1] += 1; buffer[index++] = 0; @@ -95,777 +128,751 @@ namespace profiles.identities { throw "failed to parse y coordinate (invalid base64)"; throw error; } - - if(!public_key) { - try { /* Public kex K */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - const raw = atob(base64_url_decode(key_data.d, false)); - if(raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse y coordinate (invalid base64)"; - throw error; - } - } - - buffer[1] = index - 2; /* set the final sequence length */ - - return base64_encode_ab(buffer.buffer.slice(0, index)); } - const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; - function c_strlen(buffer: Uint8Array, offset: number) : number { - let index = 0; - while(index + offset < buffer.length && buffer[index + offset] != 0) - index++; - return index; - } + buffer[1] = index - 2; /* set the final sequence length */ - export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { - /* buffer could contains a zero! */ - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - return arraybuffer_to_string(buffer); - } - - export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { - const length = Math.min(buffer.length, 100); - for(let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - - const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for(let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - - return base64_encode_ab(buffer); - } - - /** - * @param buffer base64 encoded ASN.1 string - */ - export function decode_tomcrypt_key(buffer: string) { - let decoded; - - try { - decoded = asn1.decode(atob(buffer)); - } catch(error) { - if(error instanceof DOMException) - throw "failed to parse key buffer (invalid base64)"; - throw error; - } - - let {x, y, k} = { - x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), - y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), - k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) - }; - - if(x.length > 32) { - if(x.charCodeAt(0) != 0) - throw "Invalid X coordinate! (Too long)"; - x = x.substr(1); - } - - if(y.length > 32) { - if(y.charCodeAt(0) != 0) - throw "Invalid Y coordinate! (Too long)"; - y = y.substr(1); - } - - if(k.length > 32) { - if(k.charCodeAt(0) != 0) - throw "Invalid private coordinate! (Too long)"; - k = k.substr(1); - } - - /* - console.log("Key x: %s (%d)", btoa(x), x.length); - console.log("Key y: %s (%d)", btoa(y), y.length); - console.log("Key k: %s (%d)", btoa(k), k.length); - */ - return { - crv: "P-256", - d: base64_url_encode(btoa(k)), - x: base64_url_encode(btoa(x)), - y: base64_url_encode(btoa(y)), - - ext: true, - key_ops:["deriveKey", "sign"], - kty:"EC", - }; - } + return base64_encode_ab(buffer.buffer.slice(0, index)); } - class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { - identity: TeaSpeakIdentity; - handler: HandshakeCommandHandler; + const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; + function c_strlen(buffer: Uint8Array, offset: number) : number { + let index = 0; + while(index + offset < buffer.length && buffer[index + offset] != 0) + index++; + return index; + } - constructor(connection: connection.AbstractServerConnection, identity: TeaSpeakIdentity) { - super(connection); - this.identity = identity; - this.handler = new HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + export async function decrypt_ts_identity(buffer: Uint8Array) : Promise { + /* buffer could contains a zero! */ + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + return arraybuffer_to_string(buffer); + } + + export async function encrypt_ts_identity(buffer: Uint8Array) : Promise { + const length = Math.min(buffer.length, 100); + for(let i = 0; i < length; i++) + buffer[i] ^= crypt_key.charCodeAt(i); + + const hash = new Uint8Array(await sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); + for(let i = 0; i < 20; i++) + buffer[i] ^= hash[i]; + + return base64_encode_ab(buffer); + } + + /** + * @param buffer base64 encoded ASN.1 string + */ + export function decode_tomcrypt_key(buffer: string) { + let decoded; + + try { + decoded = asn1.decode(atob(buffer)); + } catch(error) { + if(error instanceof DOMException) + throw "failed to parse key buffer (invalid base64)"; + throw error; } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - publicKey: this.identity.public_key - }).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + let {x, y, k} = { + x: decoded.children[2].content(Infinity, asn1.TagType.VisibleString), + y: decoded.children[3].content(Infinity, asn1.TagType.VisibleString), + k: decoded.children[4].content(Infinity, asn1.TagType.VisibleString) + }; + + if(x.length > 32) { + if(x.charCodeAt(0) != 0) + throw "Invalid X coordinate! (Too long)"; + x = x.substr(1); + } + + if(y.length > 32) { + if(y.charCodeAt(0) != 0) + throw "Invalid Y coordinate! (Too long)"; + y = y.substr(1); + } + + if(k.length > 32) { + if(k.charCodeAt(0) != 0) + throw "Invalid private coordinate! (Too long)"; + k = k.substr(1); + } + + return { + crv: "P-256", + d: base64_url_encode(btoa(k)), + x: base64_url_encode(btoa(x)), + y: base64_url_encode(btoa(y)), + + ext: true, + key_ops:["deriveKey", "sign"], + kty:"EC", + }; + } +} + +class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { + identity: TeaSpeakIdentity; + handler: HandshakeCommandHandler; + + constructor(connection: AbstractServerConnection, identity: TeaSpeakIdentity) { + super(connection); + this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); + } + + start_handshake() { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { + intention: 0, + authentication_method: this.identity.type(), + publicKey: this.identity.public_key + }).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); + + if(error instanceof CommandResult) + error = error.extra_message || error.message; + this.trigger_fail("failed to execute begin (" + error + ")"); + }); + } + + private handle_proof(json) { + if(!json[0]["digest"]) { + this.trigger_fail("server too old"); + return; + } + + this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { + this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { + log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); if(error instanceof CommandResult) error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - - private handle_proof(json) { - if(!json[0]["digest"]) { - this.trigger_fail("server too old"); - return; - } - - this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { - this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { - log.error(LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - - if(error instanceof CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - }).catch(error => { - this.trigger_fail("failed to sign message"); - }); - } - - protected trigger_fail(message: string) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - - protected trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } + this.trigger_fail("failed to execute proof (" + error + ")"); + }).then(() => this.trigger_success()); + }).catch(error => { + this.trigger_fail("failed to sign message"); + }); } - class IdentityPOWWorker { - private _worker: Worker; - private _current_hash: string; - private _best_level: number; - - async initialize(key: string) { - this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - - /* initialize */ - await new Promise((resolve, reject) => { - const timeout_id = setTimeout(() => reject("timeout"), 1000); - - this._worker.onmessage = event => { - clearTimeout(timeout_id); - - if(!event.data) { - reject("invalid data"); - return; - } - - if(!event.data.success) { - reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - this._worker.onerror = event => { - log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); - clearTimeout(timeout_id); - reject("Failed to load worker (" + event.message + ")"); - }; - }); - - /* set data */ - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "set_data", - private_key: key, - code: "set_data" - }); - - const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); - - this._worker.onmessage = event => { - clearTimeout(timeout_id); - - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - }); - } - - async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { - this._current_hash = hash; - if(target < this._best_level) - return true; - - return await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "mine", - hash: this._current_hash, - iterations: iterations, - target: target, - code: "mine" - }); - - const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - if(event.data.result) { - this._best_level = event.data.level; - this._current_hash = event.data.hash; - resolve(true); - } else { - resolve(false); /* no result */ - } - }; - }); - } - - current_hash() : string { - return this._current_hash; - } - - current_level() : number { - return this._best_level; - } - - async finalize(timeout?: number) { - try { - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "finalize", - code: "finalize" - }); - - const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); - - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - resolve(); - }; - }); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); - } - - this._worker.terminate(); - this._worker = undefined; - } - - private handle_message(message: any) { - log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); - } + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); } - export class TeaSpeakIdentity implements Identity { - static async generate_new() : Promise { - let key: CryptoKeyPair; - try { - key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(e) { - log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); - throw "Failed to generate keypair"; - } - const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } +} - const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); - await identity.initialize(); - return identity; - } +class IdentityPOWWorker { + private _worker: Worker; + private _current_hash: string; + private _best_level: number; - static async import_ts(ts_string: string, ini?: boolean) : Promise { - const parse_string = string => { - /* parsing without INI structure */ - const V_index = string.indexOf('V'); - if(V_index == -1) throw "invalid input (missing V)"; + async initialize(key: string) { + this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - return { - hash: string.substr(0, V_index), - data: string.substr(V_index + 1), - name: "TeaSpeak user" + /* initialize */ + await new Promise((resolve, reject) => { + const timeout_id = setTimeout(() => reject("timeout"), 1000); + + this._worker.onmessage = event => { + clearTimeout(timeout_id); + + if(!event.data) { + reject("invalid data"); + return; } + + if(!event.data.success) { + reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); }; + this._worker.onerror = event => { + log.error(LogCategory.IDENTITIES, tr("POW Worker error %o"), event); + clearTimeout(timeout_id); + reject("Failed to load worker (" + event.message + ")"); + }; + }); - const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { - /* parsing with INI structure */ - let identity: string, name: string; - - for(const line of ts_string.split("\n")) { - if(line.startsWith("identity=")) - identity = line.substr(9); - else if(line.startsWith("nickname=")) - name = line.substr(9); - } - - if(!identity) throw "missing identity keyword"; - identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; - if(!identity) throw "invalid identity key value"; - - const result = parse_string(identity); - result.name = name || result.name; - return result; - })(); - - if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - - let buffer; - try { - buffer = new Uint8Array(arrayBufferBase64(data)); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); - throw "failed to base data (base64 decode failed)"; - } - const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - - const identity = new TeaSpeakIdentity(key64, hash, name, false); - await identity.initialize(); - return identity; - } - - hash_number: string; /* hash suffix for the private key */ - private_key: string; /* base64 representation of the private key */ - _name: string; - - public_key: string; /* only set when initialized */ - - private _initialized: boolean; - private _crypto_key: CryptoKey; - private _crypto_key_sign: CryptoKey; - - private _unique_id: string; - - constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { - this.private_key = private_key; - this.hash_number = hash || "0"; - this._name = name; - - if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { - this.initialize().catch(error => { - log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); - this._initialized = false; - }); - } - } - - fallback_name(): string | undefined { - return this._name; - } - - uid(): string { - return this._unique_id; - } - - type(): IdentitifyType { - return IdentitifyType.TEAMSPEAK; - } - - valid(): boolean { - return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; - } - - async decode(data: string) : Promise { - const json = JSON.parse(data); - if(!json) throw "invalid json"; - - if(json.version == 2) { - this.private_key = json.key; - this.hash_number = json.hash; - this._name = json.name; - } else if(json.version == 1) { - const key = json.key; - this._name = json.name; - - const clone = await TeaSpeakIdentity.import_ts(key, false); - this.private_key = clone.private_key; - this.hash_number = clone.hash_number; - } else - throw "invalid version"; - - await this.initialize(); - } - - encode?() : string { - return JSON.stringify({ - key: this.private_key, - hash: this.hash_number, - name: this._name, - version: 2 + /* set data */ + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "set_data", + private_key: key, + code: "set_data" }); - } - async level() : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; + const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); - const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + this._worker.onmessage = event => { + clearTimeout(timeout_id); - let level = 0; - while(level < hash.byteLength && hash[level] == 0) - level++; - - if(level >= hash.byteLength) { - level = 256; - } else { - let byte = hash[level]; - level <<= 3; - while((byte & 0x1) == 0) { - level++; - byte >>= 1; + if (!event.data) { + reject("invalid data"); + return; } - } - return level; - } + if (!event.data.success) { + reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } - /** - * @param {string} a - * @param {string} b - * @description b must be smaller (in bytes) then a - */ - private string_add(a: string, b: string) { - const char_result: number[] = []; - const char_a = [...a].reverse().map(e => e.charCodeAt(0)); - const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + this._worker.onmessage = event => this.handle_message(event.data); + resolve(); + }; + }); + } - let carry = false; - while(char_b.length > 0) { - let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } + async mine(hash: string, iterations: number, target: number, timeout?: number) : Promise { + this._current_hash = hash; + if(target < this._best_level) + return true; - while(char_a.length > 0) { - let result = char_a.pop_front() + (carry ? 1 : 0); - if((carry = result > 57)) - result -= 10; - char_result.push(result); - } + return await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "mine", + hash: this._current_hash, + iterations: iterations, + target: target, + code: "mine" + }); - if(carry) - char_result.push(49); + const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - return String.fromCharCode.apply(null, char_result.slice().reverse()); - } + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + clearTimeout(timeout_id); + if (!event.data) { + reject("invalid data"); + return; + } - async improve_level_for(time: number, threads: number) : Promise { - let active = true; - setTimeout(() => active = false, time); + if (!event.data.success) { + reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } - return await this.improve_level(-1, threads, () => active); - } - - async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { - if(!this._initialized || !this.public_key) - throw "not initialized"; - if(target == -1) /* get the highest level possible */ - target = 0; - else if(target <= await this.level()) - return true; - - const workers: IdentityPOWWorker[] = []; - - const iterations = 100000; - let current_hash; - const next_hash = () => { - if(!current_hash) - return (current_hash = this.hash_number); - - if(current_hash.length < iterations.toString().length) { - current_hash = this.string_add(iterations.toString(), current_hash); + if(event.data.result) { + this._best_level = event.data.level; + this._current_hash = event.data.hash; + resolve(true); } else { - current_hash = this.string_add(current_hash, iterations.toString()); + resolve(false); /* no result */ } - return current_hash; }; + }); + } - { /* init */ - const initialize_promise: Promise[] = []; - for(let index = 0; index < threads; index++) { - const worker = new IdentityPOWWorker(); - workers.push(worker); - initialize_promise.push(worker.initialize(this.public_key)); - } + current_hash() : string { + return this._current_hash; + } - try { - await Promise.all(initialize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to initialize"; - } + current_level() : number { + return this._best_level; + } + + async finalize(timeout?: number) { + try { + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + + clearTimeout(timeout_id); + + if (!event.data) { + reject("invalid data"); + return; + } + + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + resolve(); + }; + }); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } + + this._worker.terminate(); + this._worker = undefined; + } + + private handle_message(message: any) { + log.info(LogCategory.IDENTITIES, tr("Received message: %o"), message); + } +} + +export class TeaSpeakIdentity implements Identity { + static async generate_new() : Promise { + let key: CryptoKeyPair; + try { + key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(e) { + log.error(LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); + throw "Failed to generate keypair"; + } + const private_key = await CryptoHelper.export_ecc_key(key.privateKey, false); + + const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); + await identity.initialize(); + return identity; + } + + static async import_ts(ts_string: string, ini?: boolean) : Promise { + const parse_string = string => { + /* parsing without INI structure */ + const V_index = string.indexOf('V'); + if(V_index == -1) throw "invalid input (missing V)"; + + return { + hash: string.substr(0, V_index), + data: string.substr(V_index + 1), + name: "TeaSpeak user" + } + }; + + const {hash, data, name} = (!ini ? () => parse_string(ts_string) : () => { + /* parsing with INI structure */ + let identity: string, name: string; + + for(const line of ts_string.split("\n")) { + if(line.startsWith("identity=")) + identity = line.substr(9); + else if(line.startsWith("nickname=")) + name = line.substr(9); } - let result = false; - let best_level = 0; - let target_level = target > 0 ? target : await this.level() + 1; + if(!identity) throw "missing identity keyword"; + identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; + if(!identity) throw "invalid identity key value"; - const worker_promise: Promise[] = []; + const result = parse_string(identity); + result.name = name || result.name; + return result; + })(); - const hash_timestamps: number[] = []; - let last_hashrate_update: number = 0; + if(!ts_string.match(/[0-9]+/g)) throw "invalid hash!"; - const update_hashrate = () => { - if(!callback_status) return; - const now = Date.now(); - hash_timestamps.push(now); + let buffer; + try { + buffer = new Uint8Array(arrayBufferBase64(data)); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); + throw "failed to base data (base64 decode failed)"; + } + const key64 = await CryptoHelper.decrypt_ts_identity(new Uint8Array(arrayBufferBase64(data))); - if(last_hashrate_update + 1000 < now) { - last_hashrate_update = now; + const identity = new TeaSpeakIdentity(key64, hash, name, false); + await identity.initialize(); + return identity; + } - const timeout = now - 10 * 1000; /* 10s */ - const rounds = hash_timestamps.filter(e => e > timeout); - callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) - } - }; + hash_number: string; /* hash suffix for the private key */ + private_key: string; /* base64 representation of the private key */ + _name: string; + + public_key: string; /* only set when initialized */ + + private _initialized: boolean; + private _crypto_key: CryptoKey; + private _crypto_key_sign: CryptoKey; + + private _unique_id: string; + + constructor(private_key?: string, hash?: string, name?: string, initialize?: boolean) { + this.private_key = private_key; + this.hash_number = hash || "0"; + this._name = name; + + if(this.private_key && (typeof(initialize) === "undefined" || initialize)) { + this.initialize().catch(error => { + log.error(LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); + this._initialized = false; + }); + } + } + + fallback_name(): string | undefined { + return this._name; + } + + uid(): string { + return this._unique_id; + } + + type(): IdentitifyType { + return IdentitifyType.TEAMSPEAK; + } + + valid(): boolean { + return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; + } + + async decode(data: string) : Promise { + const json = JSON.parse(data); + if(!json) throw "invalid json"; + + if(json.version == 2) { + this.private_key = json.key; + this.hash_number = json.hash; + this._name = json.name; + } else if(json.version == 1) { + const key = json.key; + this._name = json.name; + + const clone = await TeaSpeakIdentity.import_ts(key, false); + this.private_key = clone.private_key; + this.hash_number = clone.hash_number; + } else + throw "invalid version"; + + await this.initialize(); + } + + encode?() : string { + return JSON.stringify({ + key: this.private_key, + hash: this.hash_number, + name: this._name, + version: 2 + }); + } + + async level() : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + + const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number)); + + let level = 0; + while(level < hash.byteLength && hash[level] == 0) + level++; + + if(level >= hash.byteLength) { + level = 256; + } else { + let byte = hash[level]; + level <<= 3; + while((byte & 0x1) == 0) { + level++; + byte >>= 1; + } + } + + return level; + } + + /** + * @param {string} a + * @param {string} b + * @description b must be smaller (in bytes) then a + */ + private string_add(a: string, b: string) { + const char_result: number[] = []; + const char_a = [...a].reverse().map(e => e.charCodeAt(0)); + const char_b = [...b].reverse().map(e => e.charCodeAt(0)); + + let carry = false; + while(char_b.length > 0) { + let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; + if((carry = result > 57)) + result -= 10; + char_result.push(result); + } + + while(char_a.length > 0) { + let result = char_a.pop_front() + (carry ? 1 : 0); + if((carry = result > 57)) + result -= 10; + char_result.push(result); + } + + if(carry) + char_result.push(49); + + return String.fromCharCode.apply(null, char_result.slice().reverse()); + } + + + async improve_level_for(time: number, threads: number) : Promise { + let active = true; + setTimeout(() => active = false, time); + + return await this.improve_level(-1, threads, () => active); + } + + async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise { + if(!this._initialized || !this.public_key) + throw "not initialized"; + if(target == -1) /* get the highest level possible */ + target = 0; + else if(target <= await this.level()) + return true; + + const workers: IdentityPOWWorker[] = []; + + const iterations = 100000; + let current_hash; + const next_hash = () => { + if(!current_hash) + return (current_hash = this.hash_number); + + if(current_hash.length < iterations.toString().length) { + current_hash = this.string_add(iterations.toString(), current_hash); + } else { + current_hash = this.string_add(current_hash, iterations.toString()); + } + return current_hash; + }; + + { /* init */ + const initialize_promise: Promise[] = []; + for(let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } try { - result = await new Promise((resolve, reject) => { - let active = true; + await Promise.all(initialize_promise); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } + } - const exit = () => { - const timeout = setTimeout(() => resolve(true), 1000); - Promise.all(worker_promise).then(result => { - clearTimeout(timeout); - resolve(true); - }).catch(error => resolve(true)); - active = false; + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : await this.level() + 1; + + const worker_promise: Promise[] = []; + + const hash_timestamps: number[] = []; + let last_hashrate_update: number = 0; + + const update_hashrate = () => { + if(!callback_status) return; + const now = Date.now(); + hash_timestamps.push(now); + + if(last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + + const timeout = now - 10 * 1000; /* 10s */ + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) + } + }; + + try { + result = await new Promise((resolve, reject) => { + let active = true; + + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; + }; + + for(const worker of workers) { + const worker_mine = () => { + if(!active) return; + + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + + worker_promise.remove(p); + + if(result.valueOf()) { + if(worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + + log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if(callback_level) + callback_level(best_level); + } + + if(active) { + if(target > 0) + exit(); + else + target_level = best_level + 1; + } + } + + if(active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + + log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + + return Promise.resolve(); + }); + + worker_promise.push(p); }; - for(const worker of workers) { - const worker_mine = () => { - if(!active) return; - - const promise = worker.mine(next_hash(), iterations, target_level); - const p = promise.then(result => { - update_hashrate(); - - worker_promise.remove(p); - - if(result.valueOf()) { - if(worker.current_level() > best_level) { - this.hash_number = worker.current_hash(); - - log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); - best_level = worker.current_level(); - if(callback_level) - callback_level(best_level); - } - - if(active) { - if(target > 0) - exit(); - else - target_level = best_level + 1; - } - } - - if(active && (active = active_callback())) - setTimeout(() => worker_mine(), 0); - else { - exit(); - } - - return Promise.resolve(); - }).catch(error => { - worker_promise.remove(p); - - log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); - reject(error); - - return Promise.resolve(); - }); - - worker_promise.push(p); - }; - - worker_mine(); - } - }); - } catch(error) { - //error already printed before reject had been called - } - - { /* shutdown */ - const finalize_promise: Promise[] = []; - for(const worker of workers) - finalize_promise.push(worker.finalize(250)); - - try { - await Promise.all(finalize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to finalize"; + worker_mine(); } - } - - - return result; + }); + } catch(error) { + //error already printed before reject had been called } - private async initialize() { - if(!this.private_key) - throw "Invalid private key"; - - let jwk: any; - try { - jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); - if(!jwk) - throw "result undefined"; - } catch(error) { - throw "failed to parse key (" + error + ")"; - } + { /* shutdown */ + const finalize_promise: Promise[] = []; + for(const worker of workers) + finalize_promise.push(worker.finalize(250)); try { - this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); + await Promise.all(finalize_promise); } catch(error) { log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto sign key"; + throw "failed to finalize"; } - - try { - this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to create crypto key"; - } - - try { - this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); - this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to calculate unique id"; - } - - this._initialized = true; - //const public_key = await profiles.identities.CryptoHelper.export_ecc_key(key, true); } - async export_ts(ini?: boolean) : Promise { - if(!this.private_key) - throw "Invalid private key"; - const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); - if(!ini) return identity; + return result; + } - return "[Identity]\n" + - "id=TeaWeb-Exported\n" + - "identity=\"" + identity + "\"\n" + - "nickname=\"" + this.fallback_name() + "\"\n" + - "phonetic_nickname="; + private async initialize() { + if(!this.private_key) + throw "Invalid private key"; + + let jwk: any; + try { + jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); + if(!jwk) + throw "result undefined"; + } catch(error) { + throw "failed to parse key (" + error + ")"; } - async sign_message(message: string, hash: string = "SHA-256") : Promise { - /* bring this to libtomcrypt format */ - const sign_buffer = await crypto.subtle.sign({ - name: "ECDSA", - hash: hash - }, this._crypto_key_sign, str2ab8(message)); - const sign = new Uint8Array(sign_buffer); - /* first 32 r bits | last 32 s bits */ - - const buffer = new Uint8Array(72); - let index = 0; - - { /* the initial sequence */ - buffer[index++] = 0x30; /* type */ - buffer[index++] = 0x00; /* we will set the sequence length later */ - } - { /* integer r */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[0] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = sign[i]; - } - { /* integer s */ - buffer[index++] = 0x02; /* type */ - buffer[index++] = 0x20; /* length */ - - if(sign[32] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - - for(let i = 0; i < 32; i++) - buffer[index++] = sign[32 + i]; - } - buffer[1] = index - 2; - - return base64_encode_ab(buffer.subarray(0, index)); + try { + this._crypto_key_sign = await crypto.subtle.importKey("jwk", jwk, {name:'ECDSA', namedCurve: 'P-256'}, false, ["sign"]); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto sign key"; } - spawn_identity_handshake_handler(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { - return new TeaSpeakHandshakeHandler(connection, this); + try { + this._crypto_key = await crypto.subtle.importKey("jwk", jwk, {name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to create crypto key"; } + + try { + this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true); + this._unique_id = base64_encode_ab(await sha.sha1(this.public_key)); + } catch(error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to calculate unique id"; + } + + this._initialized = true; + } + + async export_ts(ini?: boolean) : Promise { + if(!this.private_key) + throw "Invalid private key"; + + const identity = this.hash_number + "V" + await CryptoHelper.encrypt_ts_identity(new Uint8Array(str2ab8(this.private_key))); + if(!ini) return identity; + + return "[Identity]\n" + + "id=TeaWeb-Exported\n" + + "identity=\"" + identity + "\"\n" + + "nickname=\"" + this.fallback_name() + "\"\n" + + "phonetic_nickname="; + } + + async sign_message(message: string, hash: string = "SHA-256") : Promise { + /* bring this to libtomcrypt format */ + const sign_buffer = await crypto.subtle.sign({ + name: "ECDSA", + hash: hash + }, this._crypto_key_sign, str2ab8(message)); + const sign = new Uint8Array(sign_buffer); + /* first 32 r bits | last 32 s bits */ + + const buffer = new Uint8Array(72); + let index = 0; + + { /* the initial sequence */ + buffer[index++] = 0x30; /* type */ + buffer[index++] = 0x00; /* we will set the sequence length later */ + } + { /* integer r */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[0] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[i]; + } + { /* integer s */ + buffer[index++] = 0x02; /* type */ + buffer[index++] = 0x20; /* length */ + + if(sign[32] > 0x7F) { + buffer[index - 1] += 1; + buffer[index++] = 0; + } + + for(let i = 0; i < 32; i++) + buffer[index++] = sign[32 + i]; + } + buffer[1] = index - 2; + + return base64_encode_ab(buffer.subarray(0, index)); + } + + spawn_identity_handshake_handler(connection: AbstractServerConnection): HandshakeIdentityHandler { + return new TeaSpeakHandshakeHandler(connection, this); } } \ No newline at end of file diff --git a/shared/js/profiles/identities/teaspeak-forum.ts b/shared/js/profiles/identities/teaspeak-forum.ts index 1ab6cd53..3ea4ff81 100644 --- a/shared/js/profiles/identities/teaspeak-forum.ts +++ b/shared/js/profiles/identities/teaspeak-forum.ts @@ -1,5 +1,11 @@ -interface Window { - grecaptcha: GReCaptcha; +import {settings, Settings} from "tc-shared/settings"; +import * as loader from "tc-loader"; +import * as fidentity from "./TeaForumIdentity"; + +declare global { + interface Window { + grecaptcha: GReCaptcha; + } } interface GReCaptcha { @@ -18,349 +24,347 @@ interface GReCaptcha { reset(widget_id?: string); } -namespace forum { - export namespace gcaptcha { - export async function initialize() { - if(typeof(window.grecaptcha) === "undefined") { - let script = document.createElement("script"); - script.async = true; +export namespace gcaptcha { + export async function initialize() { + if(typeof(window.grecaptcha) === "undefined") { + let script = document.createElement("script"); + script.async = true; - let timeout; - const callback_name = "captcha_callback_" + Math.random().toString().replace(".", ""); - try { - await new Promise((resolve, reject) => { - script.onerror = reject; - window[callback_name] = resolve; - script.src = "https://www.google.com/recaptcha/api.js?onload=" + encodeURIComponent(callback_name) + "&render=explicit"; - - document.body.append(script); - timeout = setTimeout(() => reject("timeout"), 15000); - }); - } catch(error) { - script.remove(); - script = undefined; - - console.error(tr("Failed to fetch recaptcha javascript source: %o"), error); - throw tr("failed to download source"); - } finally { - if(script) - script.onerror = undefined; - delete window[callback_name]; - clearTimeout(timeout); - } - } - - if(typeof(window.grecaptcha) === "undefined") - throw tr("failed to load recaptcha"); - } - - export async function spawn(container: JQuery, key: string, callback_data: (token: string) => any) { + let timeout; + const callback_name = "captcha_callback_" + Math.random().toString().replace(".", ""); try { - await initialize(); + await new Promise((resolve, reject) => { + script.onerror = reject; + window[callback_name] = resolve; + script.src = "https://www.google.com/recaptcha/api.js?onload=" + encodeURIComponent(callback_name) + "&render=explicit"; + + document.body.append(script); + timeout = setTimeout(() => reject("timeout"), 15000); + }); } catch(error) { - console.error(tr("Failed to initialize G-Recaptcha. Error: %o"), error); - throw tr("initialisation failed"); - } - if(container.attr("captcha-uuid")) - window.grecaptcha.reset(container.attr("captcha-uuid")); - else { - container.attr("captcha-uuid", window.grecaptcha.render(container[0], { - "sitekey": key, - callback: callback_data - })); + script.remove(); + script = undefined; + + console.error(tr("Failed to fetch recaptcha javascript source: %o"), error); + throw tr("failed to download source"); + } finally { + if(script) + script.onerror = undefined; + delete window[callback_name]; + timeout && clearTimeout(timeout); } } + + if(typeof(window.grecaptcha) === "undefined") + throw tr("failed to load recaptcha"); } - function api_url() { - return settings.static_global(Settings.KEY_TEAFORO_URL); - } - - export class Data { - readonly auth_key: string; - readonly raw: string; - readonly sign: string; - - parsed: { - user_id: number; - user_name: string; - - data_age: number; - - user_group_id: number; - - is_staff: boolean; - user_groups: number[]; - }; - - constructor(auth: string, raw: string, sign: string) { - this.auth_key = auth; - this.raw = raw; - this.sign = sign; - - this.parsed = JSON.parse(raw); + export async function spawn(container: JQuery, key: string, callback_data: (token: string) => any) { + try { + await initialize(); + } catch(error) { + console.error(tr("Failed to initialize G-Recaptcha. Error: %o"), error); + throw tr("initialisation failed"); + } + if(container.attr("captcha-uuid")) + window.grecaptcha.reset(container.attr("captcha-uuid")); + else { + container.attr("captcha-uuid", window.grecaptcha.render(container[0], { + "sitekey": key, + callback: callback_data + })); } - - - data_json() : string { return this.raw; } - data_sign() : string { return this.sign; } - - name() : string { return this.parsed.user_name; } - - user_id() { return this.parsed.user_id; } - user_group() { return this.parsed.user_group_id; } - - is_stuff() : boolean { return this.parsed.is_staff; } - is_premium() : boolean { return this.parsed.user_groups.indexOf(5) != -1; } - - data_age() : Date { return new Date(this.parsed.data_age); } - - is_expired() : boolean { return this.parsed.data_age + 48 * 60 * 60 * 1000 < Date.now(); } - should_renew() : boolean { return this.parsed.data_age + 24 * 60 * 60 * 1000 < Date.now(); } /* renew data all 24hrs */ } - let _data: Data | undefined; +} - export function logged_in() : boolean { - return !!_data && !_data.is_expired(); +function api_url() { + return settings.static_global(Settings.KEY_TEAFORO_URL); +} + +export class Data { + readonly auth_key: string; + readonly raw: string; + readonly sign: string; + + parsed: { + user_id: number; + user_name: string; + + data_age: number; + + user_group_id: number; + + is_staff: boolean; + user_groups: number[]; + }; + + constructor(auth: string, raw: string, sign: string) { + this.auth_key = auth; + this.raw = raw; + this.sign = sign; + + this.parsed = JSON.parse(raw); } - export function data() : Data { return _data; } - export interface LoginResult { - status: "success" | "captcha" | "error"; + data_json() : string { return this.raw; } + data_sign() : string { return this.sign; } - error_message?: string; - captcha?: { - type: "gre-captcha" | "unknown"; - data: any; /* in case of gre-captcha it would be the side key */ + name() : string { return this.parsed.user_name; } + + user_id() { return this.parsed.user_id; } + user_group() { return this.parsed.user_group_id; } + + is_stuff() : boolean { return this.parsed.is_staff; } + is_premium() : boolean { return this.parsed.user_groups.indexOf(5) != -1; } + + data_age() : Date { return new Date(this.parsed.data_age); } + + is_expired() : boolean { return this.parsed.data_age + 48 * 60 * 60 * 1000 < Date.now(); } + should_renew() : boolean { return this.parsed.data_age + 24 * 60 * 60 * 1000 < Date.now(); } /* renew data all 24hrs */ +} +let _data: Data | undefined; + +export function logged_in() : boolean { + return !!_data && !_data.is_expired(); +} + +export function data() : Data { return _data; } + +export interface LoginResult { + status: "success" | "captcha" | "error"; + + error_message?: string; + captcha?: { + type: "gre-captcha" | "unknown"; + data: any; /* in case of gre-captcha it would be the side key */ + }; +} + +export async function login(username: string, password: string, captcha?: any) : Promise { + let response; + try { + response = await new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/login", + type: "POST", + cache: false, + data: { + username: username, + password: password, + remember: true, + "g-recaptcha-response": captcha + }, + + crossDomain: true, + + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Login request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }) + }); + } catch(error) { + return { + status: "error", + error_message: tr("failed to send login request") }; } - export async function login(username: string, password: string, captcha?: any) : Promise { - let response; - try { - response = await new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/login", - type: "POST", - cache: false, - data: { - username: username, - password: password, - remember: true, - "g-recaptcha-response": captcha - }, + if(response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + return { + status: "error", + error_message: (response["errors"] || [])[0] || tr("Unknown error") + }; + } - crossDomain: true, + if(!response["success"]) { + console.error(tr("Login failed. Response %o"), response); - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Login request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }) - }); - } catch(error) { - return { - status: "error", - error_message: tr("failed to send login request") + let message = tr("failed to login"); + let captcha; + /* user/password wrong | and maybe captcha required */ + if(response["code"] == 1 || response["code"] == 3) + message = tr("Invalid username or password"); + if(response["code"] == 2 || response["code"] == 3) { + captcha = { + type: response["captcha"]["type"], + data: response["captcha"]["siteKey"] //TODO: Why so static here? }; - } - - if(response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - return { - status: "error", - error_message: (response["errors"] || [])[0] || tr("Unknown error") - }; - } - - if(!response["success"]) { - console.error(tr("Login failed. Response %o"), response); - - let message = tr("failed to login"); - let captcha; - /* user/password wrong | and maybe captcha required */ - if(response["code"] == 1 || response["code"] == 3) - message = tr("Invalid username or password"); - if(response["code"] == 2 || response["code"] == 3) { - captcha = { - type: response["captcha"]["type"], - data: response["captcha"]["siteKey"] //TODO: Why so static here? - }; - if(response["code"] == 2) - message = tr("captcha required"); - } - - return { - status: typeof(captcha) !== "undefined" ? "captcha" : "error", - error_message: message, - captcha: captcha - }; - } - //document.cookie = "user_data=" + response["data"] + ";path=/"; - //document.cookie = "user_sign=" + response["sign"] + ";path=/"; - - try { - _data = new Data(response["auth-key"], response["data"], response["sign"]); - localStorage.setItem("teaspeak-forum-data", response["data"]); - localStorage.setItem("teaspeak-forum-sign", response["sign"]); - localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); - profiles.identities.update_forum(); - } catch(error) { - console.error(tr("Failed to parse forum given data: %o"), error); - return { - status: "error", - error_message: tr("Failed to parse response data") - } + if(response["code"] == 2) + message = tr("captcha required"); } return { - status: "success" + status: typeof(captcha) !== "undefined" ? "captcha" : "error", + error_message: message, + captcha: captcha }; } + //document.cookie = "user_data=" + response["data"] + ";path=/"; + //document.cookie = "user_sign=" + response["sign"] + ";path=/"; - export async function renew_data() : Promise<"success" | "login-required"> { - let response; - try { - response = await new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/renew-data", - type: "GET", - cache: false, - - crossDomain: true, - - data: { - "auth-key": _data.auth_key - }, - - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Renew request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }) - }); - } catch(error) { - throw tr("failed to send renew request"); + try { + _data = new Data(response["auth-key"], response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); + fidentity.update_forum(); + } catch(error) { + console.error(tr("Failed to parse forum given data: %o"), error); + return { + status: "error", + error_message: tr("Failed to parse response data") } + } - if(response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - throw (response["errors"] || [])[0] || tr("Unknown error"); + return { + status: "success" + }; +} + +export async function renew_data() : Promise<"success" | "login-required"> { + let response; + try { + response = await new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/renew-data", + type: "GET", + cache: false, + + crossDomain: true, + + data: { + "auth-key": _data.auth_key + }, + + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Renew request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }) + }); + } catch(error) { + throw tr("failed to send renew request"); + } + + if(response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + throw (response["errors"] || [])[0] || tr("Unknown error"); + } + + if(!response["success"]) { + if(response["code"] == 1) { + return "login-required"; } + throw "invalid error code (" + response["code"] + ")"; + } + if(!response["data"] || !response["sign"]) + throw tr("response missing data"); - if(!response["success"]) { - if(response["code"] == 1) { - return "login-required"; - } + console.debug(tr("Renew succeeded. Parsing data.")); + + try { + _data = new Data(_data.auth_key, response["data"], response["sign"]); + localStorage.setItem("teaspeak-forum-data", response["data"]); + localStorage.setItem("teaspeak-forum-sign", response["sign"]); + fidentity.update_forum(); + } catch(error) { + console.error(tr("Failed to parse forum given data: %o"), error); + throw tr("failed to parse data"); + } + + return "success"; +} + +export async function logout() : Promise { + if(!logged_in()) + return; + + let response; + try { + response = await new Promise((resolve, reject) => { + $.ajax({ + url: api_url() + "?web-api/v1/logout", + type: "GET", + cache: false, + + crossDomain: true, + + data: { + "auth-key": _data.auth_key + }, + + success: resolve, + error: (xhr, status, error) => { + console.log(tr("Logout request failed %o: %o"), status, error); + reject(tr("request failed")); + } + }) + }); + } catch(error) { + throw tr("failed to send logout request"); + } + + if(response["status"] !== "ok") { + console.error(tr("Response status not okey. Error happend: %o"), response); + throw (response["errors"] || [])[0] || tr("Unknown error"); + } + + if(!response["success"]) { + /* code 1 means not logged in, its an success */ + if(response["code"] != 1) { throw "invalid error code (" + response["code"] + ")"; } - if(!response["data"] || !response["sign"]) - throw tr("response missing data"); - - console.debug(tr("Renew succeeded. Parsing data.")); - - try { - _data = new Data(_data.auth_key, response["data"], response["sign"]); - localStorage.setItem("teaspeak-forum-data", response["data"]); - localStorage.setItem("teaspeak-forum-sign", response["sign"]); - profiles.identities.update_forum(); - } catch(error) { - console.error(tr("Failed to parse forum given data: %o"), error); - throw tr("failed to parse data"); - } - - return "success"; } - export async function logout() : Promise { - if(!logged_in()) + _data = undefined; + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + fidentity.update_forum(); +} + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "TeaForo initialize", + priority: 10, + function: async () => { + const raw_data = localStorage.getItem("teaspeak-forum-data"); + const raw_sign = localStorage.getItem("teaspeak-forum-sign"); + const forum_auth = localStorage.getItem("teaspeak-forum-auth"); + if(!raw_data || !raw_sign || !forum_auth) { + console.log(tr("No TeaForo authentification found. TeaForo connection status: unconnected")); return; + } - let response; try { - response = await new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/logout", - type: "GET", - cache: false, - - crossDomain: true, - - data: { - "auth-key": _data.auth_key - }, - - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Logout request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }) - }); + _data = new Data(forum_auth, raw_data, raw_sign); } catch(error) { - throw tr("failed to send logout request"); + console.error(tr("Failed to initialize TeaForo connection from local data. Error: %o"), error); + return; + } + if(_data.should_renew()) { + console.info(tr("TeaForo data should be renewed. Executing renew.")); + renew_data().then(status => { + if(status === "success") { + console.info(tr("TeaForo data has been successfully renewed.")); + } else { + console.warn(tr("Failed to renew TeaForo data. New login required.")); + localStorage.removeItem("teaspeak-forum-data"); + localStorage.removeItem("teaspeak-forum-sign"); + localStorage.removeItem("teaspeak-forum-auth"); + } + }).catch(error => { + console.warn(tr("Failed to renew TeaForo data. An error occurred: %o"), error); + }); + return; } - if(response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - throw (response["errors"] || [])[0] || tr("Unknown error"); + if(_data && _data.is_expired()) { + console.error(tr("TeaForo data is expired. TeaForo connection isn't available!")); } - - if(!response["success"]) { - /* code 1 means not logged in, its an success */ - if(response["code"] != 1) { - throw "invalid error code (" + response["code"] + ")"; - } - } - - _data = undefined; - localStorage.removeItem("teaspeak-forum-data"); - localStorage.removeItem("teaspeak-forum-sign"); - localStorage.removeItem("teaspeak-forum-auth"); - profiles.identities.update_forum(); } - - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "TeaForo initialize", - priority: 10, - function: async () => { - const raw_data = localStorage.getItem("teaspeak-forum-data"); - const raw_sign = localStorage.getItem("teaspeak-forum-sign"); - const forum_auth = localStorage.getItem("teaspeak-forum-auth"); - if(!raw_data || !raw_sign || !forum_auth) { - console.log(tr("No TeaForo authentification found. TeaForo connection status: unconnected")); - return; - } - - try { - _data = new Data(forum_auth, raw_data, raw_sign); - } catch(error) { - console.error(tr("Failed to initialize TeaForo connection from local data. Error: %o"), error); - return; - } - if(_data.should_renew()) { - console.info(tr("TeaForo data should be renewed. Executing renew.")); - renew_data().then(status => { - if(status === "success") { - console.info(tr("TeaForo data has been successfully renewed.")); - } else { - console.warn(tr("Failed to renew TeaForo data. New login required.")); - localStorage.removeItem("teaspeak-forum-data"); - localStorage.removeItem("teaspeak-forum-sign"); - localStorage.removeItem("teaspeak-forum-auth"); - } - }).catch(error => { - console.warn(tr("Failed to renew TeaForo data. An error occurred: %o"), error); - }); - return; - } - - if(_data && _data.is_expired()) { - console.error(tr("TeaForo data is expired. TeaForo connection isn't available!")); - } - } - }) -} \ No newline at end of file +}) \ No newline at end of file diff --git a/shared/js/proto.ts b/shared/js/proto.ts index 18a162a7..9d732c33 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -1,45 +1,109 @@ //Used by CertAccept popup -interface Array { - remove(elem?: T): boolean; - last?(): T; +declare global { + interface Array { + remove(elem?: T): boolean; + last?(): T; - pop_front(): T | undefined; + pop_front(): T | undefined; + } + + interface JSON { + map_to(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number) : number; + map_field_to(object: T, value: any, field: string) : boolean; + } + + type JQueryScrollType = "height" | "width"; + interface JQuery { + render(values?: any) : string; + renderTag(values?: any) : JQuery; + hasScrollBar(direction?: JQueryScrollType) : boolean; + + + visible_height() : number; + visible_width() : number; + + /* bootstrap */ + alert() : JQuery; + modal(properties: any) : this; + bootstrapMaterialDesign() : this; + + /* first element which matches the selector, could be the element itself or a parent */ + firstParent(selector: string) : JQuery; + } + + interface JQueryStatic { + spawn(tagName: K): JQuery; + views: any; + } + + interface String { + format(...fmt): string; + format(arguments: string[]): string; + } + + interface Twemoji { + parse(message: string) : string; + } + let twemoji: Twemoji; + + interface HighlightJS { + listLanguages() : string[]; + getLanguage(name: string) : any | undefined; + + highlight(language: string, text: string, ignore_illegals?: boolean) : HighlightJSResult; + highlightAuto(text: string) : HighlightJSResult; + } + + interface HighlightJSResult { + language: string; + relevance: number; + + value: string; + second_best?: any; + } + + interface DOMPurify { + sanitize(html: string, config?: { + ADD_ATTR?: string[] + ADD_TAGS?: string[]; + }) : string; + } + let DOMPurify: DOMPurify; + + let remarkable: typeof window.remarkable; + + class webkitAudioContext extends AudioContext {} + class webkitOfflineAudioContext extends OfflineAudioContext {} + + interface Window { + readonly webkitAudioContext: typeof webkitAudioContext; + readonly AudioContext: typeof webkitAudioContext; + readonly OfflineAudioContext: typeof OfflineAudioContext; + readonly webkitOfflineAudioContext: typeof webkitOfflineAudioContext; + readonly RTCPeerConnection: typeof RTCPeerConnection; + readonly Pointer_stringify: any; + readonly jsrender: any; + + twemoji: Twemoji; + hljs: HighlightJS; + remarkable: any; + + require(id: string): any; + } + + interface Navigator { + browserSpecs: { + name: string, + version: string + }; + + mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void; + webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void; + } } -interface JSON { - map_to(object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number) : number; - map_field_to(object: T, value: any, field: string) : boolean; -} - -type JQueryScrollType = "height" | "width"; -interface JQuery { - render(values?: any) : string; - renderTag(values?: any) : JQuery; - hasScrollBar(direction?: JQueryScrollType) : boolean; - - - visible_height() : number; - visible_width() : number; - - /* bootstrap */ - alert() : JQuery; - modal(properties: any) : this; - bootstrapMaterialDesign() : this; - - /* first element which matches the selector, could be the element itself or a parent */ - firstParent(selector: string) : JQuery; -} - -interface JQueryStatic { - spawn(tagName: K): JQuery; - views: any; -} - -interface String { - format(...fmt): string; - format(arguments: string[]): string; -} +export function initialize() { } if(!JSON.map_to) { JSON.map_to = function (object: T, json: any, variables?: string | string[], validator?: (map_field: string, map_value: string) => boolean, variable_direction?: number): number { @@ -131,6 +195,7 @@ if(typeof ($) !== "undefined") { return $(document.createElement(tagName) as any); } } + if(!$.fn.renderTag) { $.fn.renderTag = function (this: JQuery, values?: any) : JQuery { let result; @@ -184,7 +249,8 @@ if(typeof ($) !== "undefined") { const result = this.height(); this.attr("style", original_style || ""); return result; - } + }; + if(!$.fn.visible_width) $.fn.visible_width = function (this: JQuery) { const original_style = this.attr("style"); @@ -197,7 +263,8 @@ if(typeof ($) !== "undefined") { const result = this.width(); this.attr("style", original_style || ""); return result; - } + }; + if(!$.fn.firstParent) $.fn.firstParent = function (this: JQuery, selector: string) { if(this.is(selector)) @@ -232,30 +299,6 @@ function concatenate(resultConstructor, ...arrays) { return result; } -function formatDate(secs: number) : string { - let years = Math.floor(secs / (60 * 60 * 24 * 365)); - let days = Math.floor(secs / (60 * 60 * 24)) % 365; - let hours = Math.floor(secs / (60 * 60)) % 24; - let minutes = Math.floor(secs / 60) % 60; - let seconds = Math.floor(secs % 60); - - let result = ""; - if(years > 0) - result += years + " " + tr("years") + " "; - if(years > 0 || days > 0) - result += days + " " + tr("days") + " "; - if(years > 0 || days > 0 || hours > 0) - result += hours + " " + tr("hours") + " "; - if(years > 0 || days > 0 || hours > 0 || minutes > 0) - result += minutes + " " + tr("minutes") + " "; - if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) - result += seconds + " " + tr("seconds") + " "; - else - result = tr("now") + " "; - - return result.substr(0, result.length - 1); -} - function calculate_width(text: string) : number { let element = $.spawn("div"); element.text(text) @@ -265,64 +308,4 @@ function calculate_width(text: string) : number { let size = element.width(); element.detach(); return size; -} - -interface Twemoji { - parse(message: string) : string; -} -declare let twemoji: Twemoji; - -interface HighlightJS { - listLanguages() : string[]; - getLanguage(name: string) : any | undefined; - - highlight(language: string, text: string, ignore_illegals?: boolean) : HighlightJSResult; - highlightAuto(text: string) : HighlightJSResult; -} - -interface HighlightJSResult { - language: string; - relevance: number; - - value: string; - second_best?: any; -} - -interface DOMPurify { - sanitize(html: string, config?: { - ADD_ATTR?: string[] - ADD_TAGS?: string[]; - }) : string; -} -declare let DOMPurify: DOMPurify; - -declare let remarkable: typeof window.remarkable; - -declare class webkitAudioContext extends AudioContext {} -declare class webkitOfflineAudioContext extends OfflineAudioContext {} - -interface Window { - readonly webkitAudioContext: typeof webkitAudioContext; - readonly AudioContext: typeof webkitAudioContext; - readonly OfflineAudioContext: typeof OfflineAudioContext; - readonly webkitOfflineAudioContext: typeof webkitOfflineAudioContext; - readonly RTCPeerConnection: typeof RTCPeerConnection; - readonly Pointer_stringify: any; - readonly jsrender: any; - - twemoji: Twemoji; - hljs: HighlightJS; - remarkable: any; - - require(id: string): any; -} - -interface Navigator { - browserSpecs: { - name: string, - version: string - }; - - mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void; - webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void; } \ No newline at end of file diff --git a/shared/js/settings.ts b/shared/js/settings.ts index 59a9c8eb..10ad216a 100644 --- a/shared/js/settings.ts +++ b/shared/js/settings.ts @@ -1,6 +1,10 @@ -/// //Used by CertAccept popup +import {createErrorModal} from "tc-shared/ui/elements/Modal"; +import {LogCategory} from "tc-shared/log"; +import * as loader from "tc-loader"; +import * as log from "tc-shared/log"; + if(typeof(customElements) !== "undefined") { try { class X_Properties extends HTMLElement {} @@ -9,12 +13,12 @@ if(typeof(customElements) !== "undefined") { customElements.define('x-properties', X_Properties, { extends: 'div' }); customElements.define('x-property', X_Property, { extends: 'div' }); } catch(error) { - console.warn("failed to define costum elements"); + console.warn("failed to define costume elements"); } } /* T = value type */ -interface SettingsKey { +export interface SettingsKey { key: string; fallback_keys?: string | string[]; @@ -25,7 +29,7 @@ interface SettingsKey { require_restart?: boolean; } -class SettingsBase { +export class SettingsBase { protected static readonly UPDATE_DIRECT: boolean = true; protected static transformStO?(input?: string, _default?: T, default_type?: string) : T { @@ -77,7 +81,7 @@ class SettingsBase { } } -class StaticSettings extends SettingsBase { +export class StaticSettings extends SettingsBase { private static _instance: StaticSettings; static get instance() : StaticSettings { if(!this._instance) @@ -139,7 +143,7 @@ class StaticSettings extends SettingsBase { } } -class Settings extends StaticSettings { +export class Settings extends StaticSettings { static readonly KEY_USER_IS_NEW: SettingsKey = { key: 'user_is_new_user', default_value: true @@ -433,7 +437,7 @@ class Settings extends StaticSettings { } } -class ServerSettings extends SettingsBase { +export class ServerSettings extends SettingsBase { private cacheServer = {}; private _server_unique_id: string; private _server_save_worker: NodeJS.Timer; @@ -511,4 +515,4 @@ class ServerSettings extends SettingsBase { } } -let settings: Settings; \ No newline at end of file +export let settings: Settings; \ No newline at end of file diff --git a/shared/js/sound/Sounds.ts b/shared/js/sound/Sounds.ts index cbb75329..7f58514c 100644 --- a/shared/js/sound/Sounds.ts +++ b/shared/js/sound/Sounds.ts @@ -1,4 +1,10 @@ -enum Sound { +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {settings} from "tc-shared/settings"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import * as sbackend from "tc-backend/audio/sounds"; + +export enum Sound { SOUND_TEST = "sound.test", SOUND_EGG = "sound.egg", @@ -61,235 +67,211 @@ enum Sound { GROUP_CHANNEL_CHANGED_SELF = "group.channel.changed.self" } -namespace sound { - export interface SoundHandle { - key: string; - filename: string; - } +export interface SoundHandle { + key: string; + filename: string; +} - export interface SoundFile { - path: string; - volume?: number; - } +export interface SoundFile { + path: string; + volume?: number; +} - let speech_mapping: {[key: string]:SoundHandle} = {}; +let speech_mapping: {[key: string]:SoundHandle} = {}; - let volume_require_save = false; - let speech_volume: {[key: string]:number} = {}; - let master_volume: number; +let volume_require_save = false; +let speech_volume: {[key: string]:number} = {}; +let master_volume: number; - let overlap_sounds: boolean; - let ignore_muted: boolean; +let overlap_sounds: boolean; +let ignore_muted: boolean; - let master_mixed: GainNode; +let master_mixed: GainNode; - function register_sound(key: string, file: string) { - speech_mapping[key] = {key: key, filename: file} as SoundHandle; - } +function register_sound(key: string, file: string) { + speech_mapping[key] = {key: key, filename: file} as SoundHandle; +} - export function get_sound_volume(sound: Sound, default_volume?: number) : number { - let result = speech_volume[sound]; - if(typeof(result) === "undefined") { - if(typeof(default_volume) !== "undefined") - result = default_volume; - else - result = 1; - } - return result; - } - - export function set_sound_volume(sound: Sound, volume: number) { - volume_require_save = volume_require_save || speech_volume[sound] != volume; - speech_volume[sound] = volume == 1 ? undefined : volume; - } - - export function get_master_volume() : number { - return master_volume; - } - - export function set_master_volume(volume: number) { - volume_require_save = volume_require_save || master_volume != volume; - master_volume = volume; - if(master_mixed) { - if(master_mixed.gain.setValueAtTime) - master_mixed.gain.setValueAtTime(volume, 0); - else - master_mixed.gain.value = volume; - } - } - - export function overlap_activated() : boolean { - return overlap_sounds; - } - - export function set_overlap_activated(flag: boolean) { - volume_require_save = volume_require_save || overlap_sounds != flag; - overlap_sounds = flag; - } - - export function ignore_output_muted() : boolean { - return ignore_muted; - } - - export function set_ignore_output_muted(flag: boolean) { - volume_require_save = volume_require_save || ignore_muted != flag; - ignore_muted = flag; - } - - export function reinitialisize_audio() { - const context = audio.player.context(); - const destination = audio.player.destination(); - - if(master_mixed) - master_mixed.disconnect(); - - master_mixed = context.createGain(); - if(master_mixed.gain.setValueAtTime) - master_mixed.gain.setValueAtTime(master_volume, 0); +export function get_sound_volume(sound: Sound, default_volume?: number) : number { + let result = speech_volume[sound]; + if(typeof(result) === "undefined") { + if(typeof(default_volume) !== "undefined") + result = default_volume; else - master_mixed.gain.value = master_volume; - master_mixed.connect(destination); + result = 1; } + return result; +} - export function save() { - if(volume_require_save) { - volume_require_save = false; +export function set_sound_volume(sound: Sound, volume: number) { + volume_require_save = volume_require_save || speech_volume[sound] != volume; + speech_volume[sound] = volume == 1 ? undefined : volume; +} - const data: any = {}; - data.version = 1; +export function get_master_volume() : number { + return master_volume; +} - for(const key in Sound) { - if(typeof(speech_volume[Sound[key]]) !== "undefined") - data[Sound[key]] = speech_volume[Sound[key]]; - } - data.master = master_volume; - data.overlap = overlap_sounds; - data.ignore_muted = ignore_muted; +export function set_master_volume(volume: number) { + volume_require_save = volume_require_save || master_volume != volume; + master_volume = volume; + if(master_mixed) { + if(master_mixed.gain.setValueAtTime) + master_mixed.gain.setValueAtTime(volume, 0); + else + master_mixed.gain.value = volume; + } +} - settings.changeGlobal("sound_volume", JSON.stringify(data)); +export function overlap_activated() : boolean { + return overlap_sounds; +} + +export function set_overlap_activated(flag: boolean) { + volume_require_save = volume_require_save || overlap_sounds != flag; + overlap_sounds = flag; +} + +export function ignore_output_muted() : boolean { + return ignore_muted; +} + +export function set_ignore_output_muted(flag: boolean) { + volume_require_save = volume_require_save || ignore_muted != flag; + ignore_muted = flag; +} + +export function save() { + if(volume_require_save) { + volume_require_save = false; + + const data: any = {}; + data.version = 1; + + for(const key of Object.keys(Sound)) { + if(typeof(speech_volume[Sound[key]]) !== "undefined") + data[Sound[key]] = speech_volume[Sound[key]]; } + data.master = master_volume; + data.overlap = overlap_sounds; + data.ignore_muted = ignore_muted; + + settings.changeGlobal("sound_volume", JSON.stringify(data)); + } +} + +export function initialize() : Promise { + $.ajaxSetup({ + beforeSend: function(jqXHR,settings){ + if (settings.dataType === 'binary') { + settings.xhr().responseType = 'arraybuffer'; + settings.processData = false; + } + } + }); + + /* volumes */ + { + const data = JSON.parse(settings.static_global("sound_volume", "{}")); + for(const sound_key of Object.keys(Sound)) { + if(typeof(data[Sound[sound_key]]) !== "undefined") + speech_volume[Sound[sound_key]] = data[Sound[sound_key]]; + } + + master_volume = typeof(data.master) === "number" ? data.master : 1; + overlap_sounds = typeof(data.overlap) === "boolean" ? data.overlap : true; + ignore_muted = typeof(data.ignore_muted) === "boolean" ? data.ignore_muted : false; } - export function initialize() : Promise { - $.ajaxSetup({ - beforeSend: function(jqXHR,settings){ - if (settings.dataType === 'binary') { - settings.xhr().responseType = 'arraybuffer'; - settings.processData = false; - } - } + register_sound("message.received", "effects/message_received.wav"); + register_sound("message.send", "effects/message_send.wav"); + + manager = new SoundManager(undefined); + return new Promise((resolve, reject) => { + $.ajax({ + url: "audio/speech/mapping.json", + success: response => { + if(typeof(response) === "string") + response = JSON.parse(response); + for(const entry of response) + register_sound(entry.key, "speech/" + entry.file); + resolve(); + }, + error: error => { + log.error(LogCategory.AUDIO, "error: %o", error); + reject(); + }, + timeout: 5000, + async: true, + type: 'GET' }); + }); +} - /* volumes */ - { - const data = JSON.parse(settings.static_global("sound_volume", "{}")); - for(const sound_key in Sound) { - if(typeof(data[Sound[sound_key]]) !== "undefined") - speech_volume[Sound[sound_key]] = data[Sound[sound_key]]; - } +export interface PlaybackOptions { + ignore_muted?: boolean; + ignore_overlap?: boolean; - master_volume = typeof(data.master) === "number" ? data.master : 1; - overlap_sounds = typeof(data.overlap) === "boolean" ? data.overlap : true; - ignore_muted = typeof(data.ignore_muted) === "boolean" ? data.ignore_muted : false; - } + default_volume?: number; - register_sound("message.received", "effects/message_received.wav"); - register_sound("message.send", "effects/message_send.wav"); + callback?: (flag: boolean) => any; +} - manager = new SoundManager(undefined); - audio.player.on_ready(reinitialisize_audio); - return new Promise((resolve, reject) => { - $.ajax({ - url: "audio/speech/mapping.json", - success: response => { - if(typeof(response) === "string") - response = JSON.parse(response); - for(const entry of response) - register_sound(entry.key, "speech/" + entry.file); - resolve(); - }, - error: error => { - log.error(LogCategory.AUDIO, "error: %o", error); - reject(); - }, - timeout: 5000, - async: true, - type: 'GET' - }); - }); +export async function resolve_sound(sound: Sound) : Promise { + const file: SoundHandle = speech_mapping[sound]; + if(!file) throw tr("Missing sound handle"); + + return file; +} + +export let manager: SoundManager; + +export class SoundManager { + private readonly _handle: ConnectionHandler; + private _playing_sounds: {[key: string]:number} = {}; + + constructor(handle: ConnectionHandler) { + this._handle = handle; } - export interface PlaybackOptions { - ignore_muted?: boolean; - ignore_overlap?: boolean; + play(_sound: Sound, options?: PlaybackOptions) { + options = options || {}; - default_volume?: number; + const volume = get_sound_volume(_sound, options.default_volume); + log.info(LogCategory.AUDIO, tr("Replaying sound %s (Sound volume: %o | Master volume %o)"), _sound, volume, master_volume); - callback?: (flag: boolean) => any; - } + if(volume == 0 || master_volume == 0) + return; - export async function resolve_sound(sound: Sound) : Promise { - const file: SoundHandle = speech_mapping[sound]; - if(!file) throw tr("Missing sound handle"); + if(this._handle && !options.ignore_muted && !ignore_output_muted() && this._handle.client_status.output_muted) + return; - return file; - } + resolve_sound(_sound).then(handle => { + if(!handle) return; - export let manager: SoundManager; - - export class SoundManager { - private readonly _handle: ConnectionHandler; - private _playing_sounds: {[key: string]:number} = {}; - - constructor(handle: ConnectionHandler) { - this._handle = handle; - } - - play(_sound: Sound, options?: PlaybackOptions) { - options = options || {}; - - const volume = get_sound_volume(_sound, options.default_volume); - log.info(LogCategory.AUDIO, tr("Replaying sound %s (Sound volume: %o | Master volume %o)"), _sound, volume, master_volume); - - if(volume == 0 || master_volume == 0) - return; - - if(this._handle && !options.ignore_muted && !sound.ignore_output_muted() && this._handle.client_status.output_muted) - return; - - const context = audio.player.context(); - if(!context) { - log.warn(LogCategory.AUDIO, tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback"), _sound); + if(!options.ignore_overlap && (this._playing_sounds[handle.filename] > 0) && !overlap_activated()) { + log.info(LogCategory.AUDIO, tr("Dropping requested playback for sound %s because it would overlap."), _sound); return; } - sound.resolve_sound(_sound).then(handle => { - if(!handle) return; - - if(!options.ignore_overlap && (this._playing_sounds[handle.filename] > 0) && !sound.overlap_activated()) { - log.info(LogCategory.AUDIO, tr("Dropping requested playback for sound %s because it would overlap."), _sound); - return; - } - - this._playing_sounds[handle.filename] = (this._playing_sounds[handle.filename] || 0) + 1; - audio.sounds.play_sound({ - path: "audio/" + handle.filename, - volume: volume * master_volume - }).then(() => { - if(options.callback) - options.callback(true); - }).catch(error => { - log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s: %o"), handle.filename, error); - if(options.callback) - options.callback(false); - }).then(() => { - this._playing_sounds[handle.filename]--; - }); + this._playing_sounds[handle.filename] = (this._playing_sounds[handle.filename] || 0) + 1; + sbackend.play_sound({ + path: "audio/" + handle.filename, + volume: volume * master_volume + }).then(() => { + if(options.callback) + options.callback(true); }).catch(error => { - log.warn(LogCategory.AUDIO, tr("Failed to replay sound %o because it could not be resolved: %o"), sound, error); + log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s: %o"), handle.filename, error); if(options.callback) options.callback(false); + }).then(() => { + this._playing_sounds[handle.filename]--; }); - } + }).catch(error => { + log.warn(LogCategory.AUDIO, tr("Failed to replay sound %o because it could not be resolved: %o"), _sound, error); + if(options.callback) + options.callback(false); + }); } } \ No newline at end of file diff --git a/shared/js/stats.ts b/shared/js/stats.ts index ce9ca84b..f18df9b7 100644 --- a/shared/js/stats.ts +++ b/shared/js/stats.ts @@ -1,243 +1,244 @@ -namespace stats { - const LOG_PREFIX = "[Statistics] "; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; - export enum CloseCodes { - UNSET = 3000, - RECONNECT = 3001, - INTERNAL_ERROR = 3002, +const LOG_PREFIX = "[Statistics] "; - BANNED = 3100, +enum CloseCodes { + UNSET = 3000, + RECONNECT = 3001, + INTERNAL_ERROR = 3002, + + BANNED = 3100, +} + +enum ConnectionState { + CONNECTING, + INITIALIZING, + CONNECTED, + UNSET +} + +export class SessionConfig { + /* + * All collected statistics will only be cached by the stats server. + * No data will be saved. + */ + volatile_collection_only?: boolean; + + /* + * Anonymize all IP addresses which will be provided while the stats collection. + * This option is quite useless when volatile_collection_only is active. + */ + anonymize_ip_addresses?: boolean; +} + +export class Config extends SessionConfig { + verbose?: boolean; + + reconnect_interval?: number; +} + +export interface UserCountData { + online_users: number; + unique_online_users: number; +} + +export type UserCountListener = (data: UserCountData) => any; + +let reconnect_timer: NodeJS.Timer; +let current_config: Config; + +let last_user_count_update: number; +let user_count_listener: UserCountListener[] = []; + +const DEFAULT_CONFIG: Config = { + verbose: true, + reconnect_interval: 5000, + anonymize_ip_addresses: true, + volatile_collection_only: false +}; + +function initialize_config_object(target_object: any, source_object: any) : any { + for(const key of Object.keys(source_object)) { + if(typeof(source_object[key]) === 'object') + initialize_config_object(target_object[key] || (target_object[key] = {}), source_object[key]); + + if(typeof(target_object[key]) !== 'undefined') + continue; + + target_object[key] = source_object[key]; } - enum ConnectionState { - CONNECTING, - INITIALIZING, - CONNECTED, - UNSET - } + return target_object; +} - export class SessionConfig { - /* - * All collected statistics will only be cached by the stats server. - * No data will be saved. - */ - volatile_collection_only?: boolean; +export function initialize(config: Config) { + current_config = initialize_config_object(config || {}, DEFAULT_CONFIG); + if(current_config.verbose) + log.info(LogCategory.STATISTICS, tr("Initializing statistics with this config: %o"), current_config); - /* - * Anonymize all IP addresses which will be provided while the stats collection. - * This option is quite useless when volatile_collection_only is active. - */ - anonymize_ip_addresses?: boolean; - } + connection.start_connection(); +} - export class Config extends SessionConfig { - verbose?: boolean; +export function register_user_count_listener(listener: UserCountListener) { + user_count_listener.push(listener); +} - reconnect_interval?: number; - } +export function all_user_count_listener() : UserCountListener[] { + return user_count_listener; +} - export interface UserCountData { - online_users: number; - unique_online_users: number; - } +export function deregister_user_count_listener(listener: UserCountListener) { + user_count_listener.remove(listener); +} - export type UserCountListener = (data: UserCountData) => any; +namespace connection { + let connection: WebSocket; + export let connection_state: ConnectionState = ConnectionState.UNSET; - let reconnect_timer: NodeJS.Timer; - let current_config: Config; + export function start_connection() { + cancel_reconnect(); + close_connection(); - let last_user_count_update: number; - let user_count_listener: UserCountListener[] = []; + connection_state = ConnectionState.CONNECTING; - const DEFAULT_CONFIG: Config = { - verbose: true, - reconnect_interval: 5000, - anonymize_ip_addresses: true, - volatile_collection_only: false - }; + connection = new WebSocket('wss://web-stats.teaspeak.de:27790'); + if(!connection) + connection = new WebSocket('wss://localhost:27788'); - function initialize_config_object(target_object: any, source_object: any) : any { - for(const key of Object.keys(source_object)) { - if(typeof(source_object[key]) === 'object') - initialize_config_object(target_object[key] || (target_object[key] = {}), source_object[key]); + { + const connection_copy = connection; + connection.onclose = (event: CloseEvent) => { + if(connection_copy !== connection) return; - if(typeof(target_object[key]) !== 'undefined') - continue; + if(current_config.verbose) + log.warn(LogCategory.STATISTICS, tr("Lost connection to statistics server (Connection closed). Reason: %o. Event object: %o"), CloseCodes[event.code] || event.code, event); - target_object[key] = source_object[key]; - } - - return target_object; - } - - export function initialize(config: Config) { - current_config = initialize_config_object(config || {}, DEFAULT_CONFIG); - if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Initializing statistics with this config: %o"), current_config); - - connection.start_connection(); - } - - export function register_user_count_listener(listener: UserCountListener) { - user_count_listener.push(listener); - } - - export function all_user_count_listener() : UserCountListener[] { - return user_count_listener; - } - - export function deregister_user_count_listener(listener: UserCountListener) { - user_count_listener.remove(listener); - } - - namespace connection { - let connection: WebSocket; - export let connection_state: ConnectionState = ConnectionState.UNSET; - - export function start_connection() { - cancel_reconnect(); - close_connection(); - - connection_state = ConnectionState.CONNECTING; - - connection = new WebSocket('wss://web-stats.teaspeak.de:27790'); - if(!connection) - connection = new WebSocket('wss://localhost:27788'); - - { - const connection_copy = connection; - connection.onclose = (event: CloseEvent) => { - if(connection_copy !== connection) return; - - if(current_config.verbose) - log.warn(LogCategory.STATISTICS, tr("Lost connection to statistics server (Connection closed). Reason: %o. Event object: %o"), CloseCodes[event.code] || event.code, event); - - if(event.code != CloseCodes.BANNED) - invoke_reconnect(); - }; - - connection.onopen = () => { - if(connection_copy !== connection) return; - - if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Successfully connected to server. Initializing session.")); - - connection_state = ConnectionState.INITIALIZING; - initialize_session(); - }; - - connection.onerror = (event: ErrorEvent) => { - if(connection_copy !== connection) return; - - if(current_config.verbose) - log.warn(LogCategory.STATISTICS, tr("Received an error. Closing connection. Object: %o"), event); - - connection.close(CloseCodes.INTERNAL_ERROR); + if(event.code != CloseCodes.BANNED) invoke_reconnect(); - }; + }; - connection.onmessage = (event: MessageEvent) => { - if(connection_copy !== connection) return; + connection.onopen = () => { + if(connection_copy !== connection) return; - if(typeof(event.data) !== 'string') { - if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Received an message which isn't a string. Event object: %o"), event); - return; - } + if(current_config.verbose) + log.info(LogCategory.STATISTICS, tr("Successfully connected to server. Initializing session.")); - handle_message(event.data as string); - }; - } + connection_state = ConnectionState.INITIALIZING; + initialize_session(); + }; + + connection.onerror = (event: ErrorEvent) => { + if(connection_copy !== connection) return; + + if(current_config.verbose) + log.warn(LogCategory.STATISTICS, tr("Received an error. Closing connection. Object: %o"), event); + + connection.close(CloseCodes.INTERNAL_ERROR); + invoke_reconnect(); + }; + + connection.onmessage = (event: MessageEvent) => { + if(connection_copy !== connection) return; + + if(typeof(event.data) !== 'string') { + if(current_config.verbose) + log.info(LogCategory.STATISTICS, tr("Received an message which isn't a string. Event object: %o"), event); + return; + } + + handle_message(event.data as string); + }; + } + } + + export function close_connection() { + if(connection) { + const connection_copy = connection; + connection = undefined; + + try { + connection_copy.close(3001); + } catch(_) {} + } + } + + function invoke_reconnect() { + close_connection(); + + if(reconnect_timer) { + clearTimeout(reconnect_timer); + reconnect_timer = undefined; } - export function close_connection() { - if(connection) { - const connection_copy = connection; - connection = undefined; - - try { - connection_copy.close(3001); - } catch(_) {} - } - } - - function invoke_reconnect() { - close_connection(); - - if(reconnect_timer) { - clearTimeout(reconnect_timer); - reconnect_timer = undefined; - } + if(current_config.verbose) + log.info(LogCategory.STATISTICS, tr("Scheduled reconnect in %dms"), current_config.reconnect_interval); + reconnect_timer = setTimeout(() => { if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Scheduled reconnect in %dms"), current_config.reconnect_interval); + log.info(LogCategory.STATISTICS, tr("Reconnecting")); + start_connection(); + }, current_config.reconnect_interval); + } - reconnect_timer = setTimeout(() => { - if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Reconnecting")); - start_connection(); - }, current_config.reconnect_interval); + export function cancel_reconnect() { + if(reconnect_timer) { + clearTimeout(reconnect_timer); + reconnect_timer = undefined; + } + } + + function send_message(type: string, data: any) { + connection.send(JSON.stringify({ + type: type, + data: data + })); + } + + function initialize_session() { + const config_object = {}; + for(const key in SessionConfig) { + if(SessionConfig.hasOwnProperty(key)) + config_object[key] = current_config[key]; } - export function cancel_reconnect() { - if(reconnect_timer) { - clearTimeout(reconnect_timer); - reconnect_timer = undefined; - } + send_message('initialize', { + config: config_object + }) + } + + function handle_message(message: string) { + const data_object = JSON.parse(message); + const type = data_object.type as string; + const data = data_object.data; + + if(typeof(handler[type]) === 'function') { + if(current_config.verbose) + log.debug(LogCategory.STATISTICS, tr("Handling message of type %s"), type); + handler[type](data); + } else if(current_config.verbose) { + log.warn(LogCategory.STATISTICS, tr("Received message with an unknown type (%s). Dropping message. Full message: %o"), type, data_object); + } + } + + namespace handler { + interface NotifyUserCount extends UserCountData { } + + function handle_notify_user_count(data: NotifyUserCount) { + last_user_count_update = Date.now(); + for(const listener of [...user_count_listener]) + listener(data); } - function send_message(type: string, data: any) { - connection.send(JSON.stringify({ - type: type, - data: data - })); + interface NotifyInitialized {} + function handle_notify_initialized(json: NotifyInitialized) { + if(current_config.verbose) + log.info(LogCategory.STATISTICS, tr("Session successfully initialized.")); + + connection_state = ConnectionState.CONNECTED; } - function initialize_session() { - const config_object = {}; - for(const key in SessionConfig) { - if(SessionConfig.hasOwnProperty(key)) - config_object[key] = current_config[key]; - } - - send_message('initialize', { - config: config_object - }) - } - - function handle_message(message: string) { - const data_object = JSON.parse(message); - const type = data_object.type as string; - const data = data_object.data; - - if(typeof(handler[type]) === 'function') { - if(current_config.verbose) - log.debug(LogCategory.STATISTICS, tr("Handling message of type %s"), type); - handler[type](data); - } else if(current_config.verbose) { - log.warn(LogCategory.STATISTICS, tr("Received message with an unknown type (%s). Dropping message. Full message: %o"), type, data_object); - } - } - - namespace handler { - interface NotifyUserCount extends UserCountData { } - - function handle_notify_user_count(data: NotifyUserCount) { - last_user_count_update = Date.now(); - for(const listener of [...user_count_listener]) - listener(data); - } - - interface NotifyInitialized {} - function handle_notify_initialized(json: NotifyInitialized) { - if(current_config.verbose) - log.info(LogCategory.STATISTICS, tr("Session successfully initialized.")); - - connection_state = ConnectionState.CONNECTED; - } - - handler["notifyinitialized"] = handle_notify_initialized; - handler["notifyusercount"] = handle_notify_user_count; - } + handler["notifyinitialized"] = handle_notify_initialized; + handler["notifyusercount"] = handle_notify_user_count; } } \ No newline at end of file diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts index 1a510f05..7dc818e4 100644 --- a/shared/js/ui/channel.ts +++ b/shared/js/ui/channel.ts @@ -1,12 +1,26 @@ -/// -/// +import {ChannelTree} from "tc-shared/ui/view"; +import {ClientEntry} from "tc-shared/ui/client"; +import * as log from "tc-shared/log"; +import {LogCategory, LogType} from "tc-shared/log"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {settings, Settings} from "tc-shared/settings"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {Sound} from "tc-shared/sound/Sounds"; +import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import * as htmltags from "./htmltags"; +import {hashPassword} from "tc-shared/utils/helpers"; +import * as server_log from "tc-shared/ui/frames/server_log"; +import {openChannelInfo} from "tc-shared/ui/modal/ModalChannelInfo"; +import {createChannelModal} from "tc-shared/ui/modal/ModalCreateChannel"; +import {formatMessage} from "tc-shared/ui/frames/chat"; -enum ChannelType { +export enum ChannelType { PERMANENT, SEMI_PERMANENT, TEMPORARY } -namespace ChannelType { +export namespace ChannelType { export function normalize(mode: ChannelType) { let value: string = ChannelType[mode]; value = value.toLowerCase(); @@ -14,13 +28,13 @@ namespace ChannelType { } } -enum ChannelSubscribeMode { +export enum ChannelSubscribeMode { SUBSCRIBED, UNSUBSCRIBED, INHERITED } -class ChannelProperties { +export class ChannelProperties { channel_order: number = 0; channel_name: string = ""; channel_name_phonetic: string = ""; @@ -55,7 +69,7 @@ class ChannelProperties { channel_conversation_history_length: number = -1; } -class ChannelEntry { +export class ChannelEntry { channelTree: ChannelTree; channelId: number; parent?: ChannelEntry; @@ -525,7 +539,7 @@ class ChannelEntry { name: tr("Show channel info"), callback: () => { trigger_close = false; - Modals.openChannelInfo(this); + openChannelInfo(this); }, icon_class: "client-about" }, @@ -565,7 +579,7 @@ class ChannelEntry { name: tr("Edit channel"), invalidPermission: !channelModify, callback: () => { - Modals.createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => { + createChannelModal(this.channelTree.client, this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => { if(changes) { changes["cid"] = this.channelId; this.channelTree.client.serverConnection.send_command("channeledit", changes); @@ -617,7 +631,7 @@ class ChannelEntry { error = error.extra_message || error.message; } - createErrorModal(tr("Failed to create bot"), MessageHelper.formatMessage(tr("Failed to create the music bot:
{0}"), error)).open(); + createErrorModal(tr("Failed to create bot"), formatMessage(tr("Failed to create the music bot:
{0}"), error)).open(); }); } }, @@ -834,7 +848,7 @@ class ChannelEntry { !this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_JOIN_IGNORE_PASSWORD).granted(1)) { createInputModal(tr("Channel password"), tr("Channel password:"), () => true, text => { if(typeof(text) == typeof(true)) return; - helpers.hashPassword(text as string).then(result => { + hashPassword(text as string).then(result => { this._cachedPassword = result; this.joinChannel(); this.updateChannelTypeIcon(); @@ -923,7 +937,7 @@ class ChannelEntry { this._tag_channel.find(".marker-text-unread").toggleClass("hidden", !flag); } - log_data() : log.server.base.Channel { + log_data() : server_log.base.Channel { return { channel_name: this.channelName(), channel_id: this.channelId diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts index d898f8f7..d6a73852 100644 --- a/shared/js/ui/client.ts +++ b/shared/js/ui/client.ts @@ -1,8 +1,34 @@ -/// -/// -/// +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {channel_tree, Registry} from "tc-shared/events"; +import {ChannelTree} from "tc-shared/ui/view"; +import * as log from "tc-shared/log"; +import {LogCategory, LogType} from "tc-shared/log"; +import {Settings, settings} from "tc-shared/settings"; +import {KeyCode, SpecialKey} from "tc-shared/PPTListener"; +import {Sound} from "tc-shared/sound/Sounds"; +import {Group, GroupManager, GroupTarget, GroupType} from "tc-shared/permission/GroupManager"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {createErrorModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import * as htmltags from "tc-shared/ui/htmltags"; +import * as server_log from "tc-shared/ui/frames/server_log"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler"; +import {voice} from "tc-shared/connection/ConnectionBase"; +import VoiceClient = voice.VoiceClient; +import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit"; +import {createServerGroupAssignmentModal} from "tc-shared/ui/modal/ModalGroupAssignment"; +import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo"; +import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient"; +import {spawnChangeVolume} from "tc-shared/ui/modal/ModalChangeVolume"; +import {spawnChangeLatency} from "tc-shared/ui/modal/ModalChangeLatency"; +import {spawnPlaylistEdit} from "tc-shared/ui/modal/ModalPlaylistEdit"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import * as ppt from "tc-backend/ppt"; +import * as hex from "tc-shared/crypto/hex"; -enum ClientType { +export enum ClientType { CLIENT_VOICE, CLIENT_QUERY, CLIENT_INTERNAL, @@ -11,7 +37,7 @@ enum ClientType { CLIENT_UNDEFINED } -class ClientProperties { +export class ClientProperties { client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type client_type_exact: ClientType = ClientType.CLIENT_VOICE; @@ -57,7 +83,7 @@ class ClientProperties { client_is_priority_speaker: boolean = false; } -class ClientConnectionInfo { +export class ClientConnectionInfo { connection_bandwidth_received_last_minute_control: number = -1; connection_bandwidth_received_last_minute_keepalive: number = -1; connection_bandwidth_received_last_minute_speech: number = -1; @@ -109,8 +135,8 @@ class ClientConnectionInfo { connection_client_port: number = -1; } -class ClientEntry { - readonly events: events.Registry; +export class ClientEntry { + readonly events: Registry; protected _clientId: number; protected _channel: ChannelEntry; @@ -121,7 +147,7 @@ class ClientEntry { protected _speaking: boolean; protected _listener_initialized: boolean; - protected _audio_handle: connection.voice.VoiceClient; + protected _audio_handle: VoiceClient; protected _audio_volume: number; protected _audio_muted: boolean; @@ -136,7 +162,7 @@ class ClientEntry { channelTree: ChannelTree; constructor(clientId: number, clientName, properties: ClientProperties = new ClientProperties()) { - this.events = new events.Registry(); + this.events = new Registry(); this._properties = properties; this._properties.client_nickname = clientName; @@ -181,7 +207,7 @@ class ClientEntry { this._channel = undefined; } - set_audio_handle(handle: connection.voice.VoiceClient) { + set_audio_handle(handle: VoiceClient) { if(this._audio_handle === handle) return; @@ -200,7 +226,7 @@ class ClientEntry { handle.callback_stopped = () => this.speaking = false; } - get_audio_handle() : connection.voice.VoiceClient { + get_audio_handle() : VoiceClient { return this._audio_handle; } @@ -285,7 +311,7 @@ class ClientEntry { let clients = this.channelTree.currently_selected as (ClientEntry | ClientEntry[]); - if(ppt.key_pressed(ppt.SpecialKey.SHIFT)) { + if(ppt.key_pressed(SpecialKey.SHIFT)) { if(clients != this && !($.isArray(clients) && clients.indexOf(this) != -1)) clients = $.isArray(clients) ? [...clients, this] : [clients, this]; } else { @@ -418,20 +444,20 @@ class ClientEntry { type: contextmenu.MenuEntryType.ENTRY, icon_class: "client-permission_client", name: tr("Client permissions"), - callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clp", {unique_id: this.clientUid()}).open() + callback: () => spawnPermissionEdit(this.channelTree.client, "clp", {unique_id: this.clientUid()}).open() }, { type: contextmenu.MenuEntryType.ENTRY, icon_class: "client-permission_client", name: tr("Client channel permissions"), - callback: () => Modals.spawnPermissionEdit(this.channelTree.client, "clchp", {unique_id: this.clientUid(), channel_id: this._channel ? this._channel.channelId : undefined }).open() + callback: () => spawnPermissionEdit(this.channelTree.client, "clchp", {unique_id: this.clientUid(), channel_id: this._channel ? this._channel.channelId : undefined }).open() } ] }]; } open_assignment_modal() { - Modals.createServerGroupAssignmentModal(this, (groups, flag) => { + createServerGroupAssignmentModal(this, (groups, flag) => { if(groups.length == 0) return Promise.resolve(true); if(groups.length == 1) { @@ -490,7 +516,7 @@ class ClientEntry { type: contextmenu.MenuEntryType.ENTRY, icon_class: "client-about", name: tr("Show client info"), - callback: () => Modals.openClientInfo(this) + callback: () => openClientInfo(this) }, contextmenu.Entry.HR(), { @@ -582,7 +608,7 @@ class ClientEntry { name: tr("Ban client"), invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), callback: () => { - Modals.spawnBanClient(this.channelTree.client, [{ + spawnBanClient(this.channelTree.client, [{ name: this.properties.client_nickname, unique_id: this.properties.client_unique_identifier }], (data) => { @@ -622,7 +648,7 @@ class ClientEntry { icon_class: "client-volume", name: tr("Change Volume"), callback: () => { - Modals.spawnChangeVolume(this, true, this._audio_volume, undefined, volume => { + spawnChangeVolume(this, true, this._audio_volume, undefined, volume => { this._audio_volume = volume; this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume); if(this._audio_handle) @@ -635,7 +661,7 @@ class ClientEntry { type: contextmenu.MenuEntryType.ENTRY, name: tr("Change playback latency"), callback: () => { - Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { + spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { this._audio_handle.reset_latency_settings(); return this._audio_handle.latency_settings(); }, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => { @@ -843,7 +869,7 @@ class ClientEntry { if(variable.key == "client_nickname") { if(variable.value !== old_value && typeof(old_value) === "string") { if(!(this instanceof LocalClientEntry)) { /* own changes will be logged somewhere else */ - this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, { + this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, { own_client: false, client: this.log_data(), new_name: variable.value, @@ -1082,7 +1108,7 @@ class ClientEntry { this.tag.css('padding-left', (5 + (index + 2) * 16) + "px"); } - log_data() : log.server.base.Client { + log_data() : server_log.base.Client { return { client_unique_id: this.properties.client_unique_identifier, client_name: this.clientNickName(), @@ -1123,7 +1149,7 @@ class ClientEntry { } } -class LocalClientEntry extends ClientEntry { +export class LocalClientEntry extends ClientEntry { handle: ConnectionHandler; private renaming: boolean; @@ -1216,14 +1242,14 @@ class LocalClientEntry extends ClientEntry { const old_name = this.clientNickName(); this.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => { settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, text); - this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGED, { + this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGED, { client: this.log_data(), old_name: old_name, new_name: text, own_client: true }); }).catch((e: CommandResult) => { - this.channelTree.client.log.log(log.server.Type.CLIENT_NICKNAME_CHANGE_FAILED, { + this.channelTree.client.log.log(server_log.Type.CLIENT_NICKNAME_CHANGE_FAILED, { reason: e.extra_message }); this.openRename(); @@ -1232,7 +1258,7 @@ class LocalClientEntry extends ClientEntry { } } -class MusicClientProperties extends ClientProperties { +export class MusicClientProperties extends ClientProperties { player_state: number = 0; player_volume: number = 0; @@ -1264,7 +1290,7 @@ class MusicClientProperties extends ClientProperties { } */ -class SongInfo { +export class SongInfo { song_id: number = 0; song_url: string = ""; song_invoker: number = 0; @@ -1277,7 +1303,7 @@ class SongInfo { song_length: number = 0; } -class MusicClientPlayerInfo extends SongInfo { +export class MusicClientPlayerInfo extends SongInfo { bot_id: number = 0; player_state: number = 0; @@ -1290,7 +1316,7 @@ class MusicClientPlayerInfo extends SongInfo { player_description: string = ""; } -class MusicClientEntry extends ClientEntry { +export class MusicClientEntry extends ClientEntry { private _info_promise: Promise; private _info_promise_age: number = 0; private _info_promise_resolve: any; @@ -1366,7 +1392,7 @@ class MusicClientEntry extends ClientEntry { this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => { for(const entry of lists) { if(entry.playlist_id == this.properties.client_playlist_id) { - Modals.spawnPlaylistEdit(this.channelTree.client, entry); + spawnPlaylistEdit(this.channelTree.client, entry); return; } } @@ -1435,7 +1461,7 @@ class MusicClientEntry extends ClientEntry { icon_class: "client-volume", name: tr("Change local volume"), callback: () => { - Modals.spawnChangeVolume(this, true, this._audio_handle.get_volume(), undefined, volume => { + spawnChangeVolume(this, true, this._audio_handle.get_volume(), undefined, volume => { this.channelTree.client.settings.changeServer("volume_client_" + this.clientUid(), volume); this._audio_handle.set_volume(volume); }); @@ -1450,7 +1476,7 @@ class MusicClientEntry extends ClientEntry { if(max_volume < 0) max_volume = 100; - Modals.spawnChangeVolume(this, false, this.properties.player_volume, max_volume / 100, value => { + spawnChangeVolume(this, false, this.properties.player_volume, max_volume / 100, value => { if(typeof(value) !== "number") return; @@ -1467,7 +1493,7 @@ class MusicClientEntry extends ClientEntry { type: contextmenu.MenuEntryType.ENTRY, name: tr("Change playback latency"), callback: () => { - Modals.spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { + spawnChangeLatency(this, this._audio_handle.latency_settings(), () => { this._audio_handle.reset_latency_settings(); return this._audio_handle.latency_settings(); }, settings => this._audio_handle.latency_settings(settings), this._audio_handle.support_flush ? () => { @@ -1482,8 +1508,8 @@ class MusicClientEntry extends ClientEntry { icon_class: "client-delete", disabled: false, callback: () => { - const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false))); - Modals.spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => { + const tag = $.spawn("div").append(formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false))); + spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => { if(result) { this.channelTree.client.serverConnection.send_command("musicbotdelete", { bot_id: this.properties.client_database_id diff --git a/shared/js/ui/client_move.ts b/shared/js/ui/client_move.ts index 5ccfa8a1..da548c80 100644 --- a/shared/js/ui/client_move.ts +++ b/shared/js/ui/client_move.ts @@ -1,6 +1,10 @@ -/// +import {ChannelTree} from "tc-shared/ui/view"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {ClientEntry} from "tc-shared/ui/client"; +import {ChannelEntry} from "tc-shared/ui/channel"; -class ClientMover { +export class ClientMover { static readonly listener_root = $(document); static readonly move_element = $("#mouse-move"); readonly channel_tree: ChannelTree; diff --git a/shared/js/ui/elements/context_divider.ts b/shared/js/ui/elements/ContextDivider.ts similarity index 94% rename from shared/js/ui/elements/context_divider.ts rename to shared/js/ui/elements/ContextDivider.ts index 3ec65814..597716b4 100644 --- a/shared/js/ui/elements/context_divider.ts +++ b/shared/js/ui/elements/ContextDivider.ts @@ -1,5 +1,15 @@ -interface JQuery { - dividerfy() : this; +import {settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; + +declare global { + interface JQuery { + dividerfy() : this; + } +} + +export function initialize() { + } if(!$.fn.dividerfy) { @@ -58,8 +68,8 @@ if(!$.fn.dividerfy) { Math.max(previous_offset.top + previous_element.height(), next_offset.top + next_element.height()); */ - let previous = 0; - let next = 0; + let previous; + let next; if(current < min) { previous = 0; next = 1; @@ -89,7 +99,7 @@ if(!$.fn.dividerfy) { })); }; - const listener_up = (event: MouseEvent) => { + const listener_up = () => { document.removeEventListener('mousemove', listener_move); document.removeEventListener('touchmove', listener_move); diff --git a/shared/js/ui/elements/context_menu.ts b/shared/js/ui/elements/ContextMenu.ts similarity index 68% rename from shared/js/ui/elements/context_menu.ts rename to shared/js/ui/elements/ContextMenu.ts index f56eb023..159d382a 100644 --- a/shared/js/ui/elements/context_menu.ts +++ b/shared/js/ui/elements/ContextMenu.ts @@ -1,82 +1,80 @@ -namespace contextmenu { - export interface MenuEntry { - callback?: () => void; - type: MenuEntryType; - name: (() => string) | string; - icon_class?: string; - icon_path?: string; - disabled?: boolean; - visible?: boolean; +export interface MenuEntry { + callback?: () => void; + type: MenuEntryType; + name: (() => string) | string; + icon_class?: string; + icon_path?: string; + disabled?: boolean; + visible?: boolean; - checkbox_checked?: boolean; + checkbox_checked?: boolean; - invalidPermission?: boolean; - sub_menu?: MenuEntry[]; - } + invalidPermission?: boolean; + sub_menu?: MenuEntry[]; +} - export enum MenuEntryType { - CLOSE, - ENTRY, - CHECKBOX, - HR, - SUB_MENU - } +export enum MenuEntryType { + CLOSE, + ENTRY, + CHECKBOX, + HR, + SUB_MENU +} - export class Entry { - static HR() { - return { - callback: () => {}, - type: MenuEntryType.HR, - name: "", - icon: "" - }; +export class Entry { + static HR() { + return { + callback: () => {}, + type: MenuEntryType.HR, + name: "", + icon: "" }; + }; - static CLOSE(callback: () => void) { - return { - callback: callback, - type: MenuEntryType.CLOSE, - name: "", - icon: "" - }; - } - } - - export interface ContextMenuProvider { - despawn_context_menu(); - spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]); - - initialize(); - finalize(); - - html_format_enabled() : boolean; - } - - let provider: ContextMenuProvider; - export function spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) { - if(!provider) { - console.error(tr("Failed to spawn context menu! Missing provider!")); - return; - } - - provider.spawn_context_menu(x, y, ...entries); - } - - export function despawn_context_menu() { - if(!provider) - return; - - provider.despawn_context_menu(); - } - - export function get_provider() : ContextMenuProvider { return provider; } - export function set_provider(_provider: ContextMenuProvider) { - provider = _provider; - provider.initialize(); + static CLOSE(callback: () => void) { + return { + callback: callback, + type: MenuEntryType.CLOSE, + name: "", + icon: "" + }; } } -class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { +export interface ContextMenuProvider { + despawn_context_menu(); + spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]); + + initialize(); + finalize(); + + html_format_enabled() : boolean; +} + +let provider: ContextMenuProvider; +export function spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) { + if(!provider) { + console.error(tr("Failed to spawn context menu! Missing provider!")); + return; + } + + provider.spawn_context_menu(x, y, ...entries); +} + +export function despawn_context_menu() { + if(!provider) + return; + + provider.despawn_context_menu(); +} + +export function get_provider() : ContextMenuProvider { return provider; } +export function set_provider(_provider: ContextMenuProvider) { + provider = _provider; + provider.initialize(); +} + +class HTMLContextMenuProvider implements ContextMenuProvider { private _global_click_listener: (event) => any; private _context_menu: JQuery; private _close_callbacks: (() => any)[] = []; @@ -118,10 +116,10 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { } } - private generate_tag(entry: contextmenu.MenuEntry) : JQuery { - if(entry.type == contextmenu.MenuEntryType.HR) { + private generate_tag(entry: MenuEntry) : JQuery { + if(entry.type == MenuEntryType.HR) { return $.spawn("hr"); - } else if(entry.type == contextmenu.MenuEntryType.ENTRY) { + } else if(entry.type == MenuEntryType.ENTRY) { let icon = entry.icon_class; if(!icon || icon.length == 0) icon = "icon_empty"; else icon = "icon " + icon; @@ -141,7 +139,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { }); } return tag; - } else if(entry.type == contextmenu.MenuEntryType.CHECKBOX) { + } else if(entry.type == MenuEntryType.CHECKBOX) { let checkbox = $.spawn("label").addClass("ccheckbox"); $.spawn("input").attr("type", "checkbox").prop("checked", !!entry.checkbox_checked).appendTo(checkbox); $.spawn("span").addClass("checkmark").appendTo(checkbox); @@ -161,7 +159,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { }); } return tag; - } else if(entry.type == contextmenu.MenuEntryType.SUB_MENU) { + } else if(entry.type == MenuEntryType.SUB_MENU) { let icon = entry.icon_class; if(!icon || icon.length == 0) icon = "icon_empty"; else icon = "icon " + icon; @@ -187,7 +185,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { return $.spawn("div").text("undefined"); } - spawn_context_menu(x: number, y: number, ...entries: contextmenu.MenuEntry[]) { + spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) { this._visible = true; let menu_tag = this._context_menu || (this._context_menu = $(".context-menu")); @@ -200,7 +198,7 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { if(typeof(entry.visible) === 'boolean' && !entry.visible) continue; - if(entry.type == contextmenu.MenuEntryType.CLOSE) { + if(entry.type == MenuEntryType.CLOSE) { if(entry.callback) this._close_callbacks.push(entry.callback); } else @@ -225,5 +223,4 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider { return true; } } - -contextmenu.set_provider(new HTMLContextMenuProvider()); \ No newline at end of file +set_provider(new HTMLContextMenuProvider()); \ No newline at end of file diff --git a/shared/js/ui/elements/modal.ts b/shared/js/ui/elements/Modal.ts similarity index 85% rename from shared/js/ui/elements/modal.ts rename to shared/js/ui/elements/Modal.ts index a0030309..1af05d0f 100644 --- a/shared/js/ui/elements/modal.ts +++ b/shared/js/ui/elements/Modal.ts @@ -1,13 +1,13 @@ -/// +import {KeyCode} from "tc-shared/PPTListener"; -enum ElementType { +export enum ElementType { HEADER, BODY, FOOTER } -type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; -const ModalFunctions = { +export type BodyCreator = (() => JQuery | JQuery[] | string) | string | JQuery | JQuery[]; +export const ModalFunctions = { divify: function (val: JQuery) { if(val.length > 1) return $.spawn("div").append(val); @@ -54,7 +54,7 @@ const ModalFunctions = { } }; -class ModalProperties { +export class ModalProperties { template?: string; header: BodyCreator = () => "HEADER"; body: BodyCreator = () => "BODY"; @@ -184,7 +184,7 @@ let _global_modal_count = 0; let _global_modal_last: HTMLElement; let _global_modal_last_time: number; -class Modal { +export class Modal { private _htmlTag: JQuery; properties: ModalProperties; shown: boolean; @@ -296,11 +296,11 @@ class Modal { } } -function createModal(data: ModalProperties | any) : Modal { +export function createModal(data: ModalProperties | any) : Modal { return new Modal(ModalFunctions.warpProperties(data)); } -class InputModalProperties extends ModalProperties { +export class InputModalProperties extends ModalProperties { maxLength?: number; field_title?: string; @@ -310,7 +310,7 @@ class InputModalProperties extends ModalProperties { error_message?: string; } -function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { +export function createInputModal(headMessage: BodyCreator, question: BodyCreator, validator: (input: string) => boolean, callback: (flag: boolean | string) => void, props: InputModalProperties | any = {}) : Modal { props = ModalFunctions.warpProperties(props); props.template_properties || (props.template_properties = {}); props.template_properties.field_title = props.field_title; @@ -370,7 +370,7 @@ function createInputModal(headMessage: BodyCreator, question: BodyCreator, valid return modal; } -function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +export function createErrorModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; @@ -382,7 +382,7 @@ function createErrorModal(header: BodyCreator, message: BodyCreator, props: Moda return modal; } -function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { +export function createInfoModal(header: BodyCreator, message: BodyCreator, props: ModalProperties | any = { footer: undefined }) { props = ModalFunctions.warpProperties(props); (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; @@ -394,52 +394,6 @@ function createInfoModal(header: BodyCreator, message: BodyCreator, props: Modal return modal; } -/* extend jquery */ - -interface ModalElements { - header?: BodyCreator; - body?: BodyCreator; - footer?: BodyCreator; -} - -interface JQuery { - modalize(entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal; -} - -$.fn.modalize = function (this: JQuery, entry_callback?: (header: JQuery, body: JQuery, footer: JQuery) => ModalElements | void, properties?: ModalProperties | any) : Modal { - properties = properties || {} as ModalProperties; - entry_callback = entry_callback || ((a,b,c) => undefined); - - let tag_modal = this[0].tagName.toLowerCase() == "modal" ? this : undefined; /* TODO may throw exception? */ - - let tag_head = tag_modal ? tag_modal.find("modal-header") : ModalFunctions.jqueriefy(properties.header); - let tag_body = tag_modal ? tag_modal.find("modal-body") : this; - let tag_footer = tag_modal ? tag_modal.find("modal-footer") : ModalFunctions.jqueriefy(properties.footer); - - const result = entry_callback(tag_head as any, tag_body, tag_footer as any) || {}; - properties.header = result.header || tag_head; - properties.body = result.body || tag_body; - properties.footer = result.footer || tag_footer; - return createModal(properties); -}; - - - - - - - - - - - - - - - - - - diff --git a/shared/js/ui/elements/NetGraph.ts b/shared/js/ui/elements/NetGraph.ts new file mode 100644 index 00000000..6f713191 --- /dev/null +++ b/shared/js/ui/elements/NetGraph.ts @@ -0,0 +1,433 @@ +export type Entry = { + timestamp: number; + + upload?: number; + download?: number; + + highlight?: boolean; +} + +export type Style = { + background_color: string; + + separator_color: string; + separator_count: number; + separator_width: number; + + upload: { + fill: string; + stroke: string; + strike_width: number; + }, + download: { + fill: string; + stroke: string; + strike_width: number; + } +} + +export type TimeSpan = { + origin: { + begin: number; + end: number; + time: number; + }, + target: { + begin: number; + end: number; + time: number; + } +} + +/* Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves + * + * Assuming A was the last point in the line plotted and B is the new point, + * we draw a curve with control points P and Q as below. + * + * A---P + * | + * | + * | + * Q---B + * + * Importantly, A and P are at the same y coordinate, as are B and Q. This is + * so adjacent curves appear to flow as one. + */ +export class Graph { + private static _loops: (() => any)[] = []; + + readonly canvas: HTMLCanvasElement; + public style: Style = { + background_color: "#28292b", + //background_color: "red", + + separator_color: "#283036", + //separator_color: 'blue', + separator_count: 10, + separator_width: 1, + + + upload: { + fill: "#2d3f4d", + stroke: "#336e9f", + strike_width: 2, + }, + + download: { + fill: "#532c26", + stroke: "#a9321c", + strike_width: 2, + } + }; + + private _canvas_context: CanvasRenderingContext2D; + private _entries: Entry[] = []; + private _entry_max = { + upload: 1, + download: 1, + }; + private _max_space = 1.12; + private _max_gap = 5; + private _listener_mouse_move; + private _listener_mouse_out; + private _animate_loop; + + _time_span: TimeSpan = { + origin: { + begin: 0, + end: 1, + time: 0 + }, + target: { + begin: 0, + end: 1, + time: 1 + } + }; + + private _detailed_shown = false; + callback_detailed_info: (upload: number, download: number, timestamp: number, event: MouseEvent) => any; + callback_detailed_hide: () => any; + + constructor(canvas: HTMLCanvasElement) { + this.canvas = canvas; + this._animate_loop = () => this.draw(); + this.recalculate_cache(); /* initialize cache */ + } + + initialize() { + this._canvas_context = this.canvas.getContext("2d"); + + Graph._loops.push(this._animate_loop); + if(Graph._loops.length == 1) { + const static_loop = () => { + Graph._loops.forEach(l => l()); + if(Graph._loops.length > 0) + requestAnimationFrame(static_loop); + else + console.log("STATIC terminate!"); + }; + static_loop(); + } + + this.canvas.onmousemove = this.on_mouse_move.bind(this); + this.canvas.onmouseleave = this.on_mouse_leave.bind(this); + } + + terminate() { + Graph._loops.remove(this._animate_loop); + } + + max_gap_size(value?: number) : number { return typeof(value) === "number" ? (this._max_gap = value) : this._max_gap; } + + private recalculate_cache(time_span?: boolean) { + this._entries = this._entries.sort((a, b) => a.timestamp - b.timestamp); + this._entry_max = { + download: 1, + upload: 1 + }; + if(time_span) { + this._time_span = { + origin: { + begin: 0, + end: 0, + time: 0 + }, + target: { + begin: this._entries.length > 0 ? this._entries[0].timestamp : 0, + end: this._entries.length > 0 ? this._entries.last().timestamp : 0, + time: 0 + } + }; + } + + for(const entry of this._entries) { + if(typeof(entry.upload) === "number") + this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload); + + if(typeof(entry.download) === "number") + this._entry_max.download = Math.max(this._entry_max.download, entry.download); + } + + this._entry_max.upload *= this._max_space; + this._entry_max.download *= this._max_space; + } + + insert_entry(entry: Entry) { + if(this._entries.length > 0 && entry.timestamp < this._entries.last().timestamp) + throw "invalid timestamp"; + + this._entries.push(entry); + + if(typeof(entry.upload) === "number") + this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload * this._max_space); + + if(typeof(entry.download) === "number") + this._entry_max.download = Math.max(this._entry_max.download, entry.download * this._max_space); + } + + insert_entries(entries: Entry[]) { + this._entries.push(...entries); + this.recalculate_cache(); + this.cleanup(); + } + + resize() { + this.canvas.style.height = "100%"; + this.canvas.style.width = "100%"; + const cstyle = getComputedStyle(this.canvas); + + this.canvas.width = parseInt(cstyle.width); + this.canvas.height = parseInt(cstyle.height); + } + + cleanup() { + const time = this.calculate_time_span(); + + let index = 0; + for(;index < this._entries.length; index++) { + if(this._entries[index].timestamp < time.begin) + continue; + + if(index == 0) + return; + break; + } + + /* keep the last entry as a reference point to the left */ + if(index > 1) { + this._entries.splice(0, index - 1); + this.recalculate_cache(); + } + } + + calculate_time_span() : { begin: number; end: number } { + const time = Date.now(); + if(time >= this._time_span.target.time) + return this._time_span.target; + + if(time <= this._time_span.origin.time) + return this._time_span.origin; + + const ob = this._time_span.origin.begin; + const oe = this._time_span.origin.end; + const ot = this._time_span.origin.time; + + const tb = this._time_span.target.begin; + const te = this._time_span.target.end; + const tt = this._time_span.target.time; + + const offset = (time - ot) / (tt - ot); + return { + begin: ob + (tb - ob) * offset, + end: oe + (te - oe) * offset, + }; + } + + draw() { + let ctx = this._canvas_context; + + const height = this.canvas.height; + const width = this.canvas.width; + + //console.log("Painting on %ox%o", height, width); + + ctx.shadowBlur = 0; + ctx.filter = ""; + ctx.lineCap = "square"; + + ctx.fillStyle = this.style.background_color; + ctx.fillRect(0, 0, width, height); + + /* first of all print the separators */ + { + const sw = this.style.separator_width; + const swh = this.style.separator_width / 2; + + ctx.lineWidth = sw; + ctx.strokeStyle = this.style.separator_color; + + ctx.beginPath(); + /* horizontal */ + { + const dw = width / this.style.separator_count; + let dx = dw / 2; + while(dx < width) { + ctx.moveTo(Math.floor(dx - swh) + .5, .5); + ctx.lineTo(Math.floor(dx - swh) + .5, Math.floor(height) + .5); + dx += dw; + } + } + + /* vertical */ + { + const dh = height / 3; //tree lines (top, center, bottom) + + let dy = dh / 2; + while(dy < height) { + ctx.moveTo(.5, Math.floor(dy - swh) + .5); + ctx.lineTo(Math.floor(width) + .5, Math.floor(dy - swh) + .5); + dy += dh; + } + } + ctx.stroke(); + ctx.closePath(); + } + + /* draw the lines */ + { + const t = this.calculate_time_span(); + const tb = t.begin; /* time begin */ + const dt = t.end - t.begin; /* delta time */ + const dtw = width / dt; /* delta time width */ + + const draw_graph = (type: "upload" | "download", direction: number, max: number) => { + const hy = Math.floor(height / 2); /* half y */ + const by = hy - direction * this.style[type].strike_width; /* the "base" line */ + + const marked_points: ({x: number, y: number})[] = []; + + ctx.beginPath(); + ctx.moveTo(0, by); + + let x, y, lx = 0, ly = by; /* last x, last y */ + + const floor = a => a; //Math.floor; + for(const entry of this._entries) { + x = floor((entry.timestamp - tb) * dtw); + if(typeof entry[type] === "number") + y = floor(hy - direction * Math.max(hy * (entry[type] / max), this.style[type].strike_width)); + else + y = hy - direction * this.style[type].strike_width; + + if(entry.timestamp < tb) { + lx = x; + ly = y; + + continue; + } + + if(x - lx > this._max_gap && this._max_gap > 0) { + ctx.lineTo(lx, by); + ctx.lineTo(x, by); + ctx.lineTo(x, y); + + lx = x; + ly = y; + continue; + } + + ctx.bezierCurveTo((x + lx) / 2, ly, (x + lx) / 2, y, x, y); + if(entry.highlight) + marked_points.push({x: x, y: y}); + + lx = x; + ly = y; + } + + ctx.strokeStyle = this.style[type].stroke; + ctx.lineWidth = this.style[type].strike_width; + ctx.lineJoin = "miter"; + ctx.stroke(); + + //Close the path and fill + ctx.lineTo(width, hy); + ctx.lineTo(0, hy); + + ctx.fillStyle = this.style[type].fill; + ctx.fill(); + + ctx.closePath(); + + { + ctx.beginPath(); + const radius = 3; + for(const point of marked_points) { + ctx.moveTo(point.x, point.y); + ctx.ellipse(point.x, point.y, radius, radius, 0, 0, 2 * Math.PI, false); + + } + ctx.stroke(); + ctx.fill(); + + ctx.closePath(); + } + }; + + const shared_max = Math.max(this._entry_max.upload, this._entry_max.download); + draw_graph("upload", 1, shared_max); + draw_graph("download", -1, shared_max); + } + } + + private on_mouse_move(event: MouseEvent) { + const offset = event.offsetX; + const max_offset = this.canvas.width; + + if(offset < 0) return; + if(offset > max_offset) return; + + const time_span = this.calculate_time_span(); + const time = time_span.begin + (time_span.end - time_span.begin) * (offset / max_offset); + let index = 0; + for(;index < this._entries.length; index++) { + if(this._entries[index].timestamp > time) + break; + } + + const entry_before = this._entries[index - 1]; /* In JS negative array access is allowed and returns undefined */ + const entry_next = this._entries[index]; /* In JS negative array access is allowed and returns undefined */ + let entry: Entry; + if(!entry_before || !entry_next) { + entry = entry_before || entry_next; + } else { + const dn = entry_next.timestamp - time; + const db = time - entry_before.timestamp; + if(dn > db) + entry = entry_before; + else + entry = entry_next; + } + + if(!entry) { + this.on_mouse_leave(event); + } else { + this._entries.forEach(e => e.highlight = false); + this._detailed_shown = true; + entry.highlight = true; + + if(this.callback_detailed_info) + this.callback_detailed_info(entry.upload, entry.download, entry.timestamp, event); + } + + } + + private on_mouse_leave(event: MouseEvent) { + if(!this._detailed_shown) return; + this._detailed_shown = false; + + this._entries.forEach(e => e.highlight = false); + if(this.callback_detailed_hide) + this.callback_detailed_hide(); + } +} \ No newline at end of file diff --git a/shared/js/ui/elements/slider.ts b/shared/js/ui/elements/Slider.ts similarity index 92% rename from shared/js/ui/elements/slider.ts rename to shared/js/ui/elements/Slider.ts index 0e956585..f704ed6d 100644 --- a/shared/js/ui/elements/slider.ts +++ b/shared/js/ui/elements/Slider.ts @@ -1,4 +1,6 @@ -interface SliderOptions { +import * as tooltip from "tc-shared/ui/elements/Tooltip"; + +export interface SliderOptions { min_value?: number; max_value?: number; initial_value?: number; @@ -8,11 +10,11 @@ interface SliderOptions { value_field?: JQuery | JQuery[]; } -interface Slider { +export interface Slider { value(value?: number) : number; } -function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { +export function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { options = Object.assign( { initial_value: 0, min_value: 0, @@ -30,7 +32,7 @@ function sliderfy(slider: JQuery, options?: SliderOptions) : Slider { throw "invalid step size"; - const tool = tooltip(slider); /* add the tooltip functionality */ + const tool = tooltip.initialize(slider); /* add the tooltip functionality */ const filler = slider.find(".filler"); const thumb = slider.find(".thumb"); const tooltip_text = slider.find(".tooltip a"); diff --git a/shared/js/ui/elements/tab.ts b/shared/js/ui/elements/Tab.ts similarity index 95% rename from shared/js/ui/elements/tab.ts rename to shared/js/ui/elements/Tab.ts index fb34577c..109476b5 100644 --- a/shared/js/ui/elements/tab.ts +++ b/shared/js/ui/elements/Tab.ts @@ -1,13 +1,12 @@ -/// +declare global { + interface JQuery { + asTabWidget(copy?: boolean) : JQuery; + tabify(copy?: boolean) : this; -interface JQuery { - asTabWidget(copy?: boolean) : JQuery; - tabify(copy?: boolean) : this; - - changeElementType(type: string) : JQuery; + changeElementType(type: string) : JQuery; + } } - if(typeof (customElements) !== "undefined") { try { class X_Tab extends HTMLElement {} @@ -26,7 +25,7 @@ if(typeof (customElements) !== "undefined") { console.warn(tr("Could not defied tab customElements!")); } -var TabFunctions = { +export const TabFunctions = { tabify(template: JQuery, copy: boolean = true) : JQuery { console.log("Tabify: copy=" + copy); console.log(template); diff --git a/shared/js/ui/elements/Tooltip.ts b/shared/js/ui/elements/Tooltip.ts new file mode 100644 index 00000000..b6657c6a --- /dev/null +++ b/shared/js/ui/elements/Tooltip.ts @@ -0,0 +1,79 @@ +let _global_tooltip: JQuery; +export type Handle = { + show(); + is_shown(); + hide(); + update(); +} +export function initialize(entry: JQuery, callbacks?: { + on_show?(tag: JQuery), + on_hide?(tag: JQuery) +}) : Handle { + if(!callbacks) callbacks = {}; + + let _show; + let _hide; + let _shown; + let _update; + + entry.find(".container-tooltip").each((index, _node) => { + const node = $(_node) as JQuery; + const node_content = node.find(".tooltip"); + + let _force_show = false, _flag_shown = false; + + const mouseenter = (event?) => { + const bounds = node[0].getBoundingClientRect(); + + if(!_global_tooltip) { + _global_tooltip = $("#global-tooltip"); + } + + _global_tooltip[0].style.left = (bounds.left + bounds.width / 2) + "px"; + _global_tooltip[0].style.top = bounds.top + "px"; + _global_tooltip[0].classList.add("shown"); + + _global_tooltip[0].innerHTML = node_content[0].innerHTML; + callbacks.on_show && callbacks.on_show(_global_tooltip); + _flag_shown = _flag_shown || !!event; /* if event is undefined then it has been triggered by hand */ + }; + + const mouseexit = () => { + if(_global_tooltip) { + if(!_force_show) { + callbacks.on_hide && callbacks.on_hide(_global_tooltip); + _global_tooltip[0].classList.remove("shown"); + } + _flag_shown = false; + } + }; + + _node.addEventListener("mouseenter", mouseenter); + + _node.addEventListener("mouseleave", mouseexit); + + _show = () => { + _force_show = true; + mouseenter(); + }; + + _hide = () => { + _force_show = false; + if(!_flag_shown) + mouseexit(); + }; + + _update = () => { + if(_flag_shown || _force_show) + mouseenter(); + }; + + _shown = () => _flag_shown || _force_show; + }); + return { + hide: _hide || (() => {}), + show: _show || (() => {}), + is_shown: _shown || (() => false), + update: _update || (() => {}) + }; +} \ No newline at end of file diff --git a/shared/js/ui/elements/net_graph.ts b/shared/js/ui/elements/net_graph.ts deleted file mode 100644 index 5e440872..00000000 --- a/shared/js/ui/elements/net_graph.ts +++ /dev/null @@ -1,435 +0,0 @@ -namespace net.graph { - export type Entry = { - timestamp: number; - - upload?: number; - download?: number; - - highlight?: boolean; - } - - export type Style = { - background_color: string; - - separator_color: string; - separator_count: number; - separator_width: number; - - upload: { - fill: string; - stroke: string; - strike_width: number; - }, - download: { - fill: string; - stroke: string; - strike_width: number; - } - } - - export type TimeSpan = { - origin: { - begin: number; - end: number; - time: number; - }, - target: { - begin: number; - end: number; - time: number; - } - } - - /* Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves - * - * Assuming A was the last point in the line plotted and B is the new point, - * we draw a curve with control points P and Q as below. - * - * A---P - * | - * | - * | - * Q---B - * - * Importantly, A and P are at the same y coordinate, as are B and Q. This is - * so adjacent curves appear to flow as one. - */ - export class Graph { - private static _loops: (() => any)[] = []; - - readonly canvas: HTMLCanvasElement; - public style: Style = { - background_color: "#28292b", - //background_color: "red", - - separator_color: "#283036", - //separator_color: 'blue', - separator_count: 10, - separator_width: 1, - - - upload: { - fill: "#2d3f4d", - stroke: "#336e9f", - strike_width: 2, - }, - - download: { - fill: "#532c26", - stroke: "#a9321c", - strike_width: 2, - } - }; - - private _canvas_context: CanvasRenderingContext2D; - private _entries: Entry[] = []; - private _entry_max = { - upload: 1, - download: 1, - }; - private _max_space = 1.12; - private _max_gap = 5; - private _listener_mouse_move; - private _listener_mouse_out; - private _animate_loop; - - _time_span: TimeSpan = { - origin: { - begin: 0, - end: 1, - time: 0 - }, - target: { - begin: 0, - end: 1, - time: 1 - } - }; - - private _detailed_shown = false; - callback_detailed_info: (upload: number, download: number, timestamp: number, event: MouseEvent) => any; - callback_detailed_hide: () => any; - - constructor(canvas: HTMLCanvasElement) { - this.canvas = canvas; - this._animate_loop = () => this.draw(); - this.recalculate_cache(); /* initialize cache */ - } - - initialize() { - this._canvas_context = this.canvas.getContext("2d"); - - Graph._loops.push(this._animate_loop); - if(Graph._loops.length == 1) { - const static_loop = () => { - Graph._loops.forEach(l => l()); - if(Graph._loops.length > 0) - requestAnimationFrame(static_loop); - else - console.log("STATIC terminate!"); - }; - static_loop(); - } - - this.canvas.onmousemove = this.on_mouse_move.bind(this); - this.canvas.onmouseleave = this.on_mouse_leave.bind(this); - } - - terminate() { - Graph._loops.remove(this._animate_loop); - } - - max_gap_size(value?: number) : number { return typeof(value) === "number" ? (this._max_gap = value) : this._max_gap; } - - private recalculate_cache(time_span?: boolean) { - this._entries = this._entries.sort((a, b) => a.timestamp - b.timestamp); - this._entry_max = { - download: 1, - upload: 1 - }; - if(time_span) { - this._time_span = { - origin: { - begin: 0, - end: 0, - time: 0 - }, - target: { - begin: this._entries.length > 0 ? this._entries[0].timestamp : 0, - end: this._entries.length > 0 ? this._entries.last().timestamp : 0, - time: 0 - } - }; - } - - for(const entry of this._entries) { - if(typeof(entry.upload) === "number") - this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload); - - if(typeof(entry.download) === "number") - this._entry_max.download = Math.max(this._entry_max.download, entry.download); - } - - this._entry_max.upload *= this._max_space; - this._entry_max.download *= this._max_space; - } - - insert_entry(entry: Entry) { - if(this._entries.length > 0 && entry.timestamp < this._entries.last().timestamp) - throw "invalid timestamp"; - - this._entries.push(entry); - - if(typeof(entry.upload) === "number") - this._entry_max.upload = Math.max(this._entry_max.upload, entry.upload * this._max_space); - - if(typeof(entry.download) === "number") - this._entry_max.download = Math.max(this._entry_max.download, entry.download * this._max_space); - } - - insert_entries(entries: Entry[]) { - this._entries.push(...entries); - this.recalculate_cache(); - this.cleanup(); - } - - resize() { - this.canvas.style.height = "100%"; - this.canvas.style.width = "100%"; - const cstyle = getComputedStyle(this.canvas); - - this.canvas.width = parseInt(cstyle.width); - this.canvas.height = parseInt(cstyle.height); - } - - cleanup() { - const time = this.calculate_time_span(); - - let index = 0; - for(;index < this._entries.length; index++) { - if(this._entries[index].timestamp < time.begin) - continue; - - if(index == 0) - return; - break; - } - - /* keep the last entry as a reference point to the left */ - if(index > 1) { - this._entries.splice(0, index - 1); - this.recalculate_cache(); - } - } - - calculate_time_span() : { begin: number; end: number } { - const time = Date.now(); - if(time >= this._time_span.target.time) - return this._time_span.target; - - if(time <= this._time_span.origin.time) - return this._time_span.origin; - - const ob = this._time_span.origin.begin; - const oe = this._time_span.origin.end; - const ot = this._time_span.origin.time; - - const tb = this._time_span.target.begin; - const te = this._time_span.target.end; - const tt = this._time_span.target.time; - - const offset = (time - ot) / (tt - ot); - return { - begin: ob + (tb - ob) * offset, - end: oe + (te - oe) * offset, - }; - } - - draw() { - let ctx = this._canvas_context; - - const height = this.canvas.height; - const width = this.canvas.width; - - //console.log("Painting on %ox%o", height, width); - - ctx.shadowBlur = 0; - ctx.filter = ""; - ctx.lineCap = "square"; - - ctx.fillStyle = this.style.background_color; - ctx.fillRect(0, 0, width, height); - - /* first of all print the separators */ - { - const sw = this.style.separator_width; - const swh = this.style.separator_width / 2; - - ctx.lineWidth = sw; - ctx.strokeStyle = this.style.separator_color; - - ctx.beginPath(); - /* horizontal */ - { - const dw = width / this.style.separator_count; - let dx = dw / 2; - while(dx < width) { - ctx.moveTo(Math.floor(dx - swh) + .5, .5); - ctx.lineTo(Math.floor(dx - swh) + .5, Math.floor(height) + .5); - dx += dw; - } - } - - /* vertical */ - { - const dh = height / 3; //tree lines (top, center, bottom) - - let dy = dh / 2; - while(dy < height) { - ctx.moveTo(.5, Math.floor(dy - swh) + .5); - ctx.lineTo(Math.floor(width) + .5, Math.floor(dy - swh) + .5); - dy += dh; - } - } - ctx.stroke(); - ctx.closePath(); - } - - /* draw the lines */ - { - const t = this.calculate_time_span(); - const tb = t.begin; /* time begin */ - const dt = t.end - t.begin; /* delta time */ - const dtw = width / dt; /* delta time width */ - - const draw_graph = (type: "upload" | "download", direction: number, max: number) => { - const hy = Math.floor(height / 2); /* half y */ - const by = hy - direction * this.style[type].strike_width; /* the "base" line */ - - const marked_points: ({x: number, y: number})[] = []; - - ctx.beginPath(); - ctx.moveTo(0, by); - - let x, y, lx = 0, ly = by; /* last x, last y */ - - const floor = a => a; //Math.floor; - for(const entry of this._entries) { - x = floor((entry.timestamp - tb) * dtw); - if(typeof entry[type] === "number") - y = floor(hy - direction * Math.max(hy * (entry[type] / max), this.style[type].strike_width)); - else - y = hy - direction * this.style[type].strike_width; - - if(entry.timestamp < tb) { - lx = x; - ly = y; - - continue; - } - - if(x - lx > this._max_gap && this._max_gap > 0) { - ctx.lineTo(lx, by); - ctx.lineTo(x, by); - ctx.lineTo(x, y); - - lx = x; - ly = y; - continue; - } - - ctx.bezierCurveTo((x + lx) / 2, ly, (x + lx) / 2, y, x, y); - if(entry.highlight) - marked_points.push({x: x, y: y}); - - lx = x; - ly = y; - } - - ctx.strokeStyle = this.style[type].stroke; - ctx.lineWidth = this.style[type].strike_width; - ctx.lineJoin = "miter"; - ctx.stroke(); - - //Close the path and fill - ctx.lineTo(width, hy); - ctx.lineTo(0, hy); - - ctx.fillStyle = this.style[type].fill; - ctx.fill(); - - ctx.closePath(); - - { - ctx.beginPath(); - const radius = 3; - for(const point of marked_points) { - ctx.moveTo(point.x, point.y); - ctx.ellipse(point.x, point.y, radius, radius, 0, 0, 2 * Math.PI, false); - - } - ctx.stroke(); - ctx.fill(); - - ctx.closePath(); - } - }; - - const shared_max = Math.max(this._entry_max.upload, this._entry_max.download); - draw_graph("upload", 1, shared_max); - draw_graph("download", -1, shared_max); - } - } - - private on_mouse_move(event: MouseEvent) { - const offset = event.offsetX; - const max_offset = this.canvas.width; - - if(offset < 0) return; - if(offset > max_offset) return; - - const time_span = this.calculate_time_span(); - const time = time_span.begin + (time_span.end - time_span.begin) * (offset / max_offset); - let index = 0; - for(;index < this._entries.length; index++) { - if(this._entries[index].timestamp > time) - break; - } - - const entry_before = this._entries[index - 1]; /* In JS negative array access is allowed and returns undefined */ - const entry_next = this._entries[index]; /* In JS negative array access is allowed and returns undefined */ - let entry: Entry; - if(!entry_before || !entry_next) { - entry = entry_before || entry_next; - } else { - const dn = entry_next.timestamp - time; - const db = time - entry_before.timestamp; - if(dn > db) - entry = entry_before; - else - entry = entry_next; - } - - if(!entry) { - this.on_mouse_leave(event); - } else { - this._entries.forEach(e => e.highlight = false); - this._detailed_shown = true; - entry.highlight = true; - - if(this.callback_detailed_info) - this.callback_detailed_info(entry.upload, entry.download, entry.timestamp, event); - } - - } - - private on_mouse_leave(event: MouseEvent) { - if(!this._detailed_shown) return; - this._detailed_shown = false; - - this._entries.forEach(e => e.highlight = false); - if(this.callback_detailed_hide) - this.callback_detailed_hide(); - } - } -} \ No newline at end of file diff --git a/shared/js/ui/elements/tooltip.ts b/shared/js/ui/elements/tooltip.ts deleted file mode 100644 index b64f96b5..00000000 --- a/shared/js/ui/elements/tooltip.ts +++ /dev/null @@ -1,85 +0,0 @@ -function tooltip(entry: JQuery) { - return tooltip.initialize(entry); -} - -namespace tooltip { - let _global_tooltip: JQuery; - export type Handle = { - show(); - is_shown(); - hide(); - update(); - } - export function initialize(entry: JQuery, callbacks?: { - on_show?(tag: JQuery), - on_hide?(tag: JQuery) - }) : Handle { - if(!callbacks) callbacks = {}; - - let _show; - let _hide; - let _shown; - let _update; - - entry.find(".container-tooltip").each((index, _node) => { - const node = $(_node) as JQuery; - const node_content = node.find(".tooltip"); - - let _force_show = false, _flag_shown = false; - - const mouseenter = (event?) => { - const bounds = node[0].getBoundingClientRect(); - - if(!_global_tooltip) { - _global_tooltip = $("#global-tooltip"); - } - - _global_tooltip[0].style.left = (bounds.left + bounds.width / 2) + "px"; - _global_tooltip[0].style.top = bounds.top + "px"; - _global_tooltip[0].classList.add("shown"); - - _global_tooltip[0].innerHTML = node_content[0].innerHTML; - callbacks.on_show && callbacks.on_show(_global_tooltip); - _flag_shown = _flag_shown || !!event; /* if event is undefined then it has been triggered by hand */ - }; - - const mouseexit = () => { - if(_global_tooltip) { - if(!_force_show) { - callbacks.on_hide && callbacks.on_hide(_global_tooltip); - _global_tooltip[0].classList.remove("shown"); - } - _flag_shown = false; - } - }; - - _node.addEventListener("mouseenter", mouseenter); - - _node.addEventListener("mouseleave", mouseexit); - - _show = () => { - _force_show = true; - mouseenter(); - }; - - _hide = () => { - _force_show = false; - if(!_flag_shown) - mouseexit(); - }; - - _update = () => { - if(_flag_shown || _force_show) - mouseenter(); - }; - - _shown = () => _flag_shown || _force_show; - }); - return { - hide: _hide || (() => {}), - show: _show || (() => {}), - is_shown: _shown || (() => false), - update: _update || (() => {}) - }; - } -} \ No newline at end of file diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index b2d6dad3..a873cf34 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -1,24 +1,40 @@ -/// -/// -/// -/* - client_output_hardware Value: '1' - client_output_muted Value: '0' - client_outputonly_muted Value: '0' +import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler"; +import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import {manager, Sound} from "tc-shared/sound/Sounds"; +import {default_recorder} from "tc-shared/voice/RecorderProfile"; +import {Settings, settings} from "tc-shared/settings"; +import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings"; +import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit"; +import {openBanList} from "tc-shared/ui/modal/ModalBanList"; +import { + add_current_server, + Bookmark, + bookmarks, + BookmarkType, + boorkmak_connect, + DirectoryBookmark +} from "tc-shared/bookmarks"; +import {IconManager} from "tc-shared/FileManager"; +import {spawnBookmarkModal} from "tc-shared/ui/modal/ModalBookmarks"; +import {spawnQueryCreate} from "tc-shared/ui/modal/ModalQuery"; +import {spawnQueryManage} from "tc-shared/ui/modal/ModalQueryManage"; +import {spawnPlaylistManage} from "tc-shared/ui/modal/ModalPlaylistList"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import * as slog from "tc-shared/ui/frames/server_log"; +import * as top_menu from "./MenuBar"; - client_input_hardware Value: '1' - client_input_muted Value: '0' +export let control_bar: ControlBar; /* global variable to access the control bar */ +export function set_control_bar(bar: ControlBar) { control_bar = bar; } - client_away Value: '0' - client_away_message Value: '' - */ - -let control_bar: ControlBar; /* global variable to access the control bar */ - -type MicrophoneState = "disabled" | "muted" | "enabled"; -type HeadphoneState = "muted" | "enabled"; -type AwayState = "away-global" | "away" | "online"; -class ControlBar { +export type MicrophoneState = "disabled" | "muted" | "enabled"; +export type HeadphoneState = "muted" | "enabled"; +export type AwayState = "away-global" | "away" | "online"; +export class ControlBar { private _button_away_active: AwayState; private _button_microphone: MicrophoneState; private _button_speakers: HeadphoneState; @@ -363,10 +379,10 @@ class ControlBar { private on_toggle_microphone() { if(this._button_microphone === "disabled" || this._button_microphone === "muted") { this.button_microphone = "enabled"; - sound.manager.play(Sound.MICROPHONE_ACTIVATED); + manager.play(Sound.MICROPHONE_ACTIVATED); } else { this.button_microphone = "muted"; - sound.manager.play(Sound.MICROPHONE_MUTED); + manager.play(Sound.MICROPHONE_MUTED); } if(this.connection_handler) { @@ -384,10 +400,10 @@ class ControlBar { private on_toggle_sound() { if(this._button_speakers === "muted") { this.button_speaker = "enabled"; - sound.manager.play(Sound.SOUND_ACTIVATED); + manager.play(Sound.SOUND_ACTIVATED); } else { this.button_speaker = "muted"; - sound.manager.play(Sound.SOUND_MUTED); + manager.play(Sound.SOUND_MUTED); } if(this.connection_handler) { @@ -421,20 +437,20 @@ class ControlBar { } private on_open_settings() { - Modals.spawnSettingsModal(); + spawnSettingsModal(); } private on_open_connect() { if(this.connection_handler) this.connection_handler.cancel_reconnect(true); - Modals.spawnConnectModal({}, { + spawnConnectModal({}, { url: "ts.TeaSpeak.de", enforce: false }); } private on_open_connect_new_tab() { - Modals.spawnConnectModal({ + spawnConnectModal({ default_connect_new_tab: true }, { url: "ts.TeaSpeak.de", @@ -470,7 +486,7 @@ class ControlBar { this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message? this.update_connection_state(); this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED); - this.connection_handler.log.log(log.server.Type.DISCONNECTED, {}); + this.connection_handler.log.log(slog.Type.DISCONNECTED, {}); } private on_token_use() { @@ -483,7 +499,7 @@ class ControlBar { createInfoModal(tr("Use token"), tr("Toke successfully used!")).open(); }).catch(error => { //TODO tr - createErrorModal(tr("Use token"), MessageHelper.formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open(); + createErrorModal(tr("Use token"), formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open(); }); }).open(); } @@ -497,7 +513,7 @@ class ControlBar { button.addClass("activated"); setTimeout(() => { if(this.connection_handler) - Modals.spawnPermissionEdit(this.connection_handler).open(); + spawnPermissionEdit(this.connection_handler).open(); else createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open(); button.removeClass("activated"); @@ -508,7 +524,7 @@ class ControlBar { if(!this.connection_handler.serverConnection) return; if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { - Modals.openBanList(this.connection_handler); + openBanList(this.connection_handler); } else { createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open(); this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); @@ -516,7 +532,7 @@ class ControlBar { } private on_bookmark_server_add() { - bookmarks.add_current_server(); + add_current_server(); } update_bookmark_status() { @@ -530,13 +546,13 @@ class ControlBar { let tag_bookmark = this.htmlTag.find(".btn_bookmark > .dropdown"); tag_bookmark.find(".bookmark, .directory").remove(); - const build_entry = (bookmark: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => { - if(bookmark.type == bookmarks.BookmarkType.ENTRY) { - const mark = bookmark; + const build_entry = (bookmark: DirectoryBookmark | Bookmark) => { + if(bookmark.type == BookmarkType.ENTRY) { + const mark = bookmark; const bookmark_connect = (new_tab: boolean) => { this.htmlTag.find(".btn_bookmark").find(".dropdown").removeClass("displayed"); //FIXME Not working - bookmarks.boorkmak_connect(mark, new_tab); + boorkmak_connect(mark, new_tab); }; return $.spawn("div") @@ -580,7 +596,7 @@ class ControlBar { }) ) } else { - const mark = bookmark; + const mark = bookmark; const container = $.spawn("div").addClass("sub-menu dropdown"); const result = $.spawn("div") @@ -609,19 +625,19 @@ class ControlBar { } }; - for(const bookmark of bookmarks.bookmarks().content) { + for(const bookmark of bookmarks().content) { const entry = build_entry(bookmark); tag_bookmark.append(entry); } } private on_bookmark_manage() { - Modals.spawnBookmarkModal(); + spawnBookmarkModal(); } private on_open_query_create() { if(this.connection_handler.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) { - Modals.spawnQueryCreate(this.connection_handler); + spawnQueryCreate(this.connection_handler); } else { createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open(); this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); @@ -630,7 +646,7 @@ class ControlBar { private on_open_query_manage() { if(this.connection_handler && this.connection_handler.connected) { - Modals.spawnQueryManage(this.connection_handler); + spawnQueryManage(this.connection_handler); } else { createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open(); } @@ -638,7 +654,7 @@ class ControlBar { private on_open_playlist_manage() { if(this.connection_handler && this.connection_handler.connected) { - Modals.spawnPlaylistManage(this.connection_handler); + spawnPlaylistManage(this.connection_handler); } else { createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); } diff --git a/shared/js/ui/frames/MenuBar.ts b/shared/js/ui/frames/MenuBar.ts index 04885afa..0ef579ad 100644 --- a/shared/js/ui/frames/MenuBar.ts +++ b/shared/js/ui/frames/MenuBar.ts @@ -1,541 +1,574 @@ -namespace top_menu { - export interface HRItem { } +import {Icon, IconManager} from "tc-shared/FileManager"; +import {spawnBookmarkModal} from "tc-shared/ui/modal/ModalBookmarks"; +import { + add_current_server, + Bookmark, + bookmarks, + BookmarkType, + boorkmak_connect, + DirectoryBookmark +} from "tc-shared/bookmarks"; +import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler"; +import {Sound} from "tc-shared/sound/Sounds"; +import {spawnConnectModal} from "tc-shared/ui/modal/ModalConnect"; +import {spawnPermissionEdit} from "tc-shared/ui/modal/permission/ModalPermissionEdit"; +import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {openBanList} from "tc-shared/ui/modal/ModalBanList"; +import {spawnQueryManage} from "tc-shared/ui/modal/ModalQueryManage"; +import {spawnQueryCreate} from "tc-shared/ui/modal/ModalQuery"; +import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings"; +import {spawnAbout} from "tc-shared/ui/modal/ModalAbout"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import * as loader from "tc-loader"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import * as slog from "tc-shared/ui/frames/server_log"; - export interface MenuItem { - append_item(label: string): MenuItem; - append_hr(): HRItem; - delete_item(item: MenuItem | HRItem); - items() : (MenuItem | HRItem)[]; +export interface HRItem { } - icon(klass?: string | Promise | Icon) : string; - label(value?: string) : string; - visible(value?: boolean) : boolean; - disabled(value?: boolean) : boolean; - click(callback: () => any) : this; - } +export interface MenuItem { + append_item(label: string): MenuItem; + append_hr(): HRItem; + delete_item(item: MenuItem | HRItem); + items() : (MenuItem | HRItem)[]; - export interface MenuBarDriver { - initialize(); + icon(klass?: string | Promise | Icon) : string; + label(value?: string) : string; + visible(value?: boolean) : boolean; + disabled(value?: boolean) : boolean; + click(callback: () => any) : this; +} - append_item(label: string) : MenuItem; - delete_item(item: MenuItem); - items() : MenuItem[]; +export interface MenuBarDriver { + initialize(); - flush_changes(); - } + append_item(label: string) : MenuItem; + delete_item(item: MenuItem); + items() : MenuItem[]; - let _driver: MenuBarDriver; - export function driver() : MenuBarDriver { - return _driver; - } + flush_changes(); +} - export function set_driver(driver: MenuBarDriver) { - _driver = driver; - } +let _driver: MenuBarDriver; +export function driver() : MenuBarDriver { + return _driver; +} - export interface NativeActions { - open_dev_tools(); - reload_page(); +export function set_driver(driver: MenuBarDriver) { + _driver = driver; +} - check_native_update(); - open_change_log(); +export interface NativeActions { + open_dev_tools(); + reload_page(); - quit(); + check_native_update(); + open_change_log(); - show_dev_tools(): boolean; - } - export let native_actions: NativeActions; + quit(); - namespace html { - class HTMLHrItem implements top_menu.HRItem { - readonly html_tag: JQuery; + show_dev_tools(): boolean; +} +export let native_actions: NativeActions; - constructor() { - this.html_tag = $.spawn("hr"); - } +namespace html { + class HTMLHrItem implements HRItem { + readonly html_tag: JQuery; + + constructor() { + this.html_tag = $.spawn("hr"); } + } - class HTMLMenuItem implements top_menu.MenuItem { - readonly html_tag: JQuery; - readonly _label_tag: JQuery; - readonly _label_icon_tag: JQuery; - readonly _label_text_tag: JQuery; - readonly _submenu_tag: JQuery; + class HTMLMenuItem implements MenuItem { + readonly html_tag: JQuery; + readonly _label_tag: JQuery; + readonly _label_icon_tag: JQuery; + readonly _label_text_tag: JQuery; + readonly _submenu_tag: JQuery; - private _items: (MenuItem | HRItem)[] = []; - private _label: string; - private _callback_click: () => any; + private _items: (MenuItem | HRItem)[] = []; + private _label: string; + private _callback_click: () => any; - constructor(label: string, mode: "side" | "down") { - this._label = label; + constructor(label: string, mode: "side" | "down") { + this._label = label; - this.html_tag = $.spawn("div").addClass("container-menu-item type-" + mode); + this.html_tag = $.spawn("div").addClass("container-menu-item type-" + mode); - this._label_tag = $.spawn("div").addClass("menu-item"); - this._label_icon_tag = $.spawn("div").addClass("container-icon").appendTo(this._label_tag); - $.spawn("div").addClass("container-label").append( - this._label_text_tag = $.spawn("a").text(label) - ).appendTo(this._label_tag); - this._label_tag.on('click', event => { - if(event.isDefaultPrevented()) - return; + this._label_tag = $.spawn("div").addClass("menu-item"); + this._label_icon_tag = $.spawn("div").addClass("container-icon").appendTo(this._label_tag); + $.spawn("div").addClass("container-label").append( + this._label_text_tag = $.spawn("a").text(label) + ).appendTo(this._label_tag); + this._label_tag.on('click', event => { + if(event.isDefaultPrevented()) + return; - const disabled = this.html_tag.hasClass("disabled"); - if(this._callback_click && !disabled) { - this._callback_click(); - } + const disabled = this.html_tag.hasClass("disabled"); + if(this._callback_click && !disabled) { + this._callback_click(); + } - event.preventDefault(); - if(disabled) - event.stopPropagation(); - else - HTMLMenuBarDriver.instance().close(); - }); - - this._submenu_tag = $.spawn("div").addClass("sub-menu"); - - this.html_tag.append(this._label_tag); - this.html_tag.append(this._submenu_tag); - } - - append_item(label: string): top_menu.MenuItem { - const item = new HTMLMenuItem(label, "side"); - this._items.push(item); - this._submenu_tag.append(item.html_tag); - this.html_tag.addClass('sub-entries'); - return item; - } - - append_hr(): HRItem { - const item = new HTMLHrItem(); - this._items.push(item); - this._submenu_tag.append(item.html_tag); - return item; - } - - delete_item(item: top_menu.MenuItem | top_menu.HRItem) { - this._items.remove(item); - (item as any).html_tag.detach(); - this.html_tag.toggleClass('sub-entries', this._items.length > 0); - } - - disabled(value?: boolean): boolean { - if(typeof(value) === "undefined") - return this.html_tag.hasClass("disabled"); - - this.html_tag.toggleClass("disabled", value); - return value; - } - - items(): (top_menu.MenuItem | top_menu.HRItem)[] { - return this._items; - } - - label(value?: string): string { - if(typeof(value) === "undefined" || this._label === value) - return this._label; - - return this._label; - } - - visible(value?: boolean): boolean { - if(typeof(value) === "undefined") - return this.html_tag.is(':visible'); //FIXME! - - this.html_tag.toggle(!!value); - return value; - } - - click(callback: () => any): this { - this._callback_click = callback; - return this; - } - - icon(klass?: string | Promise | Icon): string { - this._label_icon_tag.children().remove(); - if(typeof(klass) === "string") - $.spawn("div").addClass("icon_em " + klass).appendTo(this._label_icon_tag); + event.preventDefault(); + if(disabled) + event.stopPropagation(); else - IconManager.generate_tag(klass).appendTo(this._label_icon_tag); - return ""; - } + HTMLMenuBarDriver.instance().close(); + }); + this._submenu_tag = $.spawn("div").addClass("sub-menu"); + + this.html_tag.append(this._label_tag); + this.html_tag.append(this._submenu_tag); } - export class HTMLMenuBarDriver implements MenuBarDriver { - private static _instance: HTMLMenuBarDriver; - public static instance() : HTMLMenuBarDriver { - if(!this._instance) - this._instance = new HTMLMenuBarDriver(); - return this._instance; - } - - readonly html_tag: JQuery; - - private _items: MenuItem[] = []; - constructor() { - this.html_tag = $.spawn("div").addClass("top-menu-bar"); - } - - append_item(label: string): top_menu.MenuItem { - const item = new HTMLMenuItem(label, "down"); - this._items.push(item); - - this.html_tag.append(item.html_tag); - item._label_tag.on('click', enable_event => { - enable_event.preventDefault(); - - this.close(); - item.html_tag.addClass("active"); - - setTimeout(() => { - $(document).one('click focusout', event => { - if(event.isDefaultPrevented()) return; - event.preventDefault(); - - item.html_tag.removeClass("active"); - }); - }, 0); - }); - return item; - } - - close() { - this.html_tag.find(".active").removeClass("active"); - } - - delete_item(item: MenuItem) { - return undefined; - } - - items(): top_menu.MenuItem[] { - return this._items; - } - - flush_changes() { /* unused, all changed were made instantly */ } - - initialize() { - $("#top-menu-bar").replaceWith(this.html_tag); - } + append_item(label: string): MenuItem { + const item = new HTMLMenuItem(label, "side"); + this._items.push(item); + this._submenu_tag.append(item.html_tag); + this.html_tag.addClass('sub-entries'); + return item; } + + append_hr(): HRItem { + const item = new HTMLHrItem(); + this._items.push(item); + this._submenu_tag.append(item.html_tag); + return item; + } + + delete_item(item: MenuItem | HRItem) { + this._items.remove(item); + (item as any).html_tag.detach(); + this.html_tag.toggleClass('sub-entries', this._items.length > 0); + } + + disabled(value?: boolean): boolean { + if(typeof(value) === "undefined") + return this.html_tag.hasClass("disabled"); + + this.html_tag.toggleClass("disabled", value); + return value; + } + + items(): (MenuItem | HRItem)[] { + return this._items; + } + + label(value?: string): string { + if(typeof(value) === "undefined" || this._label === value) + return this._label; + + return this._label; + } + + visible(value?: boolean): boolean { + if(typeof(value) === "undefined") + return this.html_tag.is(':visible'); //FIXME! + + this.html_tag.toggle(!!value); + return value; + } + + click(callback: () => any): this { + this._callback_click = callback; + return this; + } + + icon(klass?: string | Promise | Icon): string { + this._label_icon_tag.children().remove(); + if(typeof(klass) === "string") + $.spawn("div").addClass("icon_em " + klass).appendTo(this._label_icon_tag); + else + IconManager.generate_tag(klass).appendTo(this._label_icon_tag); + return ""; + } + } - let _items_bookmark: { - root: MenuItem, - manage: MenuItem, - add_current: MenuItem - }; - - export function rebuild_bookmarks() { - if(!_items_bookmark) { - _items_bookmark = { - root: driver().append_item(tr("Favorites")), - - add_current: undefined, - manage: undefined - }; - _items_bookmark.manage = _items_bookmark.root.append_item(tr("Manage bookmarks")); - _items_bookmark.manage.icon("client-bookmark_manager"); - _items_bookmark.manage.click(() => Modals.spawnBookmarkModal()); - - _items_bookmark.add_current = _items_bookmark.root.append_item(tr("Add current server to bookmarks")); - _items_bookmark.add_current.icon('client-bookmark_add'); - _items_bookmark.add_current.click(() => bookmarks.add_current_server()); - _state_updater["bookmarks.ac"] = { item: _items_bookmark.add_current, conditions: [condition_connected]}; + export class HTMLMenuBarDriver implements MenuBarDriver { + private static _instance: HTMLMenuBarDriver; + public static instance() : HTMLMenuBarDriver { + if(!this._instance) + this._instance = new HTMLMenuBarDriver(); + return this._instance; } - _items_bookmark.root.items().filter(e => e !== _items_bookmark.add_current && e !== _items_bookmark.manage).forEach(e => { - _items_bookmark.root.delete_item(e); - }); - _items_bookmark.root.append_hr(); + readonly html_tag: JQuery; - const build_bookmark = (root: MenuItem, entry: bookmarks.DirectoryBookmark | bookmarks.Bookmark) => { - if(entry.type == bookmarks.BookmarkType.DIRECTORY) { - const directory = entry as bookmarks.DirectoryBookmark; - const item = root.append_item(directory.display_name); - item.icon('client-folder'); - for(const entry of directory.content) - build_bookmark(item, entry); - if(directory.content.length == 0) - item.disabled(true); - } else { - const bookmark = entry as bookmarks.Bookmark; - const item = root.append_item(bookmark.display_name); - item.icon(IconManager.load_cached_icon(bookmark.last_icon_id || 0)); - item.click(() => bookmarks.boorkmak_connect(bookmark)); - } + private _items: MenuItem[] = []; + constructor() { + this.html_tag = $.spawn("div").addClass("top-menu-bar"); + } + + append_item(label: string): MenuItem { + const item = new HTMLMenuItem(label, "down"); + this._items.push(item); + + this.html_tag.append(item.html_tag); + item._label_tag.on('click', enable_event => { + enable_event.preventDefault(); + + this.close(); + item.html_tag.addClass("active"); + + setTimeout(() => { + $(document).one('click focusout', event => { + if(event.isDefaultPrevented()) return; + event.preventDefault(); + + item.html_tag.removeClass("active"); + }); + }, 0); + }); + return item; + } + + close() { + this.html_tag.find(".active").removeClass("active"); + } + + delete_item(item: MenuItem) { + return undefined; + } + + items(): MenuItem[] { + return this._items; + } + + flush_changes() { /* unused, all changed were made instantly */ } + + initialize() { + $("#top-menu-bar").replaceWith(this.html_tag); + } + } +} + +let _items_bookmark: { + root: MenuItem, + manage: MenuItem, + add_current: MenuItem +}; + +export function rebuild_bookmarks() { + if(!_items_bookmark) { + _items_bookmark = { + root: driver().append_item(tr("Favorites")), + + add_current: undefined, + manage: undefined }; + _items_bookmark.manage = _items_bookmark.root.append_item(tr("Manage bookmarks")); + _items_bookmark.manage.icon("client-bookmark_manager"); + _items_bookmark.manage.click(() => spawnBookmarkModal()); - for(const entry of bookmarks.bookmarks().content) - build_bookmark(_items_bookmark.root, entry); - driver().flush_changes(); + _items_bookmark.add_current = _items_bookmark.root.append_item(tr("Add current server to bookmarks")); + _items_bookmark.add_current.icon('client-bookmark_add'); + _items_bookmark.add_current.click(() => add_current_server()); + _state_updater["bookmarks.ac"] = { item: _items_bookmark.add_current, conditions: [condition_connected]}; } - /* will be called on connection handler change or on client connect state or mic state change etc... */ - let _state_updater: {[key: string]:{ item: MenuItem; conditions: (() => boolean)[], update_handler?: (item: MenuItem) => any }} = {}; - export function update_state() { - for(const _key of Object.keys(_state_updater)) { - const item = _state_updater[_key]; - if(item.update_handler) { - if(item.update_handler(item.item)) - continue; - } - let enabled = true; - for(const condition of item.conditions) - if(!condition()) { - enabled = false; - break; - } - item.item.disabled(!enabled); + _items_bookmark.root.items().filter(e => e !== _items_bookmark.add_current && e !== _items_bookmark.manage).forEach(e => { + _items_bookmark.root.delete_item(e); + }); + _items_bookmark.root.append_hr(); + + const build_bookmark = (root: MenuItem, entry: DirectoryBookmark | Bookmark) => { + if(entry.type == BookmarkType.DIRECTORY) { + const directory = entry as DirectoryBookmark; + const item = root.append_item(directory.display_name); + item.icon('client-folder'); + for(const entry of directory.content) + build_bookmark(item, entry); + if(directory.content.length == 0) + item.disabled(true); + } else { + const bookmark = entry as Bookmark; + const item = root.append_item(bookmark.display_name); + item.icon(IconManager.load_cached_icon(bookmark.last_icon_id || 0)); + item.click(() => boorkmak_connect(bookmark)); } - driver().flush_changes(); - } - - const condition_connected = () => { - const scon = server_connections ? server_connections.active_connection_handler() : undefined; - return scon && scon.connected; }; - declare namespace native { - export function initialize(); + for(const entry of bookmarks().content) + build_bookmark(_items_bookmark.root, entry); + driver().flush_changes(); +} + +/* will be called on connection handler change or on client connect state or mic state change etc... */ +let _state_updater: {[key: string]:{ item: MenuItem; conditions: (() => boolean)[], update_handler?: (item: MenuItem) => any }} = {}; +export function update_state() { + for(const _key of Object.keys(_state_updater)) { + const item = _state_updater[_key]; + if(item.update_handler) { + if(item.update_handler(item.item)) + continue; + } + let enabled = true; + for(const condition of item.conditions) + if(!condition()) { + enabled = false; + break; + } + item.item.disabled(!enabled); + } + driver().flush_changes(); +} + +const condition_connected = () => { + const scon = server_connections ? server_connections.active_connection_handler() : undefined; + return scon && scon.connected; +}; + +declare namespace native { + export function initialize(); +} + +export function initialize() { + const driver_ = driver(); + driver_.initialize(); + + /* build connection */ + let item: MenuItem; + { + const menu = driver_.append_item(tr("Connection")); + item = menu.append_item(tr("Connect to a server")); + item.icon('client-connect'); + item.click(() => spawnConnectModal({})); + + const do_disconnect = (handlers: ConnectionHandler[]) => { + for(const handler of handlers) { + handler.cancel_reconnect(true); + handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message? + server_connections.active_connection_handler().serverConnection.disconnect(); + handler.sound.play(Sound.CONNECTION_DISCONNECTED); + handler.log.log(slog.Type.DISCONNECTED, {}); + } + control_bar.update_connection_state(); + update_state(); + }; + item = menu.append_item(tr("Disconnect from current server")); + item.icon('client-disconnect'); + item.disabled(true); + item.click(() => { + const handler = server_connections.active_connection_handler(); + do_disconnect([handler]); + }); + _state_updater["connection.dc"] = { item: item, conditions: [() => condition_connected()]}; + + item = menu.append_item(tr("Disconnect from all servers")); + item.icon('client-disconnect'); + item.click(() => { + do_disconnect(server_connections.server_connection_handlers()); + }); + _state_updater["connection.dca"] = { item: item, conditions: [], update_handler: (item) => { + item.visible(server_connections && server_connections.server_connection_handlers().length > 1); + return true; + }}; + + if(loader.version().type !== "web") { + menu.append_hr(); + + item = menu.append_item(tr("Quit")); + item.icon('client-close_button'); + item.click(() => native_actions.quit()); + } + } + { + rebuild_bookmarks(); } - export function initialize() { - const driver = top_menu.driver(); - driver.initialize(); - - /* build connection */ - let item: MenuItem; - { - const menu = driver.append_item(tr("Connection")); - item = menu.append_item(tr("Connect to a server")); - item.icon('client-connect'); - item.click(() => Modals.spawnConnectModal({})); - - const do_disconnect = (handlers: ConnectionHandler[]) => { - for(const handler of handlers) { - handler.cancel_reconnect(true); - handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message? - server_connections.active_connection_handler().serverConnection.disconnect(); - handler.sound.play(Sound.CONNECTION_DISCONNECTED); - handler.log.log(log.server.Type.DISCONNECTED, {}); - } - control_bar.update_connection_state(); - update_state(); - }; - item = menu.append_item(tr("Disconnect from current server")); - item.icon('client-disconnect'); - item.disabled(true); - item.click(() => { - const handler = server_connections.active_connection_handler(); - do_disconnect([handler]); - }); - _state_updater["connection.dc"] = { item: item, conditions: [() => condition_connected()]}; - - item = menu.append_item(tr("Disconnect from all servers")); - item.icon('client-disconnect'); - item.click(() => { - do_disconnect(server_connections.server_connection_handlers()); - }); - _state_updater["connection.dca"] = { item: item, conditions: [], update_handler: (item) => { - item.visible(server_connections && server_connections.server_connection_handlers().length > 1); - return true; - }}; - - if(!app.is_web()) { - menu.append_hr(); - - item = menu.append_item(tr("Quit")); - item.icon('client-close_button'); - item.click(() => native_actions.quit()); - } - } - { - rebuild_bookmarks(); - } - - if(false) { - const menu = driver.append_item(tr("Self")); - /* Microphone | Sound | Away */ - } - - { - const menu = driver.append_item(tr("Permissions")); - - item = menu.append_item(tr("Server Groups")); - item.icon("client-permission_server_groups"); - item.click(() => { - Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open(); - }); - _state_updater["permission.sg"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Client Permissions")); - item.icon("client-permission_client"); - item.click(() => { - Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open(); - }); - _state_updater["permission.clp"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Channel Client Permissions")); - item.icon("client-permission_client"); - item.click(() => { - Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open(); - }); - _state_updater["permission.chclp"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Channel Groups")); - item.icon("client-permission_channel"); - item.click(() => { - Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open(); - }); - _state_updater["permission.cg"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Channel Permissions")); - item.icon("client-permission_channel"); - item.click(() => { - Modals.spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open(); - }); - _state_updater["permission.cp"] = { item: item, conditions: [condition_connected]}; - - menu.append_hr(); - item = menu.append_item(tr("List Privilege Keys")); - item.icon("client-token"); - item.click(() => { - createErrorModal(tr("Not implemented"), tr("Privilege key list is not implemented yet!")).open(); - }); - _state_updater["permission.pk"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Use Privilege Key")); - item.icon("client-token_use"); - item.click(() => { - //TODO: Fixeme use one method for the control bar and here! - createInputModal(tr("Use token"), tr("Please enter your token/privilege key"), message => message.length > 0, result => { - if(!result) return; - const scon = server_connections.active_connection_handler(); - - if(scon.serverConnection.connected) - scon.serverConnection.send_command("tokenuse", { - token: result - }).then(() => { - createInfoModal(tr("Use token"), tr("Toke successfully used!")).open(); - }).catch(error => { - //TODO tr - createErrorModal(tr("Use token"), MessageHelper.formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open(); - }); - }).open(); - }); - _state_updater["permission.upk"] = { item: item, conditions: [condition_connected]}; - } - - { - const menu = driver.append_item(tr("Tools")); - - /* - item = menu.append_item(tr("Manage Playlists")); - item.icon('client-music'); - item.click(() => { - const scon = server_connections.active_connection_handler(); - if(scon && scon.connected) { - Modals.spawnPlaylistManage(scon); - } else { - createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); - } - }); - _state_updater["tools.pl"] = { item: item, conditions: [condition_connected]}; - */ - - item = menu.append_item(tr("Ban List")); - item.icon('client-ban_list'); - item.click(() => { - const scon = server_connections.active_connection_handler(); - if(scon && scon.connected) { - if(scon.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { - Modals.openBanList(scon); - } else { - createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open(); - scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } - } else { - createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); - } - }); - _state_updater["tools.bl"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Query List")); - item.icon('client-server_query'); - item.click(() => { - const scon = server_connections.active_connection_handler(); - if(scon && scon.connected) { - if(scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST_OWN).granted(1)) { - Modals.spawnQueryManage(scon); - } else { - createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the server query list")).open(); - scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } - } else { - createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); - } - }); - _state_updater["tools.ql"] = { item: item, conditions: [condition_connected]}; - - item = menu.append_item(tr("Query Create")); - item.icon('client-server_query'); - item.click(() => { - const scon = server_connections.active_connection_handler(); - if(scon && scon.connected) { - if(scon.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1)) { - Modals.spawnQueryCreate(scon); - } else { - createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open(); - scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } - } else { - createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); - } - }); - _state_updater["tools.qc"] = { item: item, conditions: [condition_connected]}; - menu.append_hr(); - - item = menu.append_item(tr("Settings")); - item.icon("client-settings"); - item.click(() => Modals.spawnSettingsModal()); - } - - { - const menu = driver.append_item(tr("Help")); - - if(!app.is_web()) { - item = menu.append_item(tr("Check for updates")); - item.click(() => native_actions.check_native_update()); - - item = menu.append_item(tr("Open changelog")); - item.click(() => native_actions.open_change_log()); - } - - item = menu.append_item(tr("Visit TeaSpeak.de")); - item.click(() => window.open('https://teaspeak.de/', '_blank')); - - item = menu.append_item(tr("Visit TeaSpeak forum")); - item.click(() => window.open('https://forum.teaspeak.de/', '_blank')); - - if(!app.is_web() && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) { - menu.append_hr(); - item = menu.append_item(tr("Open developer tools")); - item.click(() => native_actions.open_dev_tools()); - - item = menu.append_item(tr("Reload UI")); - item.click(() => native_actions.reload_page()); - } - - menu.append_hr(); - item = menu.append_item(app.is_web() ? tr("About TeaWeb") : tr("About TeaClient")); - item.click(() => Modals.spawnAbout()) - } - - update_state(); + if(false) { + const menu = driver_.append_item(tr("Self")); + /* Microphone | Sound | Away */ } - /* default is HTML, the client will override this */ - set_driver(html.HTMLMenuBarDriver.instance()); -} \ No newline at end of file + { + const menu = driver_.append_item(tr("Permissions")); + + item = menu.append_item(tr("Server Groups")); + item.icon("client-permission_server_groups"); + item.click(() => { + spawnPermissionEdit(server_connections.active_connection_handler(), "sg").open(); + }); + _state_updater["permission.sg"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Client Permissions")); + item.icon("client-permission_client"); + item.click(() => { + spawnPermissionEdit(server_connections.active_connection_handler(), "clp").open(); + }); + _state_updater["permission.clp"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Channel Client Permissions")); + item.icon("client-permission_client"); + item.click(() => { + spawnPermissionEdit(server_connections.active_connection_handler(), "clchp").open(); + }); + _state_updater["permission.chclp"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Channel Groups")); + item.icon("client-permission_channel"); + item.click(() => { + spawnPermissionEdit(server_connections.active_connection_handler(), "cg").open(); + }); + _state_updater["permission.cg"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Channel Permissions")); + item.icon("client-permission_channel"); + item.click(() => { + spawnPermissionEdit(server_connections.active_connection_handler(), "chp").open(); + }); + _state_updater["permission.cp"] = { item: item, conditions: [condition_connected]}; + + menu.append_hr(); + item = menu.append_item(tr("List Privilege Keys")); + item.icon("client-token"); + item.click(() => { + createErrorModal(tr("Not implemented"), tr("Privilege key list is not implemented yet!")).open(); + }); + _state_updater["permission.pk"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Use Privilege Key")); + item.icon("client-token_use"); + item.click(() => { + //TODO: Fixeme use one method for the control bar and here! + createInputModal(tr("Use token"), tr("Please enter your token/privilege key"), message => message.length > 0, result => { + if(!result) return; + const scon = server_connections.active_connection_handler(); + + if(scon.serverConnection.connected) + scon.serverConnection.send_command("tokenuse", { + token: result + }).then(() => { + createInfoModal(tr("Use token"), tr("Toke successfully used!")).open(); + }).catch(error => { + //TODO tr + createErrorModal(tr("Use token"), formatMessage(tr("Failed to use token: {}"), error instanceof CommandResult ? error.message : error)).open(); + }); + }).open(); + }); + _state_updater["permission.upk"] = { item: item, conditions: [condition_connected]}; + } + + { + const menu = driver_.append_item(tr("Tools")); + + /* + item = menu.append_item(tr("Manage Playlists")); + item.icon('client-music'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if(scon && scon.connected) { + Modals.spawnPlaylistManage(scon); + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); + } + }); + _state_updater["tools.pl"] = { item: item, conditions: [condition_connected]}; + */ + + item = menu.append_item(tr("Ban List")); + item.icon('client-ban_list'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if(scon && scon.connected) { + if(scon.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { + openBanList(scon); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); + } + }); + _state_updater["tools.bl"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Query List")); + item.icon('client-server_query'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if(scon && scon.connected) { + if(scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_LIST_OWN).granted(1)) { + spawnQueryManage(scon); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the server query list")).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); + } + }); + _state_updater["tools.ql"] = { item: item, conditions: [condition_connected]}; + + item = menu.append_item(tr("Query Create")); + item.icon('client-server_query'); + item.click(() => { + const scon = server_connections.active_connection_handler(); + if(scon && scon.connected) { + if(scon.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1) || scon.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1)) { + spawnQueryCreate(scon); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open(); + scon.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected to use this function!")).open(); + } + }); + _state_updater["tools.qc"] = { item: item, conditions: [condition_connected]}; + menu.append_hr(); + + item = menu.append_item(tr("Settings")); + item.icon("client-settings"); + item.click(() => spawnSettingsModal()); + } + + { + const menu = driver_.append_item(tr("Help")); + + if(loader.version().type !== "web") { + item = menu.append_item(tr("Check for updates")); + item.click(() => native_actions.check_native_update()); + + item = menu.append_item(tr("Open changelog")); + item.click(() => native_actions.open_change_log()); + } + + item = menu.append_item(tr("Visit TeaSpeak.de")); + item.click(() => window.open('https://teaspeak.de/', '_blank')); + + item = menu.append_item(tr("Visit TeaSpeak forum")); + item.click(() => window.open('https://forum.teaspeak.de/', '_blank')); + + if(loader.version().type !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) { + menu.append_hr(); + item = menu.append_item(tr("Open developer tools")); + item.click(() => native_actions.open_dev_tools()); + + item = menu.append_item(tr("Reload UI")); + item.click(() => native_actions.reload_page()); + } + + menu.append_hr(); + item = menu.append_item(loader.version().type === "web" ? tr("About TeaWeb") : tr("About TeaClient")); + item.click(() => spawnAbout()) + } + + update_state(); +} + +/* default is HTML, the client will override this */ +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + function: async () => { + if(!driver()) + set_driver(html.HTMLMenuBarDriver.instance()); + }, + priority: 100, + name: "Menu bar init" +}); \ No newline at end of file diff --git a/shared/js/ui/frames/chat.ts b/shared/js/ui/frames/chat.ts index 9de0d55b..a4e6ba7c 100644 --- a/shared/js/ui/frames/chat.ts +++ b/shared/js/ui/frames/chat.ts @@ -1,4 +1,10 @@ -enum ChatType { +import {LogCategory} from "tc-shared/log"; +import {settings, Settings} from "tc-shared/settings"; +import * as log from "tc-shared/log"; +import {bbcode} from "tc-shared/MessageFormatter"; +import * as loader from "tc-loader"; + +export enum ChatType { GENERAL, SERVER, CHANNEL, @@ -6,245 +12,243 @@ enum ChatType { } declare const xbbcode: any; -namespace MessageHelper { - export function htmlEscape(message: string) : string[] { - const div = document.createElement('div'); - div.innerText = message; - message = div.innerHTML; - return message.replace(/ /g, ' ').split(/
/); - } +export function htmlEscape(message: string) : string[] { + const div = document.createElement('div'); + div.innerText = message; + message = div.innerHTML; + return message.replace(/ /g, ' ').split(/
/); +} - export function formatElement(object: any, escape_html: boolean = true) : JQuery[] { - if($.isArray(object)) { - let result = []; - for(let element of object) - result.push(...formatElement(element, escape_html)); - return result; - } else if(typeof(object) == "string") { - if(object.length == 0) return []; +export function formatElement(object: any, escape_html: boolean = true) : JQuery[] { + if($.isArray(object)) { + let result = []; + for(let element of object) + result.push(...formatElement(element, escape_html)); + return result; + } else if(typeof(object) == "string") { + if(object.length == 0) return []; - return escape_html ? - htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)) : - [$.spawn("div").css("display", "inline-block").html(object)]; - } else if(typeof(object) === "object") { - if(object instanceof $) - return [object as any]; - return formatElement(""); - } else if(typeof(object) === "function") return formatElement(object(), escape_html); - else if(typeof(object) === "undefined") return formatElement(""); - else if(typeof(object) === "number") return [$.spawn("a").text(object)]; - return formatElement(""); - } + return escape_html ? + htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)) : + [$.spawn("div").css("display", "inline-block").html(object)]; + } else if(typeof(object) === "object") { + if(object instanceof $) + return [object as any]; + return formatElement(""); + } else if(typeof(object) === "function") return formatElement(object(), escape_html); + else if(typeof(object) === "undefined") return formatElement(""); + else if(typeof(object) === "number") return [$.spawn("a").text(object)]; + return formatElement(""); +} - export function formatMessage(pattern: string, ...objects: any[]) : JQuery[] { - let begin = 0, found = 0; +export function formatMessage(pattern: string, ...objects: any[]) : JQuery[] { + let begin = 0, found = 0; - let result: JQuery[] = []; - do { - found = pattern.indexOf('{', found); - if(found == -1 || pattern.length <= found + 1) { - result.push(...formatElement(pattern.substr(begin))); - break; - } + let result: JQuery[] = []; + do { + found = pattern.indexOf('{', found); + if(found == -1 || pattern.length <= found + 1) { + result.push(...formatElement(pattern.substr(begin))); + break; + } - if(found > 0 && pattern[found - 1] == '\\') { - //TODO remove the escape! + if(found > 0 && pattern[found - 1] == '\\') { + //TODO remove the escape! + found++; + continue; + } + + result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text + + let offset = 0; + if(pattern[found + 1] == ':') { + offset++; /* the beginning : */ + while (pattern[found + 1 + offset] != ':' && found + 1 + offset < pattern.length) offset++; + const tag = pattern.substr(found + 2, offset - 1); + + offset++; /* the ending : */ + if(pattern[found + offset + 1] != '}' && found + 1 + offset < pattern.length) { found++; continue; } - result.push(...formatElement(pattern.substr(begin, found - begin))); //Append the text - - let offset = 0; - if(pattern[found + 1] == ':') { - offset++; /* the beginning : */ - while (pattern[found + 1 + offset] != ':' && found + 1 + offset < pattern.length) offset++; - const tag = pattern.substr(found + 2, offset - 1); - - offset++; /* the ending : */ - if(pattern[found + offset + 1] != '}' && found + 1 + offset < pattern.length) { - found++; - continue; - } - - result.push($.spawn(tag as any)); - } else { - let number; - while ("0123456789".includes(pattern[found + 1 + offset])) offset++; - number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0"); - if(pattern[found + offset + 1] != '}') { - found++; - continue; - } - - if(objects.length < number) - log.warn(LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number); - - result.push(...formatElement(objects[number])); + result.push($.spawn(tag as any)); + } else { + let number; + while ("0123456789".includes(pattern[found + 1 + offset])) offset++; + number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0"); + if(pattern[found + offset + 1] != '}') { + found++; + continue; } - found = found + 1 + offset; - begin = found + 1; - } while(found++); + if(objects.length < number) + log.warn(LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number); - return result; - } - - //TODO: Remove this (only legacy) - export function bbcode_chat(message: string) : JQuery[] { - return messages.formatter.bbcode.format(message, { - is_chat_message: true - }); - } - - export namespace network { - export const KB = 1024; - export const MB = 1024 * KB; - export const GB = 1024 * MB; - export const TB = 1024 * GB; - - export function format_bytes(value: number, options?: { - time?: string, - unit?: string, - exact?: boolean - }) : string { - options = options || {}; - if(typeof options.exact !== "boolean") - options.exact = true; - if(typeof options.unit !== "string") - options.unit = "Bytes"; - - let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); - - let v, unit; - if(value > 2 * TB) { - unit = "TB"; - v = value / TB; - } else if(value > GB) { - unit = "GB"; - v = value / GB; - } else if(value > MB) { - unit = "MB"; - v = value / MB; - } else if(value > KB) { - unit = "KB"; - v = value / KB; - } else { - unit = ""; - v = value; - } - - let result = ""; - if(options.exact || !unit) { - result += points; - if(options.unit) { - result += " " + options.unit; - if(options.time) - result += "/" + options.time; - } - } - if(unit) { - result += (result ? " / " : "") + v.toFixed(2) + " " + unit; - if(options.time) - result += "/" + options.time; - } - return result; + result.push(...formatElement(objects[number])); } - } - export const K = 1000; - export const M = 1000 * K; - export const G = 1000 * M; - export const T = 1000 * G; - export function format_number(value: number, options?: { + found = found + 1 + offset; + begin = found + 1; + } while(found++); + + return result; +} + +//TODO: Remove this (only legacy) +export function bbcode_chat(message: string) : JQuery[] { + return bbcode.format(message, { + is_chat_message: true + }); +} + +export namespace network { + export const KB = 1024; + export const MB = 1024 * KB; + export const GB = 1024 * MB; + export const TB = 1024 * GB; + + export function format_bytes(value: number, options?: { time?: string, - unit?: string - }) { - options = Object.assign(options || {}, {}); + unit?: string, + exact?: boolean + }) : string { + options = options || {}; + if(typeof options.exact !== "boolean") + options.exact = true; + if(typeof options.unit !== "string") + options.unit = "Bytes"; let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); let v, unit; - if(value > 2 * T) { - unit = "T"; - v = value / T; - } else if(value > G) { - unit = "G"; - v = value / G; - } else if(value > M) { - unit = "M"; - v = value / M; - } else if(value > K) { - unit = "K"; - v = value / K; + if(value > 2 * TB) { + unit = "TB"; + v = value / TB; + } else if(value > GB) { + unit = "GB"; + v = value / GB; + } else if(value > MB) { + unit = "MB"; + v = value / MB; + } else if(value > KB) { + unit = "KB"; + v = value / KB; } else { unit = ""; v = value; } - if(unit && options.time) - unit = unit + "/" + options.time; - return points + " " + (options.unit || "") + (unit ? (" / " + v.toFixed(2) + " " + unit) : ""); - } - export const TIME_SECOND = 1000; - export const TIME_MINUTE = 60 * TIME_SECOND; - export const TIME_HOUR = 60 * TIME_MINUTE; - export const TIME_DAY = 24 * TIME_HOUR; - export const TIME_WEEK = 7 * TIME_DAY; - - export function format_time(time: number, default_value: string) { let result = ""; - if(time > TIME_WEEK) { - const amount = Math.floor(time / TIME_WEEK); - result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); - time -= amount * TIME_WEEK; + if(options.exact || !unit) { + result += points; + if(options.unit) { + result += " " + options.unit; + if(options.time) + result += "/" + options.time; + } } - - if(time > TIME_DAY) { - const amount = Math.floor(time / TIME_DAY); - result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); - time -= amount * TIME_DAY; + if(unit) { + result += (result ? " / " : "") + v.toFixed(2) + " " + unit; + if(options.time) + result += "/" + options.time; } + return result; + } +} - if(time > TIME_HOUR) { - const amount = Math.floor(time / TIME_HOUR); - result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); - time -= amount * TIME_HOUR; - } +export const K = 1000; +export const M = 1000 * K; +export const G = 1000 * M; +export const T = 1000 * G; +export function format_number(value: number, options?: { + time?: string, + unit?: string +}) { + options = Object.assign(options || {}, {}); - if(time > TIME_MINUTE) { - const amount = Math.floor(time / TIME_MINUTE); - result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); - time -= amount * TIME_MINUTE; - } + let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); - if(time > TIME_SECOND) { - const amount = Math.floor(time / TIME_SECOND); - result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); - time -= amount * TIME_SECOND; - } + let v, unit; + if(value > 2 * T) { + unit = "T"; + v = value / T; + } else if(value > G) { + unit = "G"; + v = value / G; + } else if(value > M) { + unit = "M"; + v = value / M; + } else if(value > K) { + unit = "K"; + v = value / K; + } else { + unit = ""; + v = value; + } + if(unit && options.time) + unit = unit + "/" + options.time; + return points + " " + (options.unit || "") + (unit ? (" / " + v.toFixed(2) + " " + unit) : ""); +} - return result.length > 0 ? result.substring(1) : default_value; +export const TIME_SECOND = 1000; +export const TIME_MINUTE = 60 * TIME_SECOND; +export const TIME_HOUR = 60 * TIME_MINUTE; +export const TIME_DAY = 24 * TIME_HOUR; +export const TIME_WEEK = 7 * TIME_DAY; + +export function format_time(time: number, default_value: string) { + let result = ""; + if(time > TIME_WEEK) { + const amount = Math.floor(time / TIME_WEEK); + result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); + time -= amount * TIME_WEEK; } - let _icon_size_style: JQuery; - export function set_icon_size(size: string) { - if(!_icon_size_style) - _icon_size_style = $.spawn("style").appendTo($("#style")); - - _icon_size_style.text("\n" + - ".message > .emoji {\n" + - " height: " + size + "!important;\n" + - " width: " + size + "!important;\n" + - "}\n" - ); + if(time > TIME_DAY) { + const amount = Math.floor(time / TIME_DAY); + result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); + time -= amount * TIME_DAY; } - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "icon size init", - function: async () => { - MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em"); - }, - priority: 10 - }); -} \ No newline at end of file + if(time > TIME_HOUR) { + const amount = Math.floor(time / TIME_HOUR); + result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); + time -= amount * TIME_HOUR; + } + + if(time > TIME_MINUTE) { + const amount = Math.floor(time / TIME_MINUTE); + result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); + time -= amount * TIME_MINUTE; + } + + if(time > TIME_SECOND) { + const amount = Math.floor(time / TIME_SECOND); + result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); + time -= amount * TIME_SECOND; + } + + return result.length > 0 ? result.substring(1) : default_value; +} + +let _icon_size_style: JQuery; +export function set_icon_size(size: string) { + if(!_icon_size_style) + _icon_size_style = $.spawn("style").appendTo($("#style")); + + _icon_size_style.text("\n" + + ".message > .emoji {\n" + + " height: " + size + "!important;\n" + + " width: " + size + "!important;\n" + + "}\n" + ); +} + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "icon size init", + function: async () => { + set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em"); + }, + priority: 10 +}); \ No newline at end of file diff --git a/shared/js/ui/frames/chat_frame.ts b/shared/js/ui/frames/chat_frame.ts index b871ce95..a584719a 100644 --- a/shared/js/ui/frames/chat_frame.ts +++ b/shared/js/ui/frames/chat_frame.ts @@ -1,417 +1,426 @@ /* the bar on the right with the chats (Channel & Client) */ -namespace chat { - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +import {ClientEntry, MusicClientEntry} from "tc-shared/ui/client"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ServerEntry} from "tc-shared/ui/server"; +import {openMusicManage} from "tc-shared/ui/modal/ModalMusicManage"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {PrivateConverations} from "tc-shared/ui/frames/side/private_conversations"; +import {ClientInfo} from "tc-shared/ui/frames/side/client_info"; +import {MusicInfo} from "tc-shared/ui/frames/side/music_info"; +import {ConversationManager} from "tc-shared/ui/frames/side/conversations"; - export enum InfoFrameMode { - NONE = "none", - CHANNEL_CHAT = "channel_chat", - PRIVATE_CHAT = "private_chat", - CLIENT_INFO = "client_info", - MUSIC_BOT = "music_bot" +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; + +export enum InfoFrameMode { + NONE = "none", + CHANNEL_CHAT = "channel_chat", + PRIVATE_CHAT = "private_chat", + CLIENT_INFO = "client_info", + MUSIC_BOT = "music_bot" +} +export class InfoFrame { + private readonly handle: Frame; + private _html_tag: JQuery; + private _mode: InfoFrameMode; + + private _value_ping: JQuery; + private _ping_updater: number; + + private _channel_text: ChannelEntry; + private _channel_voice: ChannelEntry; + + private _button_conversation: HTMLElement; + + private _button_bot_manage: JQuery; + private _button_song_add: JQuery; + + constructor(handle: Frame) { + this.handle = handle; + this._build_html_tag(); + this.update_channel_talk(); + this.update_channel_text(); + this.set_mode(InfoFrameMode.CHANNEL_CHAT); + this._ping_updater = setInterval(() => this.update_ping(), 2000); + this.update_ping(); } - export class InfoFrame { - private readonly handle: Frame; - private _html_tag: JQuery; - private _mode: InfoFrameMode; - private _value_ping: JQuery; - private _ping_updater: number; + html_tag() : JQuery { return this._html_tag; } + destroy() { + clearInterval(this._ping_updater); - private _channel_text: ChannelEntry; - private _channel_voice: ChannelEntry; + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._value_ping = undefined; + } - private _button_conversation: HTMLElement; + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_info").renderTag(); + this._html_tag.find(".button-switch-chat-channel").on('click', () => this.handle.show_channel_conversations()); + this._value_ping = this._html_tag.find(".value-ping"); + this._html_tag.find(".chat-counter").on('click', event => this.handle.show_private_conversations()); + this._button_conversation = this._html_tag.find(".button.open-conversation").on('click', event => { + const selected_client = this.handle.client_info().current_client(); + if(!selected_client) return; - private _button_bot_manage: JQuery; - private _button_song_add: JQuery; + const conversation = selected_client ? this.handle.private_conversations().find_conversation({ + name: selected_client.properties.client_nickname, + unique_id: selected_client.properties.client_unique_identifier, + client_id: selected_client.clientId() + }, { create: true, attach: true }) : undefined; + if(!conversation) return; - constructor(handle: Frame) { - this.handle = handle; - this._build_html_tag(); - this.update_channel_talk(); - this.update_channel_text(); - this.set_mode(InfoFrameMode.CHANNEL_CHAT); - this._ping_updater = setInterval(() => this.update_ping(), 2000); - this.update_ping(); + this.handle.private_conversations().set_selected_conversation(conversation); + this.handle.show_private_conversations(); + })[0]; + + this._button_bot_manage = this._html_tag.find(".bot-manage").on('click', event => { + const bot = this.handle.music_info().current_bot(); + if(!bot) return; + + openMusicManage(this.handle.handle, bot); + }); + this._button_song_add = this._html_tag.find(".bot-add-song").on('click', event => { + this.handle.music_info().events.fire("action_song_add"); + }); + } + + update_ping() { + this._value_ping.removeClass("very-good good medium poor very-poor"); + const connection = this.handle.handle.serverConnection; + if(!this.handle.handle.connected || !connection) { + this._value_ping.text("Not connected"); + return; } - html_tag() : JQuery { return this._html_tag; } - destroy() { - clearInterval(this._ping_updater); - - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._value_ping = undefined; + const ping = connection.ping(); + if(!ping || typeof(ping.native) !== "number") { + this._value_ping.text("Not available"); + return; } - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_info").renderTag(); - this._html_tag.find(".button-switch-chat-channel").on('click', () => this.handle.show_channel_conversations()); - this._value_ping = this._html_tag.find(".value-ping"); - this._html_tag.find(".chat-counter").on('click', event => this.handle.show_private_conversations()); - this._button_conversation = this._html_tag.find(".button.open-conversation").on('click', event => { - const selected_client = this.handle.client_info().current_client(); - if(!selected_client) return; - - const conversation = selected_client ? this.handle.private_conversations().find_conversation({ - name: selected_client.properties.client_nickname, - unique_id: selected_client.properties.client_unique_identifier, - client_id: selected_client.clientId() - }, { create: true, attach: true }) : undefined; - if(!conversation) return; - - this.handle.private_conversations().set_selected_conversation(conversation); - this.handle.show_private_conversations(); - })[0]; - - this._button_bot_manage = this._html_tag.find(".bot-manage").on('click', event => { - const bot = this.handle.music_info().current_bot(); - if(!bot) return; - - Modals.openMusicManage(this.handle.handle, bot); - }); - this._button_song_add = this._html_tag.find(".bot-add-song").on('click', event => { - this.handle.music_info().events.fire("action_song_add"); - }); + let value; + if(typeof(ping.javascript) !== "undefined") { + value = ping.javascript; + this._value_ping.text(ping.javascript.toFixed(0) + "ms").attr('title', 'Native: ' + ping.native.toFixed(3) + "ms \nJavascript: " + ping.javascript.toFixed(3) + "ms"); + } else { + value = ping.native; + this._value_ping.text(ping.native.toFixed(0) + "ms").attr('title', "Ping: " + ping.native.toFixed(3) + "ms"); } - update_ping() { - this._value_ping.removeClass("very-good good medium poor very-poor"); - const connection = this.handle.handle.serverConnection; - if(!this.handle.handle.connected || !connection) { - this._value_ping.text("Not connected"); - return; - } + if(value <= 10) + this._value_ping.addClass("very-good"); + else if(value <= 30) + this._value_ping.addClass("good"); + else if(value <= 60) + this._value_ping.addClass("medium"); + else if(value <= 150) + this._value_ping.addClass("poor"); + else + this._value_ping.addClass("very-poor"); + } - const ping = connection.ping(); - if(!ping || typeof(ping.native) !== "number") { - this._value_ping.text("Not available"); - return; - } + update_channel_talk() { + const client = this.handle.handle.getClient(); + const channel = client ? client.currentChannel() : undefined; + this._channel_voice = channel; - let value; - if(typeof(ping.javascript) !== "undefined") { - value = ping.javascript; - this._value_ping.text(ping.javascript.toFixed(0) + "ms").attr('title', 'Native: ' + ping.native.toFixed(3) + "ms \nJavascript: " + ping.javascript.toFixed(3) + "ms"); - } else { - value = ping.native; - this._value_ping.text(ping.native.toFixed(0) + "ms").attr('title', "Ping: " + ping.native.toFixed(3) + "ms"); - } + const html_tag = this._html_tag.find(".value-voice-channel"); + const html_limit_tag = this._html_tag.find(".value-voice-limit"); - if(value <= 10) - this._value_ping.addClass("very-good"); - else if(value <= 30) - this._value_ping.addClass("good"); - else if(value <= 60) - this._value_ping.addClass("medium"); - else if(value <= 150) - this._value_ping.addClass("poor"); + html_limit_tag.text(""); + html_tag.children().remove(); + + if(channel) { + if(channel.properties.channel_icon_id != 0) + client.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); + $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); + + this.update_channel_limit(channel, html_limit_tag); + } else { + $.spawn("div").text("Not connected").appendTo(html_tag); + } + } + + update_channel_text() { + const channel_tree = this.handle.handle.connected ? this.handle.handle.channelTree : undefined; + const current_channel_id = channel_tree ? this.handle.channel_conversations().current_channel() : 0; + const channel = channel_tree ? channel_tree.findChannel(current_channel_id) : undefined; + this._channel_text = channel; + + const tag_container = this._html_tag.find(".mode-channel_chat.channel"); + const html_tag_title = tag_container.find(".title"); + const html_tag = tag_container.find(".value-text-channel"); + const html_limit_tag = tag_container.find(".value-text-limit"); + + /* reset */ + html_tag_title.text(tr("You're chatting in Channel")); + html_limit_tag.text(""); + html_tag.children().detach(); + + /* initialize */ + if(channel) { + if(channel.properties.channel_icon_id != 0) + this.handle.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); + $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); + + this.update_channel_limit(channel, html_limit_tag); + } else if(channel_tree && current_channel_id > 0) { + html_tag.append(formatMessage(tr("Unknown channel id {}"), current_channel_id)); + } else if(channel_tree && current_channel_id == 0) { + const server = this.handle.handle.channelTree.server; + if(server.properties.virtualserver_icon_id != 0) + this.handle.handle.fileManager.icons.generateTag(server.properties.virtualserver_icon_id).appendTo(html_tag); + $.spawn("div").text(server.properties.virtualserver_name).appendTo(html_tag); + html_tag_title.text(tr("You're chatting in Server")); + + this.update_server_limit(server, html_limit_tag); + } else if(this.handle.handle.connected) { + $.spawn("div").text("No channel selected").appendTo(html_tag); + } else { + $.spawn("div").text("Not connected").appendTo(html_tag); + } + } + + update_channel_client_count(channel: ChannelEntry) { + if(channel === this._channel_text) + this.update_channel_limit(channel, this._html_tag.find(".value-text-limit")); + if(channel === this._channel_voice) + this.update_channel_limit(channel, this._html_tag.find(".value-voice-limit")); + } + + private update_channel_limit(channel: ChannelEntry, tag: JQuery) { + let channel_limit = tr("Unlimited"); + if(!channel.properties.channel_flag_maxclients_unlimited) + channel_limit = "" + channel.properties.channel_maxclients; + else if(!channel.properties.channel_flag_maxfamilyclients_unlimited) { + if(channel.properties.channel_maxfamilyclients >= 0) + channel_limit = "" + channel.properties.channel_maxfamilyclients; + } + tag.text(channel.clients(false).length + " / " + channel_limit); + } + + private update_server_limit(server: ServerEntry, tag: JQuery) { + const fn = () => { + let text = server.properties.virtualserver_clientsonline + " / " + server.properties.virtualserver_maxclients; + if(server.properties.virtualserver_reserved_slots) + text += " (" + server.properties.virtualserver_reserved_slots + " " + tr("Reserved") + ")"; + tag.text(text); + }; + + server.updateProperties().then(fn).catch(error => tag.text(tr("Failed to update info"))); + fn(); + } + + update_chat_counter() { + const conversations = this.handle.private_conversations().conversations(); + { + const count = conversations.filter(e => e.is_unread()).length; + const count_container = this._html_tag.find(".container-indicator"); + const count_tag = count_container.find(".chat-unread-counter"); + count_container.toggle(count > 0); + count_tag.text(count); + } + { + const count_tag = this._html_tag.find(".chat-counter"); + if(conversations.length == 0) + count_tag.text(tr("No conversations")); + else if(conversations.length == 1) + count_tag.text(tr("One conversation")); else - this._value_ping.addClass("very-poor"); - } - - update_channel_talk() { - const client = this.handle.handle.getClient(); - const channel = client ? client.currentChannel() : undefined; - this._channel_voice = channel; - - const html_tag = this._html_tag.find(".value-voice-channel"); - const html_limit_tag = this._html_tag.find(".value-voice-limit"); - - html_limit_tag.text(""); - html_tag.children().remove(); - - if(channel) { - if(channel.properties.channel_icon_id != 0) - client.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); - $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); - - this.update_channel_limit(channel, html_limit_tag); - } else { - $.spawn("div").text("Not connected").appendTo(html_tag); - } - } - - update_channel_text() { - const channel_tree = this.handle.handle.connected ? this.handle.handle.channelTree : undefined; - const current_channel_id = channel_tree ? this.handle.channel_conversations().current_channel() : 0; - const channel = channel_tree ? channel_tree.findChannel(current_channel_id) : undefined; - this._channel_text = channel; - - const tag_container = this._html_tag.find(".mode-channel_chat.channel"); - const html_tag_title = tag_container.find(".title"); - const html_tag = tag_container.find(".value-text-channel"); - const html_limit_tag = tag_container.find(".value-text-limit"); - - /* reset */ - html_tag_title.text(tr("You're chatting in Channel")); - html_limit_tag.text(""); - html_tag.children().detach(); - - /* initialize */ - if(channel) { - if(channel.properties.channel_icon_id != 0) - this.handle.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); - $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag); - - this.update_channel_limit(channel, html_limit_tag); - } else if(channel_tree && current_channel_id > 0) { - html_tag.append(MessageHelper.formatMessage(tr("Unknown channel id {}"), current_channel_id)); - } else if(channel_tree && current_channel_id == 0) { - const server = this.handle.handle.channelTree.server; - if(server.properties.virtualserver_icon_id != 0) - this.handle.handle.fileManager.icons.generateTag(server.properties.virtualserver_icon_id).appendTo(html_tag); - $.spawn("div").text(server.properties.virtualserver_name).appendTo(html_tag); - html_tag_title.text(tr("You're chatting in Server")); - - this.update_server_limit(server, html_limit_tag); - } else if(this.handle.handle.connected) { - $.spawn("div").text("No channel selected").appendTo(html_tag); - } else { - $.spawn("div").text("Not connected").appendTo(html_tag); - } - } - - update_channel_client_count(channel: ChannelEntry) { - if(channel === this._channel_text) - this.update_channel_limit(channel, this._html_tag.find(".value-text-limit")); - if(channel === this._channel_voice) - this.update_channel_limit(channel, this._html_tag.find(".value-voice-limit")); - } - - private update_channel_limit(channel: ChannelEntry, tag: JQuery) { - let channel_limit = tr("Unlimited"); - if(!channel.properties.channel_flag_maxclients_unlimited) - channel_limit = "" + channel.properties.channel_maxclients; - else if(!channel.properties.channel_flag_maxfamilyclients_unlimited) { - if(channel.properties.channel_maxfamilyclients >= 0) - channel_limit = "" + channel.properties.channel_maxfamilyclients; - } - tag.text(channel.clients(false).length + " / " + channel_limit); - } - - private update_server_limit(server: ServerEntry, tag: JQuery) { - const fn = () => { - let text = server.properties.virtualserver_clientsonline + " / " + server.properties.virtualserver_maxclients; - if(server.properties.virtualserver_reserved_slots) - text += " (" + server.properties.virtualserver_reserved_slots + " " + tr("Reserved") + ")"; - tag.text(text); - }; - - server.updateProperties().then(fn).catch(error => tag.text(tr("Failed to update info"))); - fn(); - } - - update_chat_counter() { - const conversations = this.handle.private_conversations().conversations(); - { - const count = conversations.filter(e => e.is_unread()).length; - const count_container = this._html_tag.find(".container-indicator"); - const count_tag = count_container.find(".chat-unread-counter"); - count_container.toggle(count > 0); - count_tag.text(count); - } - { - const count_tag = this._html_tag.find(".chat-counter"); - if(conversations.length == 0) - count_tag.text(tr("No conversations")); - else if(conversations.length == 1) - count_tag.text(tr("One conversation")); - else - count_tag.text(conversations.length + " " + tr("conversations")); - } - } - - current_mode() : InfoFrameMode { - return this._mode; - } - - set_mode(mode: InfoFrameMode) { - for(const mode in InfoFrameMode) - this._html_tag.removeClass("mode-" + InfoFrameMode[mode]); - this._html_tag.addClass("mode-" + mode); - - if(mode === InfoFrameMode.CLIENT_INFO && this._button_conversation) { - //Will be called every time a client is shown - const selected_client = this.handle.client_info().current_client(); - const conversation = selected_client ? this.handle.private_conversations().find_conversation({ - name: selected_client.properties.client_nickname, - unique_id: selected_client.properties.client_unique_identifier, - client_id: selected_client.clientId() - }, { create: false, attach: false }) : undefined; - - const visibility = (selected_client && selected_client.clientId() !== this.handle.handle.clientId) ? "visible" : "hidden"; - if(this._button_conversation.style.visibility !== visibility) - this._button_conversation.style.visibility = visibility; - if(conversation) { - this._button_conversation.innerText = tr("Open conversation"); - } else { - this._button_conversation.innerText = tr("Start a conversation"); - } - } else if(mode === InfoFrameMode.MUSIC_BOT) { - //TODO? - } + count_tag.text(conversations.length + " " + tr("conversations")); } } - export enum FrameContent { - NONE, - PRIVATE_CHAT, - CHANNEL_CHAT, - CLIENT_INFO, - MUSIC_BOT + current_mode() : InfoFrameMode { + return this._mode; } - export class Frame { - readonly handle: ConnectionHandler; - private _info_frame: InfoFrame; - private _html_tag: JQuery; - private _container_info: JQuery; - private _container_chat: JQuery; - private _content_type: FrameContent; + set_mode(mode: InfoFrameMode) { + for(const mode in InfoFrameMode) + this._html_tag.removeClass("mode-" + InfoFrameMode[mode]); + this._html_tag.addClass("mode-" + mode); - private _conversations: PrivateConverations; - private _client_info: ClientInfo; - private _music_info: MusicInfo; - private _channel_conversations: channel.ConversationManager; + if(mode === InfoFrameMode.CLIENT_INFO && this._button_conversation) { + //Will be called every time a client is shown + const selected_client = this.handle.client_info().current_client(); + const conversation = selected_client ? this.handle.private_conversations().find_conversation({ + name: selected_client.properties.client_nickname, + unique_id: selected_client.properties.client_unique_identifier, + client_id: selected_client.clientId() + }, { create: false, attach: false }) : undefined; - constructor(handle: ConnectionHandler) { - this.handle = handle; + const visibility = (selected_client && selected_client.clientId() !== this.handle.handle.clientId) ? "visible" : "hidden"; + if(this._button_conversation.style.visibility !== visibility) + this._button_conversation.style.visibility = visibility; + if(conversation) { + this._button_conversation.innerText = tr("Open conversation"); + } else { + this._button_conversation.innerText = tr("Start a conversation"); + } + } else if(mode === InfoFrameMode.MUSIC_BOT) { + //TODO? + } + } +} - this._content_type = FrameContent.NONE; - this._info_frame = new InfoFrame(this); - this._conversations = new PrivateConverations(this); - this._channel_conversations = new channel.ConversationManager(this); - this._client_info = new ClientInfo(this); - this._music_info = new MusicInfo(this); +export enum FrameContent { + NONE, + PRIVATE_CHAT, + CHANNEL_CHAT, + CLIENT_INFO, + MUSIC_BOT +} - this._build_html_tag(); +export class Frame { + readonly handle: ConnectionHandler; + private _info_frame: InfoFrame; + private _html_tag: JQuery; + private _container_info: JQuery; + private _container_chat: JQuery; + private _content_type: FrameContent; + + private _conversations: PrivateConverations; + private _client_info: ClientInfo; + private _music_info: MusicInfo; + private _channel_conversations: ConversationManager; + + constructor(handle: ConnectionHandler) { + this.handle = handle; + + this._content_type = FrameContent.NONE; + this._info_frame = new InfoFrame(this); + this._conversations = new PrivateConverations(this); + this._channel_conversations = new ConversationManager(this); + this._client_info = new ClientInfo(this); + this._music_info = new MusicInfo(this); + + this._build_html_tag(); + this.show_channel_conversations(); + this.info_frame().update_chat_counter(); + } + + html_tag() : JQuery { return this._html_tag; } + info_frame() : InfoFrame { return this._info_frame; } + + content_type() : FrameContent { return this._content_type; } + + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + + this._info_frame && this._info_frame.destroy(); + this._info_frame = undefined; + + this._conversations && this._conversations.destroy(); + this._conversations = undefined; + + this._client_info && this._client_info.destroy(); + this._client_info = undefined; + + this._music_info && this._music_info.destroy(); + this._music_info = undefined; + + this._channel_conversations && this._channel_conversations.destroy(); + this._channel_conversations = undefined; + + this._container_info && this._container_info.remove(); + this._container_info = undefined; + + this._container_chat && this._container_chat.remove(); + this._container_chat = undefined; + } + + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat").renderTag(); + this._container_info = this._html_tag.find(".container-info"); + this._container_chat = this._html_tag.find(".container-chat"); + + this._info_frame.html_tag().appendTo(this._container_info); + } + + + private_conversations() : PrivateConverations { + return this._conversations; + } + + channel_conversations() : ConversationManager { + return this._channel_conversations; + } + + client_info() : ClientInfo { + return this._client_info; + } + + music_info() : MusicInfo { + return this._music_info; + } + + private _clear() { + this._content_type = FrameContent.NONE; + this._container_chat.children().detach(); + } + + show_private_conversations() { + if(this._content_type === FrameContent.PRIVATE_CHAT) + return; + this._clear(); + this._content_type = FrameContent.PRIVATE_CHAT; + this._container_chat.append(this._conversations.html_tag()); + this._conversations.on_show(); + this._info_frame.set_mode(InfoFrameMode.PRIVATE_CHAT); + } + + show_channel_conversations() { + if(this._content_type === FrameContent.CHANNEL_CHAT) + return; + + this._clear(); + this._content_type = FrameContent.CHANNEL_CHAT; + this._container_chat.append(this._channel_conversations.html_tag()); + this._channel_conversations.on_show(); + this._info_frame.set_mode(InfoFrameMode.CHANNEL_CHAT); + } + + show_client_info(client: ClientEntry) { + this._client_info.set_current_client(client); + this._info_frame.set_mode(InfoFrameMode.CLIENT_INFO); /* specially needs an update here to update the conversation button */ + + if(this._content_type === FrameContent.CLIENT_INFO) + return; + + this._client_info.previous_frame_content = this._content_type; + this._clear(); + this._content_type = FrameContent.CLIENT_INFO; + this._container_chat.append(this._client_info.html_tag()); + } + + show_music_player(client: MusicClientEntry) { + this._music_info.set_current_bot(client); + + if(this._content_type === FrameContent.MUSIC_BOT) + return; + + this._info_frame.set_mode(InfoFrameMode.MUSIC_BOT); + this._music_info.previous_frame_content = this._content_type; + this._clear(); + this._content_type = FrameContent.MUSIC_BOT; + this._container_chat.append(this._music_info.html_tag()); + } + + set_content(type: FrameContent) { + if(this._content_type === type) + return; + + if(type === FrameContent.CHANNEL_CHAT) this.show_channel_conversations(); - this.info_frame().update_chat_counter(); - } - - html_tag() : JQuery { return this._html_tag; } - info_frame() : InfoFrame { return this._info_frame; } - - content_type() : FrameContent { return this._content_type; } - - destroy() { - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - - this._info_frame && this._info_frame.destroy(); - this._info_frame = undefined; - - this._conversations && this._conversations.destroy(); - this._conversations = undefined; - - this._client_info && this._client_info.destroy(); - this._client_info = undefined; - - this._music_info && this._music_info.destroy(); - this._music_info = undefined; - - this._channel_conversations && this._channel_conversations.destroy(); - this._channel_conversations = undefined; - - this._container_info && this._container_info.remove(); - this._container_info = undefined; - - this._container_chat && this._container_chat.remove(); - this._container_chat = undefined; - } - - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat").renderTag(); - this._container_info = this._html_tag.find(".container-info"); - this._container_chat = this._html_tag.find(".container-chat"); - - this._info_frame.html_tag().appendTo(this._container_info); - } - - - private_conversations() : PrivateConverations { - return this._conversations; - } - - channel_conversations() : channel.ConversationManager { - return this._channel_conversations; - } - - client_info() : ClientInfo { - return this._client_info; - } - - music_info() : MusicInfo { - return this._music_info; - } - - private _clear() { + else if(type === FrameContent.PRIVATE_CHAT) + this.show_private_conversations(); + else { + this._clear(); this._content_type = FrameContent.NONE; - this._container_chat.children().detach(); - } - - show_private_conversations() { - if(this._content_type === FrameContent.PRIVATE_CHAT) - return; - this._clear(); - this._content_type = FrameContent.PRIVATE_CHAT; - this._container_chat.append(this._conversations.html_tag()); - this._conversations.on_show(); - this._info_frame.set_mode(InfoFrameMode.PRIVATE_CHAT); - } - - show_channel_conversations() { - if(this._content_type === FrameContent.CHANNEL_CHAT) - return; - - this._clear(); - this._content_type = FrameContent.CHANNEL_CHAT; - this._container_chat.append(this._channel_conversations.html_tag()); - this._channel_conversations.on_show(); - this._info_frame.set_mode(InfoFrameMode.CHANNEL_CHAT); - } - - show_client_info(client: ClientEntry) { - this._client_info.set_current_client(client); - this._info_frame.set_mode(InfoFrameMode.CLIENT_INFO); /* specially needs an update here to update the conversation button */ - - if(this._content_type === FrameContent.CLIENT_INFO) - return; - - this._client_info.previous_frame_content = this._content_type; - this._clear(); - this._content_type = FrameContent.CLIENT_INFO; - this._container_chat.append(this._client_info.html_tag()); - } - - show_music_player(client: MusicClientEntry) { - this._music_info.set_current_bot(client); - - if(this._content_type === FrameContent.MUSIC_BOT) - return; - - this._info_frame.set_mode(InfoFrameMode.MUSIC_BOT); - this._music_info.previous_frame_content = this._content_type; - this._clear(); - this._content_type = FrameContent.MUSIC_BOT; - this._container_chat.append(this._music_info.html_tag()); - } - - set_content(type: FrameContent) { - if(this._content_type === type) - return; - - if(type === FrameContent.CHANNEL_CHAT) - this.show_channel_conversations(); - else if(type === FrameContent.PRIVATE_CHAT) - this.show_private_conversations(); - else { - this._clear(); - this._content_type = FrameContent.NONE; - this._info_frame.set_mode(InfoFrameMode.NONE); - } + this._info_frame.set_mode(InfoFrameMode.NONE); } } } \ No newline at end of file diff --git a/shared/js/ui/frames/connection_handlers.ts b/shared/js/ui/frames/connection_handlers.ts index 4d9fb936..1decafc0 100644 --- a/shared/js/ui/frames/connection_handlers.ts +++ b/shared/js/ui/frames/connection_handlers.ts @@ -1,7 +1,13 @@ +import {ConnectionHandler, DisconnectReason} from "tc-shared/ConnectionHandler"; +import {Settings, settings} from "tc-shared/settings"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import * as top_menu from "./MenuBar"; -let server_connections: ServerConnectionManager; - -class ServerConnectionManager { +export let server_connections: ServerConnectionManager; +export function initialize(manager: ServerConnectionManager) { + server_connections = manager; +} +export class ServerConnectionManager { private connection_handlers: ConnectionHandler[] = []; private active_handler: ConnectionHandler | undefined; diff --git a/shared/js/ui/frames/hostbanner.ts b/shared/js/ui/frames/hostbanner.ts index 2b0466d1..9fa20334 100644 --- a/shared/js/ui/frames/hostbanner.ts +++ b/shared/js/ui/frames/hostbanner.ts @@ -1,4 +1,9 @@ -class Hostbanner { +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {settings, Settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; + +export class Hostbanner { readonly html_tag: JQuery; readonly client: ConnectionHandler; diff --git a/shared/js/ui/frames/image_preview.ts b/shared/js/ui/frames/image_preview.ts index af46412f..0ebd02b3 100644 --- a/shared/js/ui/frames/image_preview.ts +++ b/shared/js/ui/frames/image_preview.ts @@ -1,81 +1,81 @@ -namespace image_preview { - let preview_overlay: JQuery; - let container_image: JQuery; - let button_open_in_browser: JQuery; +import * as loader from "tc-loader"; - export function preview_image(url: string, original_url: string) { - if(!preview_overlay) return; +let preview_overlay: JQuery; +let container_image: JQuery; +let button_open_in_browser: JQuery; - container_image.empty(); - $.spawn("img").attr({ - "src": url, - "title": original_url, - "x-original-src": original_url - }).appendTo(container_image); +export function preview_image(url: string, original_url: string) { + if(!preview_overlay) return; - preview_overlay.removeClass("hidden"); - button_open_in_browser.show(); + container_image.empty(); + $.spawn("img").attr({ + "src": url, + "title": original_url, + "x-original-src": original_url + }).appendTo(container_image); + + preview_overlay.removeClass("hidden"); + button_open_in_browser.show(); +} + +export function preview_image_tag(tag: JQuery) { + if(!preview_overlay) return; + + container_image.empty(); + container_image.append(tag); + + preview_overlay.removeClass("hidden"); + button_open_in_browser.hide(); +} + +export function current_url() { + const image_tag = container_image.find("img"); + return image_tag.attr("x-original-src") || image_tag.attr("src") || ""; +} + +export function close_preview() { + preview_overlay.addClass("hidden"); +} + +loader.register_task(loader.Stage.LOADED, { + priority: 0, + name: "image preview init", + function: async () => { + preview_overlay = $("#overlay-image-preview"); + container_image = preview_overlay.find(".container-image") as any; + + preview_overlay.find("img").on('click', event => event.preventDefault()); + preview_overlay.on('click', event => { + if(event.isDefaultPrevented()) return; + close_preview(); + }); + + preview_overlay.find(".button-close").on('click', event => { + event.preventDefault(); + close_preview(); + }); + + preview_overlay.find(".button-download").on('click', event => { + event.preventDefault(); + + const link = document.createElement('a'); + link.href = current_url(); + link.target = "_blank"; + + const findex = link.href.lastIndexOf("/") + 1; + link.download = link.href.substr(findex); + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }); + + button_open_in_browser = preview_overlay.find(".button-open-in-window"); + button_open_in_browser.on('click', event => { + event.preventDefault(); + + const win = window.open(current_url(), '_blank'); + win.focus(); + }); } - - export function preview_image_tag(tag: JQuery) { - if(!preview_overlay) return; - - container_image.empty(); - container_image.append(tag); - - preview_overlay.removeClass("hidden"); - button_open_in_browser.hide(); - } - - export function current_url() { - const image_tag = container_image.find("img"); - return image_tag.attr("x-original-src") || image_tag.attr("src") || ""; - } - - export function close_preview() { - preview_overlay.addClass("hidden"); - } - - loader.register_task(loader.Stage.LOADED, { - priority: 0, - name: "image preview init", - function: async () => { - preview_overlay = $("#overlay-image-preview"); - container_image = preview_overlay.find(".container-image") as any; - - preview_overlay.find("img").on('click', event => event.preventDefault()); - preview_overlay.on('click', event => { - if(event.isDefaultPrevented()) return; - close_preview(); - }); - - preview_overlay.find(".button-close").on('click', event => { - event.preventDefault(); - close_preview(); - }); - - preview_overlay.find(".button-download").on('click', event => { - event.preventDefault(); - - const link = document.createElement('a'); - link.href = current_url(); - link.target = "_blank"; - - const findex = link.href.lastIndexOf("/") + 1; - link.download = link.href.substr(findex); - - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - }); - - button_open_in_browser = preview_overlay.find(".button-open-in-window"); - button_open_in_browser.on('click', event => { - event.preventDefault(); - - const win = window.open(current_url(), '_blank'); - win.focus(); - }); - } - }); -} \ No newline at end of file +}); \ No newline at end of file diff --git a/shared/js/ui/frames/server_log.ts b/shared/js/ui/frames/server_log.ts index d6e29f22..bfaaab6b 100644 --- a/shared/js/ui/frames/server_log.ts +++ b/shared/js/ui/frames/server_log.ts @@ -1,566 +1,563 @@ -namespace log { - export namespace server { - export enum Type { - CONNECTION_BEGIN = "connection_begin", - CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", - CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", - CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", - CONNECTION_LOGIN = "connection_login", - CONNECTION_CONNECTED = "connection_connected", - CONNECTION_FAILED = "connection_failed", +import {tra} from "tc-shared/i18n/localize"; +import {PermissionInfo} from "tc-shared/permission/PermissionManager"; +import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler"; +import * as htmltags from "tc-shared/ui/htmltags"; +import {bbcode_chat, format_time, formatMessage} from "tc-shared/ui/frames/chat"; +import {formatDate} from "tc-shared/MessageFormatter"; - DISCONNECTED = "disconnected", +export enum Type { + CONNECTION_BEGIN = "connection_begin", + CONNECTION_HOSTNAME_RESOLVE = "connection_hostname_resolve", + CONNECTION_HOSTNAME_RESOLVE_ERROR = "connection_hostname_resolve_error", + CONNECTION_HOSTNAME_RESOLVED = "connection_hostname_resolved", + CONNECTION_LOGIN = "connection_login", + CONNECTION_CONNECTED = "connection_connected", + CONNECTION_FAILED = "connection_failed", - CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", - CONNECTION_COMMAND_ERROR = "connection_command_error", + DISCONNECTED = "disconnected", - GLOBAL_MESSAGE = "global_message", + CONNECTION_VOICE_SETUP_FAILED = "connection_voice_setup_failed", + CONNECTION_COMMAND_ERROR = "connection_command_error", - SERVER_WELCOME_MESSAGE = "server_welcome_message", - SERVER_HOST_MESSAGE = "server_host_message", - SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", + GLOBAL_MESSAGE = "global_message", - SERVER_CLOSED = "server_closed", - SERVER_BANNED = "server_banned", - SERVER_REQUIRES_PASSWORD = "server_requires_password", + SERVER_WELCOME_MESSAGE = "server_welcome_message", + SERVER_HOST_MESSAGE = "server_host_message", + SERVER_HOST_MESSAGE_DISCONNECT = "server_host_message_disconnect", - CLIENT_VIEW_ENTER = "client_view_enter", - CLIENT_VIEW_LEAVE = "client_view_leave", - CLIENT_VIEW_MOVE = "client_view_move", + SERVER_CLOSED = "server_closed", + SERVER_BANNED = "server_banned", + SERVER_REQUIRES_PASSWORD = "server_requires_password", - CLIENT_NICKNAME_CHANGED = "client_nickname_changed", - CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", + CLIENT_VIEW_ENTER = "client_view_enter", + CLIENT_VIEW_LEAVE = "client_view_leave", + CLIENT_VIEW_MOVE = "client_view_move", - CLIENT_SERVER_GROUP_ADD = "client_server_group_add", - CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", - CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", + CLIENT_NICKNAME_CHANGED = "client_nickname_changed", + CLIENT_NICKNAME_CHANGE_FAILED = "client_nickname_change_failed", - CHANNEL_CREATE = "channel_create", - CHANNEL_DELETE = "channel_delete", + CLIENT_SERVER_GROUP_ADD = "client_server_group_add", + CLIENT_SERVER_GROUP_REMOVE = "client_server_group_remove", + CLIENT_CHANNEL_GROUP_CHANGE = "client_channel_group_change", - ERROR_CUSTOM = "error_custom", - ERROR_PERMISSION = "error_permission", + CHANNEL_CREATE = "channel_create", + CHANNEL_DELETE = "channel_delete", - RECONNECT_SCHEDULED = "reconnect_scheduled", - RECONNECT_EXECUTE = "reconnect_execute", - RECONNECT_CANCELED = "reconnect_canceled" - } + ERROR_CUSTOM = "error_custom", + ERROR_PERMISSION = "error_permission", - export namespace base { - export type Client = { - client_unique_id: string; - client_name: string; - client_id: number; - } - export type Channel = { - channel_id: number; - channel_name: string; - } - export type Server = { - server_name: string; - server_unique_id: string; - } - export type ServerAddress = { - server_hostname: string; - server_port: number; - } - } + RECONNECT_SCHEDULED = "reconnect_scheduled", + RECONNECT_EXECUTE = "reconnect_execute", + RECONNECT_CANCELED = "reconnect_canceled" +} - export namespace event { - export type GlobalMessage = { - sender: base.Client; - message: string; - } - export type ConnectBegin = { - address: base.ServerAddress; - client_nickname: string; - } - export type ErrorCustom = { - message: string; - } +export namespace base { + export type Client = { + client_unique_id: string; + client_name: string; + client_id: number; + } + export type Channel = { + channel_id: number; + channel_name: string; + } + export type Server = { + server_name: string; + server_unique_id: string; + } + export type ServerAddress = { + server_hostname: string; + server_port: number; + } +} - export type ReconnectScheduled = { - timeout: number; - } - - export type ReconnectCanceled = { } - export type ReconnectExecute = { } - - export type ErrorPermission = { - permission: PermissionInfo; - } - - export type WelcomeMessage = { - message: string; - } - - export type HostMessageDisconnect = { - message: string; - } - - export type ClientMove = { - channel_from?: base.Channel; - channel_from_own: boolean; - - channel_to?: base.Channel; - channel_to_own: boolean; - - client: base.Client; - client_own: boolean; - - invoker?: base.Client; - - message?: string; - reason: ViewReasonId; - } - - export type ClientEnter = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ClientLeave = { - channel_from?: base.Channel; - channel_to?: base.Channel; - - client: base.Client; - invoker?: base.Client; - - message?: string; - own_channel: boolean; - - reason: ViewReasonId; - ban_time?: number; - } - - export type ChannelCreate = { - creator: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ChannelDelete = { - deleter: base.Client; - channel: base.Channel; - - own_action: boolean; - } - - export type ConnectionConnected = { - own_client: base.Client; - } - export type ConnectionFailed = {}; - export type ConnectionLogin = {} - export type ConnectionHostnameResolve = {}; - export type ConnectionHostnameResolved = { - address: base.ServerAddress; - } - export type ConnectionHostnameResolveError = { - message: string; - } - - export type ConnectionVoiceSetupFailed = { - reason: string; - reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ - } - - export type ConnectionCommandError = { - error: any; - } - - export type ClientNicknameChanged = { - own_client: boolean; - - client: base.Client; - - old_name: string; - new_name: string; - } - - export type ClientNicknameChangeFailed = { - reason: string; - } - - export type ServerClosed = { - message: string; - } - - export type ServerRequiresPassword = {} - - export type ServerBanned = { - message: string; - time: number; - - invoker: base.Client; - } - } - - export type LogMessage = { - type: Type; - timestamp: number; - data: any; - } - - export interface TypeInfo { - "connection_begin" : event.ConnectBegin; - "global_message": event.GlobalMessage; - - "error_custom": event.ErrorCustom; - "error_permission": event.ErrorPermission; - - "connection_hostname_resolved": event.ConnectionHostnameResolved; - "connection_hostname_resolve": event.ConnectionHostnameResolve; - "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; - "connection_failed": event.ConnectionFailed; - "connection_login": event.ConnectionLogin; - "connection_connected": event.ConnectionConnected; - "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; - "connection_command_error": event.ConnectionCommandError; - - "reconnect_scheduled": event.ReconnectScheduled; - "reconnect_canceled": event.ReconnectCanceled; - "reconnect_execute": event.ReconnectExecute; - - "server_welcome_message": event.WelcomeMessage; - "server_host_message": event.WelcomeMessage; - "server_host_message_disconnect": event.HostMessageDisconnect; - - "server_closed": event.ServerClosed; - "server_requires_password": event.ServerRequiresPassword; - "server_banned": event.ServerBanned; - - "client_view_enter": event.ClientEnter; - "client_view_move": event.ClientMove; - "client_view_leave": event.ClientLeave; - - "client_nickname_change_failed": event.ClientNicknameChangeFailed, - "client_nickname_changed": event.ClientNicknameChanged, - - "channel_create": event.ChannelCreate; - "channel_delete": event.ChannelDelete; - - "disconnected": any; - } - - export type MessageBuilderOptions = {}; - export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; - - export const MessageBuilders: {[key: string]: MessageBuilder} = { - "error_custom": (data: event.ErrorCustom, options) => { - return [$.spawn("div").addClass("log-error").text(data.message)] - } - }; +export namespace event { + export type GlobalMessage = { + sender: base.Client; + message: string; + } + export type ConnectBegin = { + address: base.ServerAddress; + client_nickname: string; + } + export type ErrorCustom = { + message: string; } - export class ServerLog { - private readonly handle: ConnectionHandler; - private history_length: number = 100; + export type ReconnectScheduled = { + timeout: number; + } - private _log: server.LogMessage[] = []; - private _html_tag: JQuery; - private _log_container: JQuery; - private auto_follow: boolean; /* automatic scroll to bottom */ - private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ + export type ReconnectCanceled = { } + export type ReconnectExecute = { } - constructor(handle: ConnectionHandler) { - this.handle = handle; - this.auto_follow = true; + export type ErrorPermission = { + permission: PermissionInfo; + } - this._html_tag = $.spawn("div").addClass("container-log"); - this._log_container = $.spawn("div").addClass("container-messages"); - this._log_container.appendTo(this._html_tag); + export type WelcomeMessage = { + message: string; + } - this._html_tag.on('scroll', event => { - if(Date.now() - this._ignore_event < 100) { - this._ignore_event = 0; - return; - } + export type HostMessageDisconnect = { + message: string; + } - this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; - }); - } + export type ClientMove = { + channel_from?: base.Channel; + channel_from_own: boolean; - log(type: T, data: server.TypeInfo[T]) { - const event = { - data: data, - timestamp: Date.now(), - type: type as any - }; + channel_to?: base.Channel; + channel_to_own: boolean; - this._log.push(event); - while(this._log.length > this.history_length) - this._log.pop_front(); + client: base.Client; + client_own: boolean; - this.append_log(event); - } + invoker?: base.Client; - html_tag() : JQuery { - return this._html_tag; - } + message?: string; + reason: ViewReasonId; + } - destroy() { - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._log_container = undefined; + export type ClientEnter = { + channel_from?: base.Channel; + channel_to?: base.Channel; - this._log = undefined; - } + client: base.Client; + invoker?: base.Client; - private _scroll_task: number; + message?: string; + own_channel: boolean; - private append_log(message: server.LogMessage) { - let container = $.spawn("div").addClass("log-message"); + reason: ViewReasonId; + ban_time?: number; + } - /* build timestamp */ - { - const num = number => ('00' + number).substr(-2); - const date = new Date(message.timestamp); - $.spawn("div") - .addClass("timestamp") - .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") - .appendTo(container); + export type ClientLeave = { + channel_from?: base.Channel; + channel_to?: base.Channel; + + client: base.Client; + invoker?: base.Client; + + message?: string; + own_channel: boolean; + + reason: ViewReasonId; + ban_time?: number; + } + + export type ChannelCreate = { + creator: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ChannelDelete = { + deleter: base.Client; + channel: base.Channel; + + own_action: boolean; + } + + export type ConnectionConnected = { + own_client: base.Client; + } + export type ConnectionFailed = {}; + export type ConnectionLogin = {} + export type ConnectionHostnameResolve = {}; + export type ConnectionHostnameResolved = { + address: base.ServerAddress; + } + export type ConnectionHostnameResolveError = { + message: string; + } + + export type ConnectionVoiceSetupFailed = { + reason: string; + reconnect_delay: number; /* if less or equal to 0 reconnect is prohibited */ + } + + export type ConnectionCommandError = { + error: any; + } + + export type ClientNicknameChanged = { + own_client: boolean; + + client: base.Client; + + old_name: string; + new_name: string; + } + + export type ClientNicknameChangeFailed = { + reason: string; + } + + export type ServerClosed = { + message: string; + } + + export type ServerRequiresPassword = {} + + export type ServerBanned = { + message: string; + time: number; + + invoker: base.Client; + } +} + +export type LogMessage = { + type: Type; + timestamp: number; + data: any; +} + +export interface TypeInfo { + "connection_begin" : event.ConnectBegin; + "global_message": event.GlobalMessage; + + "error_custom": event.ErrorCustom; + "error_permission": event.ErrorPermission; + + "connection_hostname_resolved": event.ConnectionHostnameResolved; + "connection_hostname_resolve": event.ConnectionHostnameResolve; + "connection_hostname_resolve_error": event.ConnectionHostnameResolveError; + "connection_failed": event.ConnectionFailed; + "connection_login": event.ConnectionLogin; + "connection_connected": event.ConnectionConnected; + "connection_voice_setup_failed": event.ConnectionVoiceSetupFailed; + "connection_command_error": event.ConnectionCommandError; + + "reconnect_scheduled": event.ReconnectScheduled; + "reconnect_canceled": event.ReconnectCanceled; + "reconnect_execute": event.ReconnectExecute; + + "server_welcome_message": event.WelcomeMessage; + "server_host_message": event.WelcomeMessage; + "server_host_message_disconnect": event.HostMessageDisconnect; + + "server_closed": event.ServerClosed; + "server_requires_password": event.ServerRequiresPassword; + "server_banned": event.ServerBanned; + + "client_view_enter": event.ClientEnter; + "client_view_move": event.ClientMove; + "client_view_leave": event.ClientLeave; + + "client_nickname_change_failed": event.ClientNicknameChangeFailed, + "client_nickname_changed": event.ClientNicknameChanged, + + "channel_create": event.ChannelCreate; + "channel_delete": event.ChannelDelete; + + "disconnected": any; +} + +export type MessageBuilderOptions = {}; +export type MessageBuilder = (data: TypeInfo[T], options: MessageBuilderOptions) => JQuery[] | undefined; + +export const MessageBuilders: {[key: string]: MessageBuilder} = { + "error_custom": (data: event.ErrorCustom, options) => { + return [$.spawn("div").addClass("log-error").text(data.message)] + } +}; + +export class ServerLog { + private readonly handle: ConnectionHandler; + private history_length: number = 100; + + private _log: LogMessage[] = []; + private _html_tag: JQuery; + private _log_container: JQuery; + private auto_follow: boolean; /* automatic scroll to bottom */ + private _ignore_event: number; /* after auto scroll we've triggered the scroll event. We want to prevent this so we capture the next event */ + + constructor(handle: ConnectionHandler) { + this.handle = handle; + this.auto_follow = true; + + this._html_tag = $.spawn("div").addClass("container-log"); + this._log_container = $.spawn("div").addClass("container-messages"); + this._log_container.appendTo(this._html_tag); + + this._html_tag.on('scroll', event => { + if(Date.now() - this._ignore_event < 100) { + this._ignore_event = 0; + return; } - /* build message data */ - { - const builder = server.MessageBuilders[message.type]; - if(!builder) { - MessageHelper.formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); - } else { - const elements = builder(message.data, {}); - if(!elements || elements.length == 0) - return; /* discard message */ - container.append(...elements); - } + this.auto_follow = (this._html_tag[0].scrollTop + this._html_tag[0].clientHeight + this._html_tag[0].clientHeight * .125) > this._html_tag[0].scrollHeight; + }); + } + + log(type: T, data: TypeInfo[T]) { + const event = { + data: data, + timestamp: Date.now(), + type: type as any + }; + + this._log.push(event); + while(this._log.length > this.history_length) + this._log.pop_front(); + + this.append_log(event); + } + + html_tag() : JQuery { + return this._html_tag; + } + + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._log_container = undefined; + + this._log = undefined; + } + + private _scroll_task: number; + + private append_log(message: LogMessage) { + let container = $.spawn("div").addClass("log-message"); + + /* build timestamp */ + { + const num = number => ('00' + number).substr(-2); + const date = new Date(message.timestamp); + $.spawn("div") + .addClass("timestamp") + .text("<" + num(date.getHours()) + ":" + num(date.getMinutes()) + ":" + num(date.getSeconds()) + ">") + .appendTo(container); + } + + /* build message data */ + { + const builder = MessageBuilders[message.type]; + if(!builder) { + formatMessage(tr("missing log message builder {0}!"), message.type).forEach(e => e.addClass("log-error").appendTo(container)); + } else { + const elements = builder(message.data, {}); + if(!elements || elements.length == 0) + return; /* discard message */ + container.append(...elements); } - this._ignore_event = Date.now(); - this._log_container.append(container); + } + this._ignore_event = Date.now(); + this._log_container.append(container); - /* max history messages! */ - const messages = this._log_container.children(); - let index = 0; - while(messages.length - index > this.history_length) - index++; - const hide_elements = messages.filter(idx => idx < index); - hide_elements.hide(250, () => hide_elements.remove()); + /* max history messages! */ + const messages = this._log_container.children(); + let index = 0; + while(messages.length - index > this.history_length) + index++; + const hide_elements = messages.filter(idx => idx < index); + hide_elements.hide(250, () => hide_elements.remove()); - if(this.auto_follow) { - clearTimeout(this._scroll_task); + if(this.auto_follow) { + clearTimeout(this._scroll_task); - /* do not enforce a recalculate style here */ - this._scroll_task = setTimeout(() => { - this._html_tag.scrollTop(this._html_tag[0].scrollHeight); - this._scroll_task = 0; - }, 5) as any; - } + /* do not enforce a recalculate style here */ + this._scroll_task = setTimeout(() => { + this._html_tag.scrollTop(this._html_tag[0].scrollHeight); + this._scroll_task = 0; + }, 5) as any; } } } /* impl of the parsers */ -namespace log { - export namespace server { - namespace impl { - const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ - client_unique_id: client.client_unique_id, - client_id: client.client_id, - client_name: client.client_name, - add_braces: braces - }); - const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ - channel_display_name: channel.channel_name, - channel_name: channel.channel_name, - channel_id: channel.channel_id, - add_braces: braces - }); +const client_tag = (client: base.Client, braces?: boolean) => htmltags.generate_client_object({ + client_unique_id: client.client_unique_id, + client_id: client.client_id, + client_name: client.client_name, + add_braces: braces +}); +const channel_tag = (channel: base.Channel, braces?: boolean) => htmltags.generate_channel_object({ + channel_display_name: channel.channel_name, + channel_name: channel.channel_name, + channel_id: channel.channel_id, + add_braces: braces +}); - MessageBuilders["connection_begin"] = (data: event.ConnectBegin, options) => { - return MessageHelper.formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); - }; +MessageBuilders["connection_begin"] = (data: event.ConnectBegin, options) => { + return formatMessage(tr("Connecting to {0}{1}"), data.address.server_hostname, data.address.server_port == 9987 ? "" : (":" + data.address.server_port)); +}; - MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => MessageHelper.formatMessage(tr("Resolving hostname")); - MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => MessageHelper.formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); - MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => MessageHelper.formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); +MessageBuilders["connection_hostname_resolve"] = (data: event.ConnectionHostnameResolve, options) => formatMessage(tr("Resolving hostname")); +MessageBuilders["connection_hostname_resolved"] = (data: event.ConnectionHostnameResolved, options) => formatMessage(tr("Hostname resolved successfully to {0}:{1}"), data.address.server_hostname, data.address.server_port); +MessageBuilders["connection_hostname_resolve_error"] = (data: event.ConnectionHostnameResolveError, options) => formatMessage(tr("Failed to resolve hostname. Connecting to given hostname. Error: {0}"), data.message); - MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => MessageHelper.formatMessage(tr("Logging in...")); - MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => MessageHelper.formatMessage(tr("Connect failed.")); - MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => MessageHelper.formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); +MessageBuilders["connection_login"] = (data: event.ConnectionLogin, options) => formatMessage(tr("Logging in...")); +MessageBuilders["connection_failed"] = (data: event.ConnectionFailed, options) => formatMessage(tr("Connect failed.")); +MessageBuilders["connection_connected"] = (data: event.ConnectionConnected, options) => formatMessage(tr("Connected as {0}"), client_tag(data.own_client, true)); - MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { - return MessageHelper.formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); - }; +MessageBuilders["connection_voice_setup_failed"] = (data: event.ConnectionVoiceSetupFailed, options) => { + return formatMessage(tr("Failed to setup voice bridge: {0}. Allow reconnect: {1}"), data.reason, data.reconnect_delay > 0 ? tr("yes") : tr("no")); +}; - MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { - return MessageHelper.formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); - }; +MessageBuilders["error_permission"] = (data: event.ErrorPermission, options) => { + return formatMessage(tr("Insufficient client permissions. Failed on permission {0}"), data.permission ? data.permission.name : "unknown").map(e => e.addClass("log-error")); +}; - MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { - if(data.reason == ViewReasonId.VREASON_SYSTEM) { - return undefined; - } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - /* client appeared */ - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); - } - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), - client_tag(data.client), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - if(data.channel_from) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), - client_tag(data.client), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } - } - return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; - }; - - MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { - if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker) - ); - } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to) - ); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), - client_tag(data.client), - channel_tag(data.channel_from), - channel_tag(data.channel_to), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } - return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; - }; - - MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { - if(data.reason == ViewReasonId.VREASON_USER_ACTION) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); - } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { - return MessageHelper.formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { - return MessageHelper.formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), - client_tag(data.client), - channel_tag(data.channel_from), - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else if(data.reason == ViewReasonId.VREASON_BAN) { - let duration = "permanently"; - if(data.ban_time) - duration = "for " + formatDate(data.ban_time); - return MessageHelper.formatMessage(tr("{0} was banned {1} by {2}.{3}"), - client_tag(data.client), - duration, - client_tag(data.invoker), - data.message ? (" (" + data.message + ")") : "" - ); - } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { - return MessageHelper.formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); - } else if(data.reason == ViewReasonId.VREASON_MOVED) { - return MessageHelper.formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); - } - - return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; - }; - - MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; - - MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { - return MessageHelper.bbcode_chat("[color=green]" + data.message + "[/color]"); - }; - - MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { - if(data.own_client) { - return MessageHelper.formatMessage(tr("Nickname successfully changed.")); - } else { - return MessageHelper.formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); - } - }; - - MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { - return []; /* we do not show global messages within log */ - }; - - MessageBuilders["disconnected"] = () => MessageHelper.formatMessage(tr("Disconnected from server")); - - MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { - return tra("Reconnecting in {0}.", MessageHelper.format_time(data.timeout, tr("now"))) - }; - - MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { - return tra("Canceled reconnect.") - }; - - MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { - return tra("Reconnecting...") - }; - - MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { - let result: JQuery[]; - - const time = data.time == 0 ? tr("ever") : MessageHelper.format_time(data.time * 1000, tr("one second")); - if(data.invoker.client_id > 0) { - if(data.message) - result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); - else - result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); - } else { - if(data.message) - result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); - else - result = tra("You've been banned from the server for {0}.", time); - } - - return result.map(e => e.addClass("log-error")); - }; +MessageBuilders["client_view_enter"] = (data: event.ClientEnter, options) => { + if(data.reason == ViewReasonId.VREASON_SYSTEM) { + return undefined; + } if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + /* client appeared */ + if(data.channel_from) { + return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your {2}") : tr("{0} appeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else { + return formatMessage(data.own_channel ? tr("{0} connected to your channel {1}") : tr("{0} connected to channel {1}"), client_tag(data.client), channel_tag(data.channel_to)); + } + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + if(data.channel_from) { + return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, moved by {3}") : tr("{0} appeared from {1} to {2}, moved by {3}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } else { + return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, moved by {2}") : tr("{0} appeared to {1}, moved by {2}"), + client_tag(data.client), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + if(data.channel_from) { + return formatMessage(data.own_channel ? tr("{0} appeared from {1} to your channel {2}, kicked by {3}{4}") : tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else { + return formatMessage(data.own_channel ? tr("{0} appeared to your channel {1}, kicked by {2}{3}") : tr("{0} appeared to {1}, kicked by {2}{3}"), + client_tag(data.client), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); } } -} \ No newline at end of file + return [$.spawn("div").addClass("log-error").text("Invalid view enter reason id (" + data.message + ")")]; +}; + +MessageBuilders["client_view_move"] = (data: event.ClientMove, options) => { + if(data.reason == ViewReasonId.VREASON_MOVED) { + return formatMessage(data.client_own ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker) + ); + } else if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return formatMessage(data.client_own ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to) + ); + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return formatMessage(data.client_own ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), + client_tag(data.client), + channel_tag(data.channel_from), + channel_tag(data.channel_to), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } + return [$.spawn("div").addClass("log-error").text("Invalid view move reason id (" + data.reason + ")")]; +}; + +MessageBuilders["client_view_leave"] = (data: event.ClientLeave, options) => { + if(data.reason == ViewReasonId.VREASON_USER_ACTION) { + return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}") : tr("{0} disappeared from {1} to {2}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to)); + } else if(data.reason == ViewReasonId.VREASON_SERVER_LEFT) { + return formatMessage(tr("{0} left the server{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_SERVER_KICK) { + return formatMessage(tr("{0} was kicked from the server by {1}.{2}"), client_tag(data.client), client_tag(data.invoker), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_CHANNEL_KICK) { + return formatMessage(data.own_channel ? tr("{0} was kicked from your channel by {2}.{3}") : tr("{0} was kicked from channel {1} by {2}.{3}"), + client_tag(data.client), + channel_tag(data.channel_from), + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else if(data.reason == ViewReasonId.VREASON_BAN) { + let duration = "permanently"; + if(data.ban_time) + duration = "for " + formatDate(data.ban_time); + return formatMessage(tr("{0} was banned {1} by {2}.{3}"), + client_tag(data.client), + duration, + client_tag(data.invoker), + data.message ? (" (" + data.message + ")") : "" + ); + } else if(data.reason == ViewReasonId.VREASON_TIMEOUT) { + return formatMessage(tr("{0} timed out{1}"), client_tag(data.client), data.message ? (" (" + data.message + ")") : ""); + } else if(data.reason == ViewReasonId.VREASON_MOVED) { + return formatMessage(data.own_channel ? tr("{0} disappeared from your channel {1} to {2}, moved by {3}") : tr("{0} disappeared from {1} to {2}, moved by {3}"), client_tag(data.client), channel_tag(data.channel_from), channel_tag(data.channel_to), client_tag(data.invoker)); + } + + return [$.spawn("div").addClass("log-error").text("Invalid view leave reason id (" + data.reason + ")")]; +}; + +MessageBuilders["server_welcome_message"] = (data: event.WelcomeMessage, options) => { + return bbcode_chat("[color=green]" + data.message + "[/color]"); +}; + +MessageBuilders["server_host_message"] = (data: event.WelcomeMessage, options) => { + return bbcode_chat("[color=green]" + data.message + "[/color]"); +}; + +MessageBuilders["client_nickname_changed"] = (data: event.ClientNicknameChanged, options) => { + if(data.own_client) { + return formatMessage(tr("Nickname successfully changed.")); + } else { + return formatMessage(tr("{0} changed his nickname from \"{1}\" to \"{2}\""), client_tag(data.client), data.old_name, data.new_name); + } +}; + +MessageBuilders["global_message"] = (data: event.GlobalMessage, options) => { + return []; /* we do not show global messages within log */ +}; + +MessageBuilders["disconnected"] = () => formatMessage(tr("Disconnected from server")); + +MessageBuilders["reconnect_scheduled"] = (data: event.ReconnectScheduled, options) => { + return tra("Reconnecting in {0}.", format_time(data.timeout, tr("now"))) +}; + +MessageBuilders["reconnect_canceled"] = (data: event.ReconnectCanceled, options) => { + return tra("Canceled reconnect.") +}; + +MessageBuilders["reconnect_execute"] = (data: event.ReconnectExecute, options) => { + return tra("Reconnecting...") +}; + +MessageBuilders["server_banned"] = (data: event.ServerBanned, options) => { + let result: JQuery[]; + + const time = data.time == 0 ? tr("ever") : format_time(data.time * 1000, tr("one second")); + if(data.invoker.client_id > 0) { + if(data.message) + result = tra("You've been banned from the server by {0} for {1}. Reason: {2}", client_tag(data.invoker), time, data.message); + else + result = tra("You've been banned from the server by {0} for {1}.", client_tag(data.invoker), time); + } else { + if(data.message) + result = tra("You've been banned from the server for {0}. Reason: {1}", time, data.message); + else + result = tra("You've been banned from the server for {0}.", time); + } + + return result.map(e => e.addClass("log-error")); +}; \ No newline at end of file diff --git a/shared/js/ui/frames/side/chat_box.ts b/shared/js/ui/frames/side/chat_box.ts index 863f9335..b8f2cc7d 100644 --- a/shared/js/ui/frames/side/chat_box.ts +++ b/shared/js/ui/frames/side/chat_box.ts @@ -1,267 +1,268 @@ -namespace chat { - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +import {Settings, settings} from "tc-shared/settings"; +import {helpers} from "tc-shared/ui/frames/side/chat_helper"; - export class ChatBox { - private _html_tag: JQuery; - private _html_input: JQuery; - private _enabled: boolean; - private __callback_text_changed; - private __callback_key_down; - private __callback_key_up; - private __callback_paste; +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - private _typing_timeout: number; /* ID when the next callback_typing will be called */ - private _typing_last_event: number; /* timestamp of the last typing event */ +export class ChatBox { + private _html_tag: JQuery; + private _html_input: JQuery; + private _enabled: boolean; + private __callback_text_changed; + private __callback_key_down; + private __callback_key_up; + private __callback_paste; - private _message_history: string[] = []; - private _message_history_length = 100; - private _message_history_index = 0; + private _typing_timeout: number; /* ID when the next callback_typing will be called */ + private _typing_last_event: number; /* timestamp of the last typing event */ - typing_interval: number = 2000; /* update frequency */ - callback_typing: () => any; - callback_text: (text: string) => any; + private _message_history: string[] = []; + private _message_history_length = 100; + private _message_history_index = 0; - constructor() { - this._enabled = true; - this.__callback_key_up = this._callback_key_up.bind(this); - this.__callback_key_down = this._callback_key_down.bind(this); - this.__callback_text_changed = this._callback_text_changed.bind(this); - this.__callback_paste = event => this._callback_paste(event); + typing_interval: number = 2000; /* update frequency */ + callback_typing: () => any; + callback_text: (text: string) => any; - this._build_html_tag(); - this._initialize_listener(); - } + constructor() { + this._enabled = true; + this.__callback_key_up = this._callback_key_up.bind(this); + this.__callback_key_down = this._callback_key_down.bind(this); + this.__callback_text_changed = this._callback_text_changed.bind(this); + this.__callback_paste = event => this._callback_paste(event); - html_tag() : JQuery { - return this._html_tag; - } + this._build_html_tag(); + this._initialize_listener(); + } - destroy() { - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._html_input = undefined; + html_tag() : JQuery { + return this._html_tag; + } - clearTimeout(this._typing_timeout); + destroy() { + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._html_input = undefined; - this.__callback_text_changed = undefined; - this.__callback_key_down = undefined; - this.__callback_paste = undefined; + clearTimeout(this._typing_timeout); - this.callback_text = undefined; - this.callback_typing = undefined; - } + this.__callback_text_changed = undefined; + this.__callback_key_down = undefined; + this.__callback_paste = undefined; - private _initialize_listener() { - this._html_input.on("cut paste drop keydown keyup", (event) => this.__callback_text_changed(event)); - this._html_input.on("change", this.__callback_text_changed); - this._html_input.on("keydown", this.__callback_key_down); - this._html_input.on("keyup", this.__callback_key_up); - this._html_input.on("paste", this.__callback_paste); - } + this.callback_text = undefined; + this.callback_typing = undefined; + } - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_chatbox").renderTag({ - emojy_support: settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES) - }); - this._html_input = this._html_tag.find(".textarea") as any; + private _initialize_listener() { + this._html_input.on("cut paste drop keydown keyup", (event) => this.__callback_text_changed(event)); + this._html_input.on("change", this.__callback_text_changed); + this._html_input.on("keydown", this.__callback_key_down); + this._html_input.on("keyup", this.__callback_key_up); + this._html_input.on("paste", this.__callback_paste); + } - const tag: JQuery & { lsxEmojiPicker(args: any); } = this._html_tag.find('.button-emoji') as any; - tag.lsxEmojiPicker({ - width: 300, - height: 400, - twemoji: typeof(window.twemoji) !== "undefined", - onSelect: emoji => this._html_input.html(this._html_input.html() + emoji.value), - closeOnSelect: false - }); - } + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_chatbox").renderTag({ + emojy_support: settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES) + }); + this._html_input = this._html_tag.find(".textarea") as any; - private _callback_text_changed(event: Event) { - if(event && event.defaultPrevented) + const tag: JQuery & { lsxEmojiPicker(args: any); } = this._html_tag.find('.button-emoji') as any; + tag.lsxEmojiPicker({ + width: 300, + height: 400, + twemoji: typeof(window.twemoji) !== "undefined", + onSelect: emoji => this._html_input.html(this._html_input.html() + emoji.value), + closeOnSelect: false + }); + } + + private _callback_text_changed(event: Event) { + if(event && event.defaultPrevented) + return; + + /* Auto resize */ + const text = this._html_input[0]; + text.style.height = "1em"; + text.style.height = text.scrollHeight + 'px'; + + if(!event || (event.type !== "keydown" && event.type !== "keyup" && event.type !== "change")) + return; + + this._typing_last_event = Date.now(); + if(this._typing_timeout) + return; + + const _trigger_typing = (last_time: number) => { + if(this._typing_last_event <= last_time) { + this._typing_timeout = 0; return; - - /* Auto resize */ - const text = this._html_input[0]; - text.style.height = "1em"; - text.style.height = text.scrollHeight + 'px'; - - if(!event || (event.type !== "keydown" && event.type !== "keyup" && event.type !== "change")) - return; - - this._typing_last_event = Date.now(); - if(this._typing_timeout) - return; - - const _trigger_typing = (last_time: number) => { - if(this._typing_last_event <= last_time) { - this._typing_timeout = 0; - return; - } - - try { - if(this.callback_typing) - this.callback_typing(); - } finally { - this._typing_timeout = setTimeout(_trigger_typing, this.typing_interval, this._typing_last_event); - } - }; - _trigger_typing(0); /* We def want that*/ - } - - private _text(element: HTMLElement) { - if(typeof(element) !== "object") - return element; - - if(element instanceof HTMLImageElement) - return element.alt || element.title; - if(element instanceof HTMLBRElement) { - return '\n'; } - if(element.childNodes.length > 0) - return [...element.childNodes].map(e => this._text(e as HTMLElement)).join(""); + try { + if(this.callback_typing) + this.callback_typing(); + } finally { + this._typing_timeout = setTimeout(_trigger_typing, this.typing_interval, this._typing_last_event); + } + }; + _trigger_typing(0); /* We def want that*/ + } - if(element.nodeType == Node.TEXT_NODE) - return element.textContent; - return typeof(element.innerText) === "string" ? element.innerText : ""; + private _text(element: HTMLElement) { + if(typeof(element) !== "object") + return element; + + if(element instanceof HTMLImageElement) + return element.alt || element.title; + if(element instanceof HTMLBRElement) { + return '\n'; } - private htmlEscape(message: string) : string { - const div = document.createElement('div'); - div.innerText = message; - message = div.innerHTML; - return message.replace(/ /g, ' '); - } - private _callback_paste(event: ClipboardEvent) { - const _event = (event).originalEvent as ClipboardEvent || event; - const clipboard = _event.clipboardData || (window).clipboardData; - if(!clipboard) return; + if(element.childNodes.length > 0) + return [...element.childNodes].map(e => this._text(e as HTMLElement)).join(""); + + if(element.nodeType == Node.TEXT_NODE) + return element.textContent; + return typeof(element.innerText) === "string" ? element.innerText : ""; + } + + private htmlEscape(message: string) : string { + const div = document.createElement('div'); + div.innerText = message; + message = div.innerHTML; + return message.replace(/ /g, ' '); + } + private _callback_paste(event: ClipboardEvent) { + const _event = (event).originalEvent as ClipboardEvent || event; + const clipboard = _event.clipboardData || (window).clipboardData; + if(!clipboard) return; - const raw_text = clipboard.getData('text/plain'); - const selection = window.getSelection(); - if (!selection.rangeCount) - return false; + const raw_text = clipboard.getData('text/plain'); + const selection = window.getSelection(); + if (!selection.rangeCount) + return false; - let html_xml = clipboard.getData('text/html'); - if(!html_xml) - html_xml = $.spawn("div").text(raw_text).html(); + let html_xml = clipboard.getData('text/html'); + if(!html_xml) + html_xml = $.spawn("div").text(raw_text).html(); - const parser = new DOMParser(); - const nodes = parser.parseFromString(html_xml, "text/html"); + const parser = new DOMParser(); + const nodes = parser.parseFromString(html_xml, "text/html"); - let data = this._text(nodes.body); + let data = this._text(nodes.body); - /* fix prefix & suffix new lines */ + /* fix prefix & suffix new lines */ + { + let prefix_length = 0, suffix_length = 0; { - let prefix_length = 0, suffix_length = 0; - { - for(let i = 0; i < raw_text.length; i++) - if(raw_text.charAt(i) === '\n') - prefix_length++; - else if(raw_text.charAt(i) !== '\r') - break; - for(let i = raw_text.length - 1; i >= 0; i++) - if(raw_text.charAt(i) === '\n') - suffix_length++; - else if(raw_text.charAt(i) !== '\r') - break; - } - - data = data.replace(/^[\n\r]+|[\n\r]+$/g, ''); - data = "\n".repeat(prefix_length) + data + "\n".repeat(suffix_length); + for(let i = 0; i < raw_text.length; i++) + if(raw_text.charAt(i) === '\n') + prefix_length++; + else if(raw_text.charAt(i) !== '\r') + break; + for(let i = raw_text.length - 1; i >= 0; i++) + if(raw_text.charAt(i) === '\n') + suffix_length++; + else if(raw_text.charAt(i) !== '\r') + break; } + + data = data.replace(/^[\n\r]+|[\n\r]+$/g, ''); + data = "\n".repeat(prefix_length) + data + "\n".repeat(suffix_length); + } + event.preventDefault(); + + selection.deleteFromDocument(); + document.execCommand('insertHTML', false, this.htmlEscape(data)); + } + + private test_message(message: string) : boolean { + message = message + .replace(/ /gi, "") + .replace(/
/gi, "") + .replace(/\n/gi, "") + .replace(//gi, ""); + return message.length > 0; + } + + private _callback_key_down(event: KeyboardEvent) { + if(event.key.toLowerCase() === "enter" && !event.shiftKey) { event.preventDefault(); - selection.deleteFromDocument(); - document.execCommand('insertHTML', false, this.htmlEscape(data)); - } - - private test_message(message: string) : boolean { - message = message - .replace(/ /gi, "") - .replace(/
/gi, "") - .replace(/\n/gi, "") - .replace(//gi, ""); - return message.length > 0; - } - - private _callback_key_down(event: KeyboardEvent) { - if(event.key.toLowerCase() === "enter" && !event.shiftKey) { - event.preventDefault(); - - /* deactivate chatbox when no callback? */ - let text = this._html_input[0].innerText as string; - if(!this.test_message(text)) - return; - - this._message_history.push(text); - this._message_history_index = this._message_history.length; - if(this._message_history.length > this._message_history_length) - this._message_history = this._message_history.slice(this._message_history.length - this._message_history_length); - - if(this.callback_text) { - this.callback_text(helpers.preprocess_chat_message(text)); - } - - if(this._typing_timeout) - clearTimeout(this._typing_timeout); - this._typing_timeout = 1; /* enforce no typing update while sending */ - this._html_input.text(""); - setTimeout(() => { - this.__callback_text_changed(); - this._typing_timeout = 0; /* enable text change listener again */ - }); - } else if(event.key.toLowerCase() === "arrowdown") { - //TODO: Test for at the last line within the box - if(this._message_history_index < 0) return; - if(this._message_history_index >= this._message_history.length) return; /* OOB, even with the empty message */ - - this._message_history_index++; - this._html_input[0].innerText = this._message_history[this._message_history_index] || ""; /* OOB just returns "undefined" */ - } else if(event.key.toLowerCase() === "arrowup") { - //TODO: Test for at the first line within the box - if(this._message_history_index <= 0) return; /* we cant go "down" */ - this._message_history_index--; - this._html_input[0].innerText = this._message_history[this._message_history_index]; - } else { - if(this._message_history_index >= 0) { - if(this._message_history_index >= this._message_history.length) { - if("" !== this._html_input[0].innerText) - this._message_history_index = -1; - } else if(this._message_history[this._message_history_index] !== this._html_input[0].innerText) - this._message_history_index = -1; - } - } - } - - private _callback_key_up(event: KeyboardEvent) { - if("" === this._html_input[0].innerText) - this._message_history_index = this._message_history.length; - } - - private _context_task: number; - set_enabled(flag: boolean) { - if(this._enabled === flag) + /* deactivate chatbox when no callback? */ + let text = this._html_input[0].innerText as string; + if(!this.test_message(text)) return; - if(!this._context_task) { - this._enabled = flag; - /* Allow the browser to asynchronously recalculate everything */ - this._context_task = setTimeout(() => { - this._context_task = undefined; - this._html_input.each((_, e) => { e.contentEditable = this._enabled ? "true" : "false"; }); - }); - this._html_tag.find('.button-emoji').toggleClass("disabled", !flag); + this._message_history.push(text); + this._message_history_index = this._message_history.length; + if(this._message_history.length > this._message_history_length) + this._message_history = this._message_history.slice(this._message_history.length - this._message_history_length); + + if(this.callback_text) { + this.callback_text(helpers.preprocess_chat_message(text)); + } + + if(this._typing_timeout) + clearTimeout(this._typing_timeout); + this._typing_timeout = 1; /* enforce no typing update while sending */ + this._html_input.text(""); + setTimeout(() => { + this.__callback_text_changed(); + this._typing_timeout = 0; /* enable text change listener again */ + }); + } else if(event.key.toLowerCase() === "arrowdown") { + //TODO: Test for at the last line within the box + if(this._message_history_index < 0) return; + if(this._message_history_index >= this._message_history.length) return; /* OOB, even with the empty message */ + + this._message_history_index++; + this._html_input[0].innerText = this._message_history[this._message_history_index] || ""; /* OOB just returns "undefined" */ + } else if(event.key.toLowerCase() === "arrowup") { + //TODO: Test for at the first line within the box + if(this._message_history_index <= 0) return; /* we cant go "down" */ + this._message_history_index--; + this._html_input[0].innerText = this._message_history[this._message_history_index]; + } else { + if(this._message_history_index >= 0) { + if(this._message_history_index >= this._message_history.length) { + if("" !== this._html_input[0].innerText) + this._message_history_index = -1; + } else if(this._message_history[this._message_history_index] !== this._html_input[0].innerText) + this._message_history_index = -1; } } + } - is_enabled() { - return this._enabled; - } + private _callback_key_up(event: KeyboardEvent) { + if("" === this._html_input[0].innerText) + this._message_history_index = this._message_history.length; + } - focus_input() { - this._html_input.focus(); + private _context_task: number; + set_enabled(flag: boolean) { + if(this._enabled === flag) + return; + + if(!this._context_task) { + this._enabled = flag; + /* Allow the browser to asynchronously recalculate everything */ + this._context_task = setTimeout(() => { + this._context_task = undefined; + this._html_input.each((_, e) => { e.contentEditable = this._enabled ? "true" : "false"; }); + }); + this._html_tag.find('.button-emoji').toggleClass("disabled", !flag); } } + + is_enabled() { + return this._enabled; + } + + focus_input() { + this._html_input.focus(); + } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/chat_helper.ts b/shared/js/ui/frames/side/chat_helper.ts index c3e7396f..1522741c 100644 --- a/shared/js/ui/frames/side/chat_helper.ts +++ b/shared/js/ui/frames/side/chat_helper.ts @@ -1,423 +1,426 @@ -namespace chat { - export namespace helpers { - //https://regex101.com/r/YQbfcX/2 - //static readonly URL_REGEX = /^(?([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?(?:[^\s?]+)?)(?:\?(?\S+))?)?$/gm; - const URL_REGEX = /^(([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/((?:[^\s?]+)?)(?:\?(\S+))?)?$/gm; - function process_urls(message: string) : string { - const words = message.split(/[ \n]/); - for(let index = 0; index < words.length; index++) { - const flag_escaped = words[index].startsWith('!'); - const unescaped = flag_escaped ? words[index].substr(1) : words[index]; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {Settings, settings} from "tc-shared/settings"; - _try: - try { - const url = new URL(unescaped); - log.debug(LogCategory.GENERAL, tr("Chat message contains URL: %o"), url); - if(url.protocol !== 'http:' && url.protocol !== 'https:') - break _try; - if(flag_escaped) { - message = undefined; - words[index] = unescaped; - } else { - message = undefined; - words[index] = "[url=" + url.toString() + "]" + url.toString() + "[/url]"; - } - } catch(e) { /* word isn't an url */ } +declare const xbbcode; +export namespace helpers { + //https://regex101.com/r/YQbfcX/2 + //static readonly URL_REGEX = /^(?([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/(?(?:[^\s?]+)?)(?:\?(?\S+))?)?$/gm; + const URL_REGEX = /^(([a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]{2,63})(?:\/((?:[^\s?]+)?)(?:\?(\S+))?)?$/gm; + function process_urls(message: string) : string { + const words = message.split(/[ \n]/); + for(let index = 0; index < words.length; index++) { + const flag_escaped = words[index].startsWith('!'); + const unescaped = flag_escaped ? words[index].substr(1) : words[index]; - if(unescaped.match(URL_REGEX)) { + _try: + try { + const url = new URL(unescaped); + log.debug(LogCategory.GENERAL, tr("Chat message contains URL: %o"), url); + if(url.protocol !== 'http:' && url.protocol !== 'https:') + break _try; if(flag_escaped) { message = undefined; words[index] = unescaped; } else { message = undefined; - words[index] = "[url=" + unescaped + "]" + unescaped + "[/url]"; + words[index] = "[url=" + url.toString() + "]" + url.toString() + "[/url]"; } + } catch(e) { /* word isn't an url */ } + + if(unescaped.match(URL_REGEX)) { + if(flag_escaped) { + message = undefined; + words[index] = unescaped; + } else { + message = undefined; + words[index] = "[url=" + unescaped + "]" + unescaped + "[/url]"; } } - - return message || words.join(" "); } - namespace md2bbc { - export type RemarkToken = { - type: string; - tight: boolean; - lines: number[]; - level: number; + return message || words.join(" "); + } - /* img */ - alt?: string; - src?: string; + namespace md2bbc { + export type RemarkToken = { + type: string; + tight: boolean; + lines: number[]; + level: number; - /* link */ - href?: string; + /* img */ + alt?: string; + src?: string; - /* table */ - align?: string; + /* link */ + href?: string; - /* code */ - params?: string; + /* table */ + align?: string; - content?: string; - hLevel?: number; - children?: RemarkToken[]; - } + /* code */ + params?: string; - export class Renderer { - private static renderers = { - "text": (renderer: Renderer, token: RemarkToken) => renderer.options().process_url ? process_urls(renderer.maybe_escape_bb(token.content)) : renderer.maybe_escape_bb(token.content), - "softbreak": () => "\n", - "hardbreak": () => "\n", + content?: string; + hLevel?: number; + children?: RemarkToken[]; + } - "paragraph_open": (renderer: Renderer, token: RemarkToken) => { - const last_line = !renderer.last_paragraph || !renderer.last_paragraph.lines ? 0 : renderer.last_paragraph.lines[1]; - const lines = token.lines[0] - last_line; - return [...new Array(lines)].map(e => "[br]").join(""); - }, - "paragraph_close": () => "", + export class Renderer { + private static renderers = { + "text": (renderer: Renderer, token: RemarkToken) => renderer.options().process_url ? process_urls(renderer.maybe_escape_bb(token.content)) : renderer.maybe_escape_bb(token.content), + "softbreak": () => "\n", + "hardbreak": () => "\n", - "strong_open": (renderer: Renderer, token: RemarkToken) => "[b]", - "strong_close": (renderer: Renderer, token: RemarkToken) => "[/b]", + "paragraph_open": (renderer: Renderer, token: RemarkToken) => { + const last_line = !renderer.last_paragraph || !renderer.last_paragraph.lines ? 0 : renderer.last_paragraph.lines[1]; + const lines = token.lines[0] - last_line; + return [...new Array(lines)].map(e => "[br]").join(""); + }, + "paragraph_close": () => "", - "em_open": (renderer: Renderer, token: RemarkToken) => "[i]", - "em_close": (renderer: Renderer, token: RemarkToken) => "[/i]", + "strong_open": (renderer: Renderer, token: RemarkToken) => "[b]", + "strong_close": (renderer: Renderer, token: RemarkToken) => "[/b]", - "del_open": () => "[s]", - "del_close": () => "[/s]", + "em_open": (renderer: Renderer, token: RemarkToken) => "[i]", + "em_close": (renderer: Renderer, token: RemarkToken) => "[/i]", - "sup": (renderer: Renderer, token: RemarkToken) => "[sup]" + renderer.maybe_escape_bb(token.content) + "[/sup]", - "sub": (renderer: Renderer, token: RemarkToken) => "[sub]" + renderer.maybe_escape_bb(token.content) + "[/sub]", + "del_open": () => "[s]", + "del_close": () => "[/s]", - "bullet_list_open": () => "[ul]", - "bullet_list_close": () => "[/ul]", + "sup": (renderer: Renderer, token: RemarkToken) => "[sup]" + renderer.maybe_escape_bb(token.content) + "[/sup]", + "sub": (renderer: Renderer, token: RemarkToken) => "[sub]" + renderer.maybe_escape_bb(token.content) + "[/sub]", - "ordered_list_open": () => "[ol]", - "ordered_list_close": () => "[/ol]", + "bullet_list_open": () => "[ul]", + "bullet_list_close": () => "[/ul]", - "list_item_open": () => "[li]", - "list_item_close": () => "[/li]", + "ordered_list_open": () => "[ol]", + "ordered_list_close": () => "[/ol]", - "table_open": () => "[table]", - "table_close": () => "[/table]", + "list_item_open": () => "[li]", + "list_item_close": () => "[/li]", - "thead_open": () => "", - "thead_close": () => "", + "table_open": () => "[table]", + "table_close": () => "[/table]", - "tbody_open": () => "", - "tbody_close": () => "", + "thead_open": () => "", + "thead_close": () => "", - "tr_open": () => "[tr]", - "tr_close": () => "[/tr]", + "tbody_open": () => "", + "tbody_close": () => "", - "th_open": (renderer: Renderer, token: RemarkToken) => "[th" + (token.align ? ("=" + token.align) : "") + "]", - "th_close": () => "[/th]", + "tr_open": () => "[tr]", + "tr_close": () => "[/tr]", - "td_open": () => "[td]", - "td_close": () => "[/td]", + "th_open": (renderer: Renderer, token: RemarkToken) => "[th" + (token.align ? ("=" + token.align) : "") + "]", + "th_close": () => "[/th]", - "link_open": (renderer: Renderer, token: RemarkToken) => "[url" + (token.href ? ("=" + token.href) : "") + "]", - "link_close": () => "[/url]", + "td_open": () => "[td]", + "td_close": () => "[/td]", - "image": (renderer: Renderer, token: RemarkToken) => "[img=" + (token.src) + "]" + (token.alt || token.src) + "[/img]", + "link_open": (renderer: Renderer, token: RemarkToken) => "[url" + (token.href ? ("=" + token.href) : "") + "]", + "link_close": () => "[/url]", - //footnote_ref + "image": (renderer: Renderer, token: RemarkToken) => "[img=" + (token.src) + "]" + (token.alt || token.src) + "[/img]", - //"content": "==Marked text==", - //mark_open - //mark_close + //footnote_ref - //++Inserted text++ - "ins_open": () => "[u]", - "ins_close": () => "[/u]", + //"content": "==Marked text==", + //mark_open + //mark_close - /* + //++Inserted text++ + "ins_open": () => "[u]", + "ins_close": () => "[/u]", + + /* ``` test [/code] test ``` - */ + */ - "code": (renderer: Renderer, token: RemarkToken) => "[i-code]" + xbbcode.escape(token.content) + "[/i-code]", - "fence": (renderer: Renderer, token: RemarkToken) => "[code" + (token.params ? ("=" + token.params) : "") + "]" + xbbcode.escape(token.content) + "[/code]", + "code": (renderer: Renderer, token: RemarkToken) => "[i-code]" + xbbcode.escape(token.content) + "[/i-code]", + "fence": (renderer: Renderer, token: RemarkToken) => "[code" + (token.params ? ("=" + token.params) : "") + "]" + xbbcode.escape(token.content) + "[/code]", - "heading_open": (renderer: Renderer, token: RemarkToken) => "[size=" + (9 - Math.min(4, token.hLevel)) + "]", - "heading_close": (renderer: Renderer, token: RemarkToken) => "[/size][hr]", + "heading_open": (renderer: Renderer, token: RemarkToken) => "[size=" + (9 - Math.min(4, token.hLevel)) + "]", + "heading_close": (renderer: Renderer, token: RemarkToken) => "[/size][hr]", - "hr": () => "[hr]", + "hr": () => "[hr]", - //> Experience real-time editing with Remarkable! - //blockquote_open, - //blockquote_close - }; + //> Experience real-time editing with Remarkable! + //blockquote_open, + //blockquote_close + }; - private _options; - last_paragraph: RemarkToken; + private _options; + last_paragraph: RemarkToken; - render(tokens: RemarkToken[], options: any, env: any) { - this.last_paragraph = undefined; - this._options = options; - let result = ''; + render(tokens: RemarkToken[], options: any, env: any) { + this.last_paragraph = undefined; + this._options = options; + let result = ''; - //TODO: Escape BB-Codes - for(let index = 0; index < tokens.length; index++) { - if (tokens[index].type === 'inline') { - result += this.render_inline(tokens[index].children, index); - } else { - result += this.render_token(tokens[index], index); - } - } - - this._options = undefined; - return result; - } - - private render_token(token: RemarkToken, index: number) { - log.debug(LogCategory.GENERAL, tr("Render Markdown token: %o"), token); - const renderer = Renderer.renderers[token.type]; - if(typeof(renderer) === "undefined") { - log.warn(LogCategory.CHAT, tr("Missing markdown to bbcode renderer for token %s: %o"), token.type, token); - return token.content || ""; - } - - const result = renderer(this, token, index); - if(token.type === "paragraph_open") this.last_paragraph = token; - return result; - } - - private render_inline(tokens: RemarkToken[], index: number) { - let result = ''; - - for(let index = 0; index < tokens.length; index++) { + //TODO: Escape BB-Codes + for(let index = 0; index < tokens.length; index++) { + if (tokens[index].type === 'inline') { + result += this.render_inline(tokens[index].children, index); + } else { result += this.render_token(tokens[index], index); } - - return result; } - options() : any { - return this._options; + this._options = undefined; + return result; + } + + private render_token(token: RemarkToken, index: number) { + log.debug(LogCategory.GENERAL, tr("Render Markdown token: %o"), token); + const renderer = Renderer.renderers[token.type]; + if(typeof(renderer) === "undefined") { + log.warn(LogCategory.CHAT, tr("Missing markdown to bbcode renderer for token %s: %o"), token.type, token); + return token.content || ""; } - maybe_escape_bb(text: string) { - if(this._options.escape_bb) - return xbbcode.escape(text); - return text; - } - } - } - - let _renderer: any; - function process_markdown(message: string, options: { - process_url?: boolean, - escape_bb?: boolean - }) : string { - if(typeof(window.remarkable) === "undefined") - return (options.process_url ? process_urls(message) : message); - - if(!_renderer) { - _renderer = new window.remarkable.Remarkable('full'); - _renderer.set({ - typographer: true - }); - _renderer.renderer = new md2bbc.Renderer(); - _renderer.inline.ruler.disable([ 'newline', 'autolink' ]); - } - _renderer.set({ - process_url: !!options.process_url, - escape_bb: !!options.escape_bb - }); - let result: string = _renderer.render(message); - if(result.endsWith("\n")) - result = result.substr(0, result.length - 1); - return result; - } - - export function preprocess_chat_message(message: string) : string { - const process_url = settings.static_global(Settings.KEY_CHAT_TAG_URLS); - const parse_markdown = settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN); - const escape_bb = !settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE); - - if(parse_markdown) - return process_markdown(message, { - process_url: process_url, - escape_bb: escape_bb - }); - - if(escape_bb) - message = xbbcode.escape(message); - return process_url ? process_urls(message) : message; - } - - export namespace history { - let _local_cache: Cache; - - async function get_cache() { - if(_local_cache) - return _local_cache; - - if(!('caches' in window)) - throw "missing cache extension!"; - - return (_local_cache = await caches.open('chat_history')); + const result = renderer(this, token, index); + if(token.type === "paragraph_open") this.last_paragraph = token; + return result; } - export async function load_history(key: string) : Promise { - const cache = await get_cache(); - const request = new Request("https://_local_cache/cache_request_" + key); - const cached_response = await cache.match(request); - if(!cached_response) - return undefined; + private render_inline(tokens: RemarkToken[], index: number) { + let result = ''; - return await cached_response.json(); - } - - export async function save_history(key: string, value: any) { - const cache = await get_cache(); - const request = new Request("https://_local_cache/cache_request_" + key); - const data = JSON.stringify(value); - - const new_headers = new Headers(); - new_headers.set("Content-type", "application/json"); - new_headers.set("Content-length", data.length.toString()); - - - await cache.put(request, new Response(data, { - headers: new_headers - })); - } - } - - export namespace date { - export function same_day(a: number | Date, b: number | Date) { - a = a instanceof Date ? a : new Date(a); - b = b instanceof Date ? b : new Date(b); - - if(a.getDate() !== b.getDate()) - return false; - if(a.getMonth() !== b.getMonth()) - return false; - return a.getFullYear() === b.getFullYear(); - } - } - } - - export namespace format { - export namespace date { - export enum ColloquialFormat { - YESTERDAY, - TODAY, - GENERAL - } - - export function date_format(date: Date, now: Date, ignore_settings?: boolean) : ColloquialFormat { - if(!ignore_settings && !settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)) - return ColloquialFormat.GENERAL; - - let delta_day = now.getDate() - date.getDate(); - if(delta_day < 1) /* month change? */ - delta_day = date.getDate() - now.getDate(); - if(delta_day == 0) - return ColloquialFormat.TODAY; - else if(delta_day == 1) - return ColloquialFormat.YESTERDAY; - return ColloquialFormat.GENERAL; - } - - export function format_date_general(date: Date, hours?: boolean) : string { - return ('00' + date.getDate()).substr(-2) + "." - + ('00' + date.getMonth()).substr(-2) + "." - + date.getFullYear() + - (typeof(hours) === "undefined" || hours ? " at " - + ('00' + date.getHours()).substr(-2) + ":" - + ('00' + date.getMinutes()).substr(-2) - : ""); - } - - export function format_date_colloquial(date: Date, current_timestamp: Date) : { result: string; format: ColloquialFormat } { - const format = date_format(date, current_timestamp); - if(format == ColloquialFormat.GENERAL) { - return { - result: format_date_general(date), - format: format - }; - } else { - let hrs = date.getHours(); - let time = "AM"; - if(hrs > 12) { - hrs -= 12; - time = "PM"; - } - return { - result: (format == ColloquialFormat.YESTERDAY ? tr("Yesterday at") : tr("Today at")) + " " + hrs + ":" + date.getMinutes() + " " + time, - format: format - }; - } - } - - export function format_chat_time(date: Date) : { - result: string, - next_update: number /* in MS */ - } { - const timestamp = date.getTime(); - const current_timestamp = new Date(); - - const result = { - result: "", - next_update: 0 - }; - - if(settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)) { - const format = format_date_colloquial(date, current_timestamp); - result.result = format.result; - result.next_update = 0; /* TODO: Update on day change? */ - } else { - const delta = current_timestamp.getTime() - timestamp; - if(delta < 2000) { - result.result = "now"; - result.next_update = 2500 - delta; /* update after two seconds */ - } else if(delta < 30000) { /* 30 seconds */ - result.result = Math.floor(delta / 1000) + " " + tr("seconds ago"); - result.next_update = 1000; /* update every second */ - } else if(delta < 30 * 60 * 1000) { /* 30 minutes */ - if(delta < 120 * 1000) - result.result = tr("one minute ago"); - else - result.result = Math.floor(delta / (1000 * 60)) + " " + tr("minutes ago"); - result.next_update = 60000; /* updater after a minute */ - } else { - result.result = format_date_colloquial(date, current_timestamp).result; - result.next_update = 0; /* TODO: Update on day change? */ - } + for(let index = 0; index < tokens.length; index++) { + result += this.render_token(tokens[index], index); } return result; } - } - export namespace time { - export function format_online_time(secs: number) : string { - let years = Math.floor(secs / (60 * 60 * 24 * 365)); - let days = Math.floor(secs / (60 * 60 * 24)) % 365; - let hours = Math.floor(secs / (60 * 60)) % 24; - let minutes = Math.floor(secs / 60) % 60; - let seconds = Math.floor(secs % 60); - let result = ""; - if(years > 0) - result += years + " " + tr("years") + " "; - if(years > 0 || days > 0) - result += days + " " + tr("days") + " "; - if(years > 0 || days > 0 || hours > 0) - result += hours + " " + tr("hours") + " "; - if(years > 0 || days > 0 || hours > 0 || minutes > 0) - result += minutes + " " + tr("minutes") + " "; - if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) - result += seconds + " " + tr("seconds") + " "; - else - result = tr("now") + " "; + options() : any { + return this._options; + } - return result.substr(0, result.length - 1); + maybe_escape_bb(text: string) { + if(this._options.escape_bb) + return xbbcode.escape(text); + return text; } } } + + let _renderer: any; + function process_markdown(message: string, options: { + process_url?: boolean, + escape_bb?: boolean + }) : string { + if(typeof(window.remarkable) === "undefined") + return (options.process_url ? process_urls(message) : message); + + if(!_renderer) { + _renderer = new window.remarkable.Remarkable('full'); + _renderer.set({ + typographer: true + }); + _renderer.renderer = new md2bbc.Renderer(); + _renderer.inline.ruler.disable([ 'newline', 'autolink' ]); + } + _renderer.set({ + process_url: !!options.process_url, + escape_bb: !!options.escape_bb + }); + let result: string = _renderer.render(message); + if(result.endsWith("\n")) + result = result.substr(0, result.length - 1); + return result; + } + + export function preprocess_chat_message(message: string) : string { + const process_url = settings.static_global(Settings.KEY_CHAT_TAG_URLS); + const parse_markdown = settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN); + const escape_bb = !settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE); + + if(parse_markdown) + return process_markdown(message, { + process_url: process_url, + escape_bb: escape_bb + }); + + if(escape_bb) + message = xbbcode.escape(message); + return process_url ? process_urls(message) : message; + } + + export namespace history { + let _local_cache: Cache; + + async function get_cache() { + if(_local_cache) + return _local_cache; + + if(!('caches' in window)) + throw "missing cache extension!"; + + return (_local_cache = await caches.open('chat_history')); + } + + export async function load_history(key: string) : Promise { + const cache = await get_cache(); + const request = new Request("https://_local_cache/cache_request_" + key); + const cached_response = await cache.match(request); + if(!cached_response) + return undefined; + + return await cached_response.json(); + } + + export async function save_history(key: string, value: any) { + const cache = await get_cache(); + const request = new Request("https://_local_cache/cache_request_" + key); + const data = JSON.stringify(value); + + const new_headers = new Headers(); + new_headers.set("Content-type", "application/json"); + new_headers.set("Content-length", data.length.toString()); + + + await cache.put(request, new Response(data, { + headers: new_headers + })); + } + } + + export namespace date { + export function same_day(a: number | Date, b: number | Date) { + a = a instanceof Date ? a : new Date(a); + b = b instanceof Date ? b : new Date(b); + + if(a.getDate() !== b.getDate()) + return false; + if(a.getMonth() !== b.getMonth()) + return false; + return a.getFullYear() === b.getFullYear(); + } + } +} + +export namespace format { + export namespace date { + export enum ColloquialFormat { + YESTERDAY, + TODAY, + GENERAL + } + + export function date_format(date: Date, now: Date, ignore_settings?: boolean) : ColloquialFormat { + if(!ignore_settings && !settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)) + return ColloquialFormat.GENERAL; + + let delta_day = now.getDate() - date.getDate(); + if(delta_day < 1) /* month change? */ + delta_day = date.getDate() - now.getDate(); + if(delta_day == 0) + return ColloquialFormat.TODAY; + else if(delta_day == 1) + return ColloquialFormat.YESTERDAY; + return ColloquialFormat.GENERAL; + } + + export function format_date_general(date: Date, hours?: boolean) : string { + return ('00' + date.getDate()).substr(-2) + "." + + ('00' + date.getMonth()).substr(-2) + "." + + date.getFullYear() + + (typeof(hours) === "undefined" || hours ? " at " + + ('00' + date.getHours()).substr(-2) + ":" + + ('00' + date.getMinutes()).substr(-2) + : ""); + } + + export function format_date_colloquial(date: Date, current_timestamp: Date) : { result: string; format: ColloquialFormat } { + const format = date_format(date, current_timestamp); + if(format == ColloquialFormat.GENERAL) { + return { + result: format_date_general(date), + format: format + }; + } else { + let hrs = date.getHours(); + let time = "AM"; + if(hrs > 12) { + hrs -= 12; + time = "PM"; + } + return { + result: (format == ColloquialFormat.YESTERDAY ? tr("Yesterday at") : tr("Today at")) + " " + hrs + ":" + date.getMinutes() + " " + time, + format: format + }; + } + } + + export function format_chat_time(date: Date) : { + result: string, + next_update: number /* in MS */ + } { + const timestamp = date.getTime(); + const current_timestamp = new Date(); + + const result = { + result: "", + next_update: 0 + }; + + if(settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)) { + const format = format_date_colloquial(date, current_timestamp); + result.result = format.result; + result.next_update = 0; /* TODO: Update on day change? */ + } else { + const delta = current_timestamp.getTime() - timestamp; + if(delta < 2000) { + result.result = "now"; + result.next_update = 2500 - delta; /* update after two seconds */ + } else if(delta < 30000) { /* 30 seconds */ + result.result = Math.floor(delta / 1000) + " " + tr("seconds ago"); + result.next_update = 1000; /* update every second */ + } else if(delta < 30 * 60 * 1000) { /* 30 minutes */ + if(delta < 120 * 1000) + result.result = tr("one minute ago"); + else + result.result = Math.floor(delta / (1000 * 60)) + " " + tr("minutes ago"); + result.next_update = 60000; /* updater after a minute */ + } else { + result.result = format_date_colloquial(date, current_timestamp).result; + result.next_update = 0; /* TODO: Update on day change? */ + } + } + + return result; + } + } + export namespace time { + export function format_online_time(secs: number) : string { + let years = Math.floor(secs / (60 * 60 * 24 * 365)); + let days = Math.floor(secs / (60 * 60 * 24)) % 365; + let hours = Math.floor(secs / (60 * 60)) % 24; + let minutes = Math.floor(secs / 60) % 60; + let seconds = Math.floor(secs % 60); + + let result = ""; + if(years > 0) + result += years + " " + tr("years") + " "; + if(years > 0 || days > 0) + result += days + " " + tr("days") + " "; + if(years > 0 || days > 0 || hours > 0) + result += hours + " " + tr("hours") + " "; + if(years > 0 || days > 0 || hours > 0 || minutes > 0) + result += minutes + " " + tr("minutes") + " "; + if(years > 0 || days > 0 || hours > 0 || minutes > 0 || seconds > 0) + result += seconds + " " + tr("seconds") + " "; + else + result = tr("now") + " "; + + return result.substr(0, result.length - 1); + } + } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/client_info.ts b/shared/js/ui/frames/side/client_info.ts index 9847b42b..3865ac51 100644 --- a/shared/js/ui/frames/side/client_info.ts +++ b/shared/js/ui/frames/side/client_info.ts @@ -1,273 +1,280 @@ -namespace chat { - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +import {GroupManager} from "tc-shared/permission/GroupManager"; +import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame"; +import {ClientEntry, LocalClientEntry} from "tc-shared/ui/client"; +import {openClientInfo} from "tc-shared/ui/modal/ModalClientInfo"; +import * as htmltags from "tc-shared/ui/htmltags"; +import * as image_preview from "../image_preview"; +import {format} from "tc-shared/ui/frames/side/chat_helper"; +import * as i18nc from "tc-shared/i18n/country"; - export class ClientInfo { - readonly handle: Frame; - private _html_tag: JQuery; - private _current_client: ClientEntry | undefined; - private _online_time_updater: number; - previous_frame_content: FrameContent; +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - constructor(handle: Frame) { - this.handle = handle; - this._build_html_tag(); - } +export class ClientInfo { + readonly handle: Frame; + private _html_tag: JQuery; + private _current_client: ClientEntry | undefined; + private _online_time_updater: number; + previous_frame_content: FrameContent; - html_tag() : JQuery { - return this._html_tag; - } + constructor(handle: Frame) { + this.handle = handle; + this._build_html_tag(); + } - destroy() { - clearInterval(this._online_time_updater); + html_tag() : JQuery { + return this._html_tag; + } - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; + destroy() { + clearInterval(this._online_time_updater); - this._current_client = undefined; - this.previous_frame_content = undefined; - } + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_client_info").renderTag(); - this._html_tag.find(".button-close").on('click', () => { - if(this.previous_frame_content === FrameContent.CLIENT_INFO) - this.previous_frame_content = FrameContent.NONE; + this._current_client = undefined; + this.previous_frame_content = undefined; + } - this.handle.set_content(this.previous_frame_content); - }); - this._html_tag.find(".button-more").on('click', () => { - if(!this._current_client) - return; + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_client_info").renderTag(); + this._html_tag.find(".button-close").on('click', () => { + if(this.previous_frame_content === FrameContent.CLIENT_INFO) + this.previous_frame_content = FrameContent.NONE; - Modals.openClientInfo(this._current_client); - }); - this._html_tag.find('.container-avatar-edit').on('click', () => this.handle.handle.update_avatar()); - } - - current_client() : ClientEntry { - return this._current_client; - } - - set_current_client(client: ClientEntry | undefined, enforce?: boolean) { - if(client) client.updateClientVariables(); /* just to ensure */ - if(client === this._current_client && (typeof(enforce) === "undefined" || !enforce)) + this.handle.set_content(this.previous_frame_content); + }); + this._html_tag.find(".button-more").on('click', () => { + if(!this._current_client) return; - this._current_client = client; + openClientInfo(this._current_client); + }); + this._html_tag.find('.container-avatar-edit').on('click', () => this.handle.handle.update_avatar()); + } - /* updating the header */ - { - const client_name = this._html_tag.find(".client-name"); - client_name.children().remove(); - htmltags.generate_client_object({ - add_braces: false, - client_name: client ? client.clientNickName() : "undefined", - client_unique_id: client ? client.clientUid() : "", - client_id: client ? client.clientId() : 0 - }).appendTo(client_name); + current_client() : ClientEntry { + return this._current_client; + } - const client_description = this._html_tag.find(".client-description"); - client_description.text(client ? client.properties.client_description : "").toggle(!!client.properties.client_description); + set_current_client(client: ClientEntry | undefined, enforce?: boolean) { + if(client) client.updateClientVariables(); /* just to ensure */ + if(client === this._current_client && (typeof(enforce) === "undefined" || !enforce)) + return; - const is_local_entry = client instanceof LocalClientEntry; - const container_avatar = this._html_tag.find(".container-avatar"); - container_avatar.find(".avatar").remove(); - if(client) { - const avatar = this.handle.handle.fileManager.avatars.generate_chat_tag({id: client.clientId()}, client.clientUid()); - if(!is_local_entry) { - avatar.css("cursor", "pointer").on('click', event => { - image_preview.preview_image_tag(this.handle.handle.fileManager.avatars.generate_chat_tag({id: client.clientId()}, client.clientUid())); - }); + this._current_client = client; + + /* updating the header */ + { + const client_name = this._html_tag.find(".client-name"); + client_name.children().remove(); + htmltags.generate_client_object({ + add_braces: false, + client_name: client ? client.clientNickName() : "undefined", + client_unique_id: client ? client.clientUid() : "", + client_id: client ? client.clientId() : 0 + }).appendTo(client_name); + + const client_description = this._html_tag.find(".client-description"); + client_description.text(client ? client.properties.client_description : "").toggle(!!client.properties.client_description); + + const is_local_entry = client instanceof LocalClientEntry; + const container_avatar = this._html_tag.find(".container-avatar"); + container_avatar.find(".avatar").remove(); + if(client) { + const avatar = this.handle.handle.fileManager.avatars.generate_chat_tag({id: client.clientId()}, client.clientUid()); + if(!is_local_entry) { + avatar.css("cursor", "pointer").on('click', event => { + image_preview.preview_image_tag(this.handle.handle.fileManager.avatars.generate_chat_tag({id: client.clientId()}, client.clientUid())); + }); + } + avatar.appendTo(container_avatar); + } else + this.handle.handle.fileManager.avatars.generate_chat_tag(undefined, undefined).appendTo(container_avatar); + + container_avatar.toggleClass("editable", is_local_entry); + } + /* updating the info fields */ + { + const online_time = this._html_tag.find(".client-online-time"); + online_time.text(format.time.format_online_time(client ? client.calculateOnlineTime() : 0)); + if(this._online_time_updater) { + clearInterval(this._online_time_updater); + this._online_time_updater = 0; + } + if(client) { + this._online_time_updater = setInterval(() => { + const client = this._current_client; + if(!client) { + clearInterval(this._online_time_updater); + this._online_time_updater = undefined; + return; } - avatar.appendTo(container_avatar); - } else - this.handle.handle.fileManager.avatars.generate_chat_tag(undefined, undefined).appendTo(container_avatar); - container_avatar.toggleClass("editable", is_local_entry); - } - /* updating the info fields */ - { - const online_time = this._html_tag.find(".client-online-time"); - online_time.text(format.time.format_online_time(client ? client.calculateOnlineTime() : 0)); - if(this._online_time_updater) { - clearInterval(this._online_time_updater); - this._online_time_updater = 0; - } - if(client) { - this._online_time_updater = setInterval(() => { - const client = this._current_client; - if(!client) { - clearInterval(this._online_time_updater); - this._online_time_updater = undefined; - return; - } - - if(client.currentChannel()) /* If he has no channel then he might be disconnected */ - online_time.text(format.time.format_online_time(client.calculateOnlineTime())); - else { - online_time.text(online_time.text() + tr(" (left view)")); - clearInterval(this._online_time_updater); - } - }, 1000); - } - - const country = this._html_tag.find(".client-country"); - country.children().detach(); - const country_code = (client ? client.properties.client_country : undefined) || "xx"; - $.spawn("div").addClass("country flag-" + country_code.toLowerCase()).appendTo(country); - $.spawn("a").text(i18n.country_name(country_code.toUpperCase())).appendTo(country); - - - const version = this._html_tag.find(".client-version"); - version.children().detach(); - if(client) { - let platform = client.properties.client_platform; - if(platform.indexOf("Win32") != 0 && (client.properties.client_version.indexOf("Win64") != -1 || client.properties.client_version.indexOf("WOW64") != -1)) - platform = platform.replace("Win32", "Win64"); - $.spawn("a").attr("title", client.properties.client_version).text( - client.properties.client_version.split(" ")[0] + " on " + platform - ).appendTo(version); - } - - const volume = this._html_tag.find(".client-local-volume"); - volume.text((client && client.get_audio_handle() ? (client.get_audio_handle().get_volume() * 100) : -1).toFixed(0) + "%"); + if(client.currentChannel()) /* If he has no channel then he might be disconnected */ + online_time.text(format.time.format_online_time(client.calculateOnlineTime())); + else { + online_time.text(online_time.text() + tr(" (left view)")); + clearInterval(this._online_time_updater); + } + }, 1000); } - /* teaspeak forum */ - { - const container_forum = this._html_tag.find(".container-teaforo"); - if(client && client.properties.client_teaforo_id) { - container_forum.show(); + const country = this._html_tag.find(".client-country"); + country.children().detach(); + const country_code = (client ? client.properties.client_country : undefined) || "xx"; + $.spawn("div").addClass("country flag-" + country_code.toLowerCase()).appendTo(country); + $.spawn("a").text(i18nc.country_name(country_code.toUpperCase())).appendTo(country); - const container_data = container_forum.find(".client-teaforo-account"); - container_data.children().remove(); - let text = client.properties.client_teaforo_name; - if((client.properties.client_teaforo_flags & 0x01) > 0) - text += " (" + tr("Banned") + ")"; - if((client.properties.client_teaforo_flags & 0x02) > 0) - text += " (" + tr("Stuff") + ")"; - if((client.properties.client_teaforo_flags & 0x04) > 0) - text += " (" + tr("Premium") + ")"; + const version = this._html_tag.find(".client-version"); + version.children().detach(); + if(client) { + let platform = client.properties.client_platform; + if(platform.indexOf("Win32") != 0 && (client.properties.client_version.indexOf("Win64") != -1 || client.properties.client_version.indexOf("WOW64") != -1)) + platform = platform.replace("Win32", "Win64"); + $.spawn("a").attr("title", client.properties.client_version).text( + client.properties.client_version.split(" ")[0] + " on " + platform + ).appendTo(version); + } - $.spawn("a") - .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) - .attr("target", "_blank") - .text(text) - .appendTo(container_data); + const volume = this._html_tag.find(".client-local-volume"); + volume.text((client && client.get_audio_handle() ? (client.get_audio_handle().get_volume() * 100) : -1).toFixed(0) + "%"); + } + + /* teaspeak forum */ + { + const container_forum = this._html_tag.find(".container-teaforo"); + if(client && client.properties.client_teaforo_id) { + container_forum.show(); + + const container_data = container_forum.find(".client-teaforo-account"); + container_data.children().remove(); + + let text = client.properties.client_teaforo_name; + if((client.properties.client_teaforo_flags & 0x01) > 0) + text += " (" + tr("Banned") + ")"; + if((client.properties.client_teaforo_flags & 0x02) > 0) + text += " (" + tr("Stuff") + ")"; + if((client.properties.client_teaforo_flags & 0x04) > 0) + text += " (" + tr("Premium") + ")"; + + $.spawn("a") + .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) + .attr("target", "_blank") + .text(text) + .appendTo(container_data); + } else { + container_forum.hide(); + } + } + + /* update the client status */ + { + //TODO Implement client status! + const container_status = this._html_tag.find(".container-client-status"); + const container_status_entries = container_status.find(".client-status"); + container_status_entries.children().detach(); + if(client) { + if(client.properties.client_away) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-away"), + $.spawn("a").text(tr("Away")), + client.properties.client_away_message ? + $.spawn("a").addClass("away-message").text("(" + client.properties.client_away_message + ")") : + undefined + ) + ) + } + if(client.is_muted()) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-input_muted_local"), + $.spawn("a").text(tr("Client local muted")) + ) + ) + } + if(!client.properties.client_output_hardware) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-hardware_output_muted"), + $.spawn("a").text(tr("Speakers/Headphones disabled")) + ) + ) + } + if(!client.properties.client_input_hardware) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-hardware_input_muted"), + $.spawn("a").text(tr("Microphone disabled")) + ) + ) + } + if(client.properties.client_output_muted) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-output_muted"), + $.spawn("a").text(tr("Speakers/Headphones Muted")) + ) + ) + } + if(client.properties.client_input_muted) { + container_status_entries.append( + $.spawn("div").addClass("status-entry").append( + $.spawn("div").addClass("icon_em client-input_muted"), + $.spawn("a").text(tr("Microphone Muted")) + ) + ) + } + } + container_status.toggle(container_status_entries.children().length > 0); + } + /* update client server groups */ + { + const container_groups = this._html_tag.find(".client-group-server"); + container_groups.children().detach(); + if(client) { + const invalid_groups = []; + const groups = client.assignedServerGroupIds().map(group_id => { + const result = this.handle.handle.groups.serverGroup(group_id); + if(!result) + invalid_groups.push(group_id); + return result; + }).filter(e => !!e).sort(GroupManager.sorter()); + for(const invalid_id of invalid_groups) { + container_groups.append($.spawn("a").text("{" + tr("server group ") + invalid_groups + "}").attr("title", tr("Missing server group id!") + " (" + invalid_groups + ")")); + } + for(let group of groups) { + container_groups.append( + $.spawn("div").addClass("group-container") + .append( + this.handle.handle.fileManager.icons.generateTag(group.properties.iconid) + ).append( + $.spawn("a").text(group.name).attr("title", tr("Group id: ") + group.id) + ) + ); + } + } + } + /* update client channel group */ + { + const container_group = this._html_tag.find(".client-group-channel"); + container_group.children().detach(); + if(client) { + const group_id = client.assignedChannelGroup(); + let group = this.handle.handle.groups.channelGroup(group_id); + if(group) { + container_group.append( + $.spawn("div").addClass("group-container") + .append( + this.handle.handle.fileManager.icons.generateTag(group.properties.iconid) + ).append( + $.spawn("a").text(group.name).attr("title", tr("Group id: ") + group_id) + ) + ); } else { - container_forum.hide(); - } - } - - /* update the client status */ - { - //TODO Implement client status! - const container_status = this._html_tag.find(".container-client-status"); - const container_status_entries = container_status.find(".client-status"); - container_status_entries.children().detach(); - if(client) { - if(client.properties.client_away) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-away"), - $.spawn("a").text(tr("Away")), - client.properties.client_away_message ? - $.spawn("a").addClass("away-message").text("(" + client.properties.client_away_message + ")") : - undefined - ) - ) - } - if(client.is_muted()) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-input_muted_local"), - $.spawn("a").text(tr("Client local muted")) - ) - ) - } - if(!client.properties.client_output_hardware) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-hardware_output_muted"), - $.spawn("a").text(tr("Speakers/Headphones disabled")) - ) - ) - } - if(!client.properties.client_input_hardware) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-hardware_input_muted"), - $.spawn("a").text(tr("Microphone disabled")) - ) - ) - } - if(client.properties.client_output_muted) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-output_muted"), - $.spawn("a").text(tr("Speakers/Headphones Muted")) - ) - ) - } - if(client.properties.client_input_muted) { - container_status_entries.append( - $.spawn("div").addClass("status-entry").append( - $.spawn("div").addClass("icon_em client-input_muted"), - $.spawn("a").text(tr("Microphone Muted")) - ) - ) - } - } - container_status.toggle(container_status_entries.children().length > 0); - } - /* update client server groups */ - { - const container_groups = this._html_tag.find(".client-group-server"); - container_groups.children().detach(); - if(client) { - const invalid_groups = []; - const groups = client.assignedServerGroupIds().map(group_id => { - const result = this.handle.handle.groups.serverGroup(group_id); - if(!result) - invalid_groups.push(group_id); - return result; - }).filter(e => !!e).sort(GroupManager.sorter()); - for(const invalid_id of invalid_groups) { - container_groups.append($.spawn("a").text("{" + tr("server group ") + invalid_groups + "}").attr("title", tr("Missing server group id!") + " (" + invalid_groups + ")")); - } - for(let group of groups) { - container_groups.append( - $.spawn("div").addClass("group-container") - .append( - this.handle.handle.fileManager.icons.generateTag(group.properties.iconid) - ).append( - $.spawn("a").text(group.name).attr("title", tr("Group id: ") + group.id) - ) - ); - } - } - } - /* update client channel group */ - { - const container_group = this._html_tag.find(".client-group-channel"); - container_group.children().detach(); - if(client) { - const group_id = client.assignedChannelGroup(); - let group = this.handle.handle.groups.channelGroup(group_id); - if(group) { - container_group.append( - $.spawn("div").addClass("group-container") - .append( - this.handle.handle.fileManager.icons.generateTag(group.properties.iconid) - ).append( - $.spawn("a").text(group.name).attr("title", tr("Group id: ") + group_id) - ) - ); - } else { - container_group.append($.spawn("a").text(tr("Invalid channel group!")).attr("title", tr("Missing channel group id!") + " (" + group_id + ")")); - } + container_group.append($.spawn("a").text(tr("Invalid channel group!")).attr("title", tr("Missing channel group id!") + " (" + group_id + ")")); } } } diff --git a/shared/js/ui/frames/side/conversations.ts b/shared/js/ui/frames/side/conversations.ts index 3c45863a..18ec6a0c 100644 --- a/shared/js/ui/frames/side/conversations.ts +++ b/shared/js/ui/frames/side/conversations.ts @@ -1,615 +1,623 @@ -namespace chat { - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +import {settings, Settings} from "tc-shared/settings"; +import {format} from "tc-shared/ui/frames/side/chat_helper"; +import {bbcode_chat, formatMessage} from "tc-shared/ui/frames/chat"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {LogCategory} from "tc-shared/log"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {ChatBox} from "tc-shared/ui/frames/side/chat_box"; +import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame"; +import {createErrorModal} from "tc-shared/ui/elements/Modal"; +import * as log from "tc-shared/log"; +import * as htmltags from "tc-shared/ui/htmltags"; - export namespace channel { - export type ViewEntry = { - html_element: JQuery; - update_timer?: number; +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; + +export type ViewEntry = { + html_element: JQuery; + update_timer?: number; +} +export type MessageData = { + timestamp: number; + + message: string; + + sender_name: string; + sender_unique_id: string; + sender_database_id: number; +} +export type Message = MessageData & ViewEntry; + +export class Conversation { + readonly handle: ConversationManager; + readonly channel_id: number; + + private _flag_private: boolean; + + private _html_tag: JQuery; + private _container_messages: JQuery; + private _container_new_message: JQuery; + + private _container_no_permissions: JQuery; + private _container_no_permissions_shown: boolean = false; + + private _container_is_private: JQuery; + private _container_is_private_shown: boolean = false; + + private _container_no_support: JQuery; + private _container_no_support_shown: boolean = false; + + private _view_max_messages = 40; /* reset to 40 again as soon we tab out :) */ + private _view_older_messages: ViewEntry; + private _has_older_messages: boolean; /* undefined := not known | else flag */ + + private _view_entries: ViewEntry[] = []; + + private _last_messages: MessageData[] = []; + private _last_messages_timestamp: number = 0; + private _first_unread_message: Message; + private _first_unread_message_pointer: ViewEntry; + + private _scroll_position: number | undefined; /* undefined to follow bottom | position for special stuff */ + + constructor(handle: ConversationManager, channel_id: number) { + this.handle = handle; + this.channel_id = channel_id; + + this._build_html_tag(); + } + + html_tag() : JQuery { return this._html_tag; } + destroy() { + this._first_unread_message_pointer.html_element.detach(); + this._first_unread_message_pointer = undefined; + + this._view_older_messages.html_element.detach(); + this._view_older_messages = undefined; + + for(const view_entry of this._view_entries) { + view_entry.html_element.detach(); + clearTimeout(view_entry.update_timer); } - export type MessageData = { - timestamp: number; + this._view_entries = []; + } - message: string; + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_channel_messages").renderTag(); - sender_name: string; - sender_unique_id: string; - sender_database_id: number; + this._container_new_message = this._html_tag.find(".new-message"); + this._container_no_permissions = this._html_tag.find(".no-permissions").hide(); + this._container_is_private = this._html_tag.find(".private-conversation").hide(); + this._container_no_support = this._html_tag.find(".not-supported").hide(); + + this._container_messages = this._html_tag.find(".container-messages"); + this._container_messages.on('scroll', event => { + const exact_position = this._container_messages[0].scrollTop + this._container_messages[0].clientHeight; + const current_view = exact_position + this._container_messages[0].clientHeight * .125; + if(current_view > this._container_messages[0].scrollHeight) { + this._scroll_position = undefined; + } else { + this._scroll_position = this._container_messages[0].scrollTop; + } + + const will_visible = !!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position; + const is_visible = this._container_new_message[0].classList.contains("shown"); + if(!is_visible && will_visible) + this._container_new_message[0].classList.add("shown"); + + if(is_visible && !will_visible) + this._container_new_message[0].classList.remove("shown"); + + //This causes a Layout recalc (Forced reflow) + //this._container_new_message.toggleClass("shown",!!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position); + }); + + this._view_older_messages = this._generate_view_spacer(tr("Load older messages"), "old"); + this._first_unread_message_pointer = this._generate_view_spacer(tr("Unread messages"), "new"); + this._view_older_messages.html_element.appendTo(this._container_messages).on('click', event => { + this.fetch_older_messages(); + }); + + this._container_new_message.on('click', event => { + if(!this._first_unread_message) + return; + this._scroll_position = this._first_unread_message_pointer.html_element[0].offsetTop; + this.fix_scroll(true); + }); + this._container_messages.on('click', event => { + if(this._container_new_message.hasClass('shown')) + return; /* we have clicked, but no chance to see the unread message pointer */ + this._mark_read(); + }); + this.set_flag_private(false); + } + + is_unread() { return !!this._first_unread_message; } + + mark_read() { this._mark_read(); } + private _mark_read() { + if(this._first_unread_message) { + this._first_unread_message = undefined; + + const ctree = this.handle.handle.handle.channelTree; + if(ctree && ctree.tag_tree()) + ctree.tag_tree().find(".marker-text-unread[conversation='" + this.channel_id + "']").addClass("hidden"); } - export type Message = MessageData & ViewEntry; + this._first_unread_message_pointer.html_element.detach(); + } - export class Conversation { - readonly handle: ConversationManager; - readonly channel_id: number; + private _generate_view_message(data: MessageData) : Message { + const response = data as Message; + if(response.html_element) + return response; - private _flag_private: boolean; + const timestamp = new Date(data.timestamp); + let time = format.date.format_chat_time(timestamp); + response.html_element = $("#tmpl_frame_chat_channel_message").renderTag({ + timestamp: time.result, + client_name: htmltags.generate_client_object({ + add_braces: false, + client_name: data.sender_name, + client_unique_id: data.sender_unique_id, + client_id: 0 + }), + message: bbcode_chat(data.message), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({database_id: data.sender_database_id}, data.sender_unique_id) + }); - private _html_tag: JQuery; - private _container_messages: JQuery; - private _container_new_message: JQuery; + response.html_element.find(".button-delete").on('click', () => this.delete_message(data)); - private _container_no_permissions: JQuery; - private _container_no_permissions_shown: boolean = false; - - private _container_is_private: JQuery; - private _container_is_private_shown: boolean = false; - - private _container_no_support: JQuery; - private _container_no_support_shown: boolean = false; - - private _view_max_messages = 40; /* reset to 40 again as soon we tab out :) */ - private _view_older_messages: ViewEntry; - private _has_older_messages: boolean; /* undefined := not known | else flag */ - - private _view_entries: ViewEntry[] = []; - - private _last_messages: MessageData[] = []; - private _last_messages_timestamp: number = 0; - private _first_unread_message: Message; - private _first_unread_message_pointer: ViewEntry; - - private _scroll_position: number | undefined; /* undefined to follow bottom | position for special stuff */ - - constructor(handle: ConversationManager, channel_id: number) { - this.handle = handle; - this.channel_id = channel_id; - - this._build_html_tag(); - } - - html_tag() : JQuery { return this._html_tag; } - destroy() { - this._first_unread_message_pointer.html_element.detach(); - this._first_unread_message_pointer = undefined; - - this._view_older_messages.html_element.detach(); - this._view_older_messages = undefined; - - for(const view_entry of this._view_entries) { - view_entry.html_element.detach(); - clearTimeout(view_entry.update_timer); - } - this._view_entries = []; - } - - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_channel_messages").renderTag(); - - this._container_new_message = this._html_tag.find(".new-message"); - this._container_no_permissions = this._html_tag.find(".no-permissions").hide(); - this._container_is_private = this._html_tag.find(".private-conversation").hide(); - this._container_no_support = this._html_tag.find(".not-supported").hide(); - - this._container_messages = this._html_tag.find(".container-messages"); - this._container_messages.on('scroll', event => { - const exact_position = this._container_messages[0].scrollTop + this._container_messages[0].clientHeight; - const current_view = exact_position + this._container_messages[0].clientHeight * .125; - if(current_view > this._container_messages[0].scrollHeight) { - this._scroll_position = undefined; - } else { - this._scroll_position = this._container_messages[0].scrollTop; - } - - const will_visible = !!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position; - const is_visible = this._container_new_message[0].classList.contains("shown"); - if(!is_visible && will_visible) - this._container_new_message[0].classList.add("shown"); - - if(is_visible && !will_visible) - this._container_new_message[0].classList.remove("shown"); - - //This causes a Layout recalc (Forced reflow) - //this._container_new_message.toggleClass("shown",!!this._first_unread_message && this._first_unread_message_pointer.html_element[0].offsetTop > exact_position); - }); - - this._view_older_messages = this._generate_view_spacer(tr("Load older messages"), "old"); - this._first_unread_message_pointer = this._generate_view_spacer(tr("Unread messages"), "new"); - this._view_older_messages.html_element.appendTo(this._container_messages).on('click', event => { - this.fetch_older_messages(); - }); - - this._container_new_message.on('click', event => { - if(!this._first_unread_message) - return; - this._scroll_position = this._first_unread_message_pointer.html_element[0].offsetTop; - this.fix_scroll(true); - }); - this._container_messages.on('click', event => { - if(this._container_new_message.hasClass('shown')) - return; /* we have clicked, but no chance to see the unread message pointer */ - this._mark_read(); - }); - this.set_flag_private(false); - } - - is_unread() { return !!this._first_unread_message; } - - mark_read() { this._mark_read(); } - private _mark_read() { - if(this._first_unread_message) { - this._first_unread_message = undefined; - - const ctree = this.handle.handle.handle.channelTree; - if(ctree && ctree.tag_tree()) - ctree.tag_tree().find(".marker-text-unread[conversation='" + this.channel_id + "']").addClass("hidden"); - } - this._first_unread_message_pointer.html_element.detach(); - } - - private _generate_view_message(data: MessageData) : Message { - const response = data as Message; - if(response.html_element) - return response; - - const timestamp = new Date(data.timestamp); - let time = format.date.format_chat_time(timestamp); - response.html_element = $("#tmpl_frame_chat_channel_message").renderTag({ - timestamp: time.result, - client_name: htmltags.generate_client_object({ - add_braces: false, - client_name: data.sender_name, - client_unique_id: data.sender_unique_id, - client_id: 0 - }), - message: MessageHelper.bbcode_chat(data.message), - avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({database_id: data.sender_database_id}, data.sender_unique_id) - }); - - response.html_element.find(".button-delete").on('click', () => this.delete_message(data)); - - if(time.next_update > 0) { - const _updater = () => { - time = format.date.format_chat_time(timestamp); - response.html_element.find(".info .timestamp").text(time.result); - if(time.next_update > 0) - response.update_timer = setTimeout(_updater, time.next_update); - else - response.update_timer = 0; - }; + if(time.next_update > 0) { + const _updater = () => { + time = format.date.format_chat_time(timestamp); + response.html_element.find(".info .timestamp").text(time.result); + if(time.next_update > 0) response.update_timer = setTimeout(_updater, time.next_update); - } else { + else response.update_timer = 0; - } + }; + response.update_timer = setTimeout(_updater, time.next_update); + } else { + response.update_timer = 0; + } - return response; - } + return response; + } - private _generate_view_spacer(message: string, type: "date" | "new" | "old" | "error") : ViewEntry { - const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ - message: message - }).addClass("type-" + type); - return { - html_element: tag, - update_timer: 0 - } - } + private _generate_view_spacer(message: string, type: "date" | "new" | "old" | "error") : ViewEntry { + const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ + message: message + }).addClass("type-" + type); + return { + html_element: tag, + update_timer: 0 + } + } - last_messages_timestamp() : number { - return this._last_messages_timestamp; - } + last_messages_timestamp() : number { + return this._last_messages_timestamp; + } - fetch_last_messages() { - const fetch_count = this._view_max_messages - this._last_messages.length; - const fetch_timestamp_end = this._last_messages_timestamp + 1; /* we want newer messages then the last message we have */ + fetch_last_messages() { + const fetch_count = this._view_max_messages - this._last_messages.length; + const fetch_timestamp_end = this._last_messages_timestamp + 1; /* we want newer messages then the last message we have */ - //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] - this.handle.handle.handle.serverConnection.send_command("conversationhistory", { - cid: this.channel_id, - timestamp_end: fetch_timestamp_end, - message_count: fetch_count - }, {flagset: ["merge"], process_result: false }).catch(error => { - this._view_older_messages.html_element.toggleClass('shown', false); - if(error instanceof CommandResult) { - if(error.id == ErrorID.CONVERSATION_MORE_DATA) { - if(typeof(this._has_older_messages) === "undefined") - this._has_older_messages = true; - this._view_older_messages.html_element.toggleClass('shown', true); - return; - } else if(error.id == ErrorID.PERMISSION_ERROR) { - this._container_no_permissions.show(); - this._container_no_permissions_shown = true; - } else if(error.id == ErrorID.CONVERSATION_IS_PRIVATE) { - this.set_flag_private(true); - } - /* - else if(error.id == ErrorID.NOT_IMPLEMENTED || error.id == ErrorID.COMMAND_NOT_FOUND) { - this._container_no_support.show(); - this._container_no_support_shown = true; - } - */ - } - //TODO log and handle! - log.error(LogCategory.CHAT, tr("Failed to fetch conversation history. %o"), error); - }).then(() => { - this.fix_scroll(true); - this.handle.update_chat_box(); - }); - } - - fetch_older_messages() { - this._view_older_messages.html_element.toggleClass('shown', false); - - const entry = this._view_entries.slice().reverse().find(e => 'timestamp' in e) as any as {timestamp: number}; - //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] - this.handle.handle.handle.serverConnection.send_command("conversationhistory", { - cid: this.channel_id, - timestamp_begin: entry.timestamp - 1, - message_count: this._view_max_messages - }, {flagset: ["merge"]}).catch(error => { - this._view_older_messages.html_element.toggleClass('shown', false); - if(error instanceof CommandResult) { - if(error.id == ErrorID.CONVERSATION_MORE_DATA) { - this._view_older_messages.html_element.toggleClass('shown', true); - this.handle.update_chat_box(); - return; - } - } - //TODO log and handle! - log.error(LogCategory.CHAT, tr("Failed to fetch conversation history. %o"), error); - }).then(() => { - this.fix_scroll(true); - }); - } - - register_new_message(message: MessageData, update_view?: boolean) { - /* lets insert the message at the right index */ - let _new_message = false; - { - let spliced = false; - for(let index = 0; index < this._last_messages.length; index++) { - if(this._last_messages[index].timestamp < message.timestamp) { - this._last_messages.splice(index, 0, message); - spliced = true; - _new_message = index == 0; /* only set flag if this has been inserted at the front */ - break; - } else if(this._last_messages[index].timestamp == message.timestamp && this._last_messages[index].sender_database_id == message.sender_database_id) { - return; /* we already have that message */ - } - } - if(!spliced && this._last_messages.length < this._view_max_messages) { - this._last_messages.push(message); - } - this._last_messages_timestamp = this._last_messages[0].timestamp; - - while(this._last_messages.length > this._view_max_messages) { - if(this._last_messages[this._last_messages.length - 1] == this._first_unread_message) - break; - this._last_messages.pop(); - } - } - - /* message is within view */ - { - const entry = this._generate_view_message(message); - - let previous: ViewEntry; - for(let index = 0; index < this._view_entries.length; index++) { - const current_entry = this._view_entries[index]; - if(!('timestamp' in current_entry)) - continue; - - if((current_entry as Message).timestamp < message.timestamp) { - this._view_entries.splice(index, 0, entry); - previous = current_entry; - break; - } - } - if(!previous) - this._view_entries.push(entry); - - if(previous) - entry.html_element.insertAfter(previous.html_element); - else - entry.html_element.insertAfter(this._view_older_messages.html_element); /* last element is already the current element */ - - if(_new_message && (typeof(this._scroll_position) === "number" || this.handle.current_channel() !== this.channel_id || this.handle.handle.content_type() !== FrameContent.CHANNEL_CHAT)) { - if(typeof(this._first_unread_message) === "undefined") - this._first_unread_message = entry; - - this._first_unread_message_pointer.html_element.insertBefore(entry.html_element); - this._container_messages.trigger('scroll'); /* updates the new message stuff */ - } - if(typeof(update_view) !== "boolean" || update_view) - this.fix_scroll(true); - } - - /* update chat state */ - this._container_no_permissions.hide(); - this._container_no_permissions_shown = false; - if(update_view) this.handle.update_chat_box(); - } - - /* using a timeout here to not cause a force style recalculation */ - private _scroll_fix_timer: number; - private _scroll_animate: boolean; - - fix_scroll(animate: boolean) { - if(this._scroll_fix_timer) { - this._scroll_animate = this._scroll_animate && animate; - return; - } - - this._scroll_fix_timer = setTimeout(() => { - this._scroll_fix_timer = undefined; - - let offset; - if(this._first_unread_message) { - offset = this._first_unread_message.html_element[0].offsetTop; - } else if(typeof(this._scroll_position) !== "undefined") { - offset = this._scroll_position; - } else { - offset = this._container_messages[0].scrollHeight; - } - - if(this._scroll_animate) { - this._container_messages.stop(true).animate({ - scrollTop: offset - }, 'slow'); - } else { - this._container_messages.stop(true).scrollTop(offset); - } - }, 5); - } - - fix_view_size() { - this._view_older_messages.html_element.toggleClass('shown', !!this._has_older_messages); - - let count = 0; - for(let index = 0; index < this._view_entries.length; index++) { - if('timestamp' in this._view_entries[index]) - count++; - - if(count > this._view_max_messages) { - this._view_entries.splice(index, this._view_entries.length - index).forEach(e => { - clearTimeout(e.update_timer); - e.html_element.remove(); - }); + //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] + this.handle.handle.handle.serverConnection.send_command("conversationhistory", { + cid: this.channel_id, + timestamp_end: fetch_timestamp_end, + message_count: fetch_count + }, {flagset: ["merge"], process_result: false }).catch(error => { + this._view_older_messages.html_element.toggleClass('shown', false); + if(error instanceof CommandResult) { + if(error.id == ErrorID.CONVERSATION_MORE_DATA) { + if(typeof(this._has_older_messages) === "undefined") this._has_older_messages = true; - this._view_older_messages.html_element.toggleClass('shown', true); - break; - } - } - } - - chat_available() : boolean { - return !this._container_no_permissions_shown && !this._container_is_private_shown && !this._container_no_support_shown; - } - - text_send_failed(error: CommandResult | any) { - log.warn(LogCategory.CHAT, "Failed to send text message! (%o)", error); - //TODO: Log if message send failed? - if(error instanceof CommandResult) { - if(error.id == ErrorID.PERMISSION_ERROR) { - //TODO: Split up between channel_text_message_send permission and no view permission - if(error.json["failed_permid"] == 0) { - this._container_no_permissions_shown = true; - this._container_no_permissions.show(); - this.handle.update_chat_box(); - } - } - } - } - - set_flag_private(flag: boolean) { - if(this._flag_private === flag) + this._view_older_messages.html_element.toggleClass('shown', true); return; - - this._flag_private = flag; - this.update_private_state(); - if(!flag) - this.fetch_last_messages(); + } else if(error.id == ErrorID.PERMISSION_ERROR) { + this._container_no_permissions.show(); + this._container_no_permissions_shown = true; + } else if(error.id == ErrorID.CONVERSATION_IS_PRIVATE) { + this.set_flag_private(true); + } + /* + else if(error.id == ErrorID.NOT_IMPLEMENTED || error.id == ErrorID.COMMAND_NOT_FOUND) { + this._container_no_support.show(); + this._container_no_support_shown = true; + } + */ } + //TODO log and handle! + log.error(LogCategory.CHAT, tr("Failed to fetch conversation history. %o"), error); + }).then(() => { + this.fix_scroll(true); + this.handle.update_chat_box(); + }); + } - update_private_state() { - if(!this._flag_private) { - this._container_is_private.hide(); - this._container_is_private_shown = false; - } else { - const client = this.handle.handle.handle.getClient(); - if(client && client.currentChannel() && client.currentChannel().channelId === this.channel_id) { - this._container_is_private_shown = false; - this._container_is_private.hide(); - } else { - this._container_is_private.show(); - this._container_is_private_shown = true; - } + fetch_older_messages() { + this._view_older_messages.html_element.toggleClass('shown', false); + + const entry = this._view_entries.slice().reverse().find(e => 'timestamp' in e) as any as {timestamp: number}; + //conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] + this.handle.handle.handle.serverConnection.send_command("conversationhistory", { + cid: this.channel_id, + timestamp_begin: entry.timestamp - 1, + message_count: this._view_max_messages + }, {flagset: ["merge"]}).catch(error => { + this._view_older_messages.html_element.toggleClass('shown', false); + if(error instanceof CommandResult) { + if(error.id == ErrorID.CONVERSATION_MORE_DATA) { + this._view_older_messages.html_element.toggleClass('shown', true); + this.handle.update_chat_box(); + return; } } + //TODO log and handle! + log.error(LogCategory.CHAT, tr("Failed to fetch conversation history. %o"), error); + }).then(() => { + this.fix_scroll(true); + }); + } - delete_message(message: MessageData) { - //TODO A lot of checks! - //conversationmessagedelete cid=2 timestamp_begin= timestamp_end= cldbid= limit=1 - this.handle.handle.handle.serverConnection.send_command('conversationmessagedelete', { - cid: this.channel_id, - cldbid: message.sender_database_id, - - timestamp_begin: message.timestamp - 1, - timestamp_end: message.timestamp + 1, - - limit: 1 - }).then(() => { - return; /* in general it gets deleted via notify */ - }).catch(error => { - log.error(LogCategory.CHAT, tr("Failed to delete conversation message for conversation %o: %o"), this.channel_id, error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to delete message"), MessageHelper.formatMessage(tr("Failed to delete conversation message{:br:}Error: {}"), error)).open(); - }); - log.debug(LogCategory.CLIENT, tr("Deleting text message %o"), message); - } - - delete_messages(begin: number, end: number, sender: number, limit: number) { - let count = 0; - for(const message of this._view_entries.slice()) { - if(!('sender_database_id' in message)) - continue; - - const cmsg = message as Message; - if(end != 0 && cmsg.timestamp > end) - continue; - if(begin != 0 && cmsg.timestamp < begin) - break; - - if(cmsg.sender_database_id !== sender) - continue; - - this._delete_message(message); - if(--count >= limit) - return; + register_new_message(message: MessageData, update_view?: boolean) { + /* lets insert the message at the right index */ + let _new_message = false; + { + let spliced = false; + for(let index = 0; index < this._last_messages.length; index++) { + if(this._last_messages[index].timestamp < message.timestamp) { + this._last_messages.splice(index, 0, message); + spliced = true; + _new_message = index == 0; /* only set flag if this has been inserted at the front */ + break; + } else if(this._last_messages[index].timestamp == message.timestamp && this._last_messages[index].sender_database_id == message.sender_database_id) { + return; /* we already have that message */ } - - //TODO remove in cache? (_last_messages) } + if(!spliced && this._last_messages.length < this._view_max_messages) { + this._last_messages.push(message); + } + this._last_messages_timestamp = this._last_messages[0].timestamp; - private _delete_message(message: Message) { - if('html_element' in message) { - const cmessage = message as Message; - cmessage.html_element.remove(); - clearTimeout(cmessage.update_timer); - this._view_entries.remove(message as any); - } - - this._last_messages.remove(message); + while(this._last_messages.length > this._view_max_messages) { + if(this._last_messages[this._last_messages.length - 1] == this._first_unread_message) + break; + this._last_messages.pop(); } } - export class ConversationManager { - readonly handle: Frame; + /* message is within view */ + { + const entry = this._generate_view_message(message); - private _html_tag: JQuery; - private _chat_box: ChatBox; + let previous: ViewEntry; + for(let index = 0; index < this._view_entries.length; index++) { + const current_entry = this._view_entries[index]; + if(!('timestamp' in current_entry)) + continue; - private _container_conversation: JQuery; - - private _conversations: Conversation[] = []; - private _current_conversation: Conversation | undefined; - - private _needed_listener = () => this.update_chat_box(); - - constructor(handle: Frame) { - this.handle = handle; - - this._chat_box = new ChatBox(); - this._build_html_tag(); - - this._chat_box.callback_text = text => { - if(!this._current_conversation) - return; - - const conv = this._current_conversation; - this.handle.handle.serverConnection.send_command("sendtextmessage", {targetmode: conv.channel_id == 0 ? 3 : 2, cid: conv.channel_id, msg: text}, {process_result: false}).catch(error => { - conv.text_send_failed(error); - }); - }; - this.update_chat_box(); - } - - initialize_needed_listener() { - this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); - this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); - } - - html_tag() : JQuery { return this._html_tag; } - destroy() { - if(this.handle.handle.permissions) - this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); - this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); - this._needed_listener = undefined; - - this._chat_box && this._chat_box.destroy(); - this._chat_box = undefined; - - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; - this._container_conversation = undefined; - - for(const conversation of this._conversations) - conversation.destroy(); - this._conversations = []; - this._current_conversation = undefined; - } - - update_chat_box() { - let flag = true; - flag = flag && !!this._current_conversation; /* test if we have a conversation */ - flag = flag && !!this.handle.handle.permissions; /* test if we got permissions to test with */ - flag = flag && this.handle.handle.permissions.neededPermission(this._current_conversation.channel_id == 0 ? PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND : PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND).granted(1); - flag = flag && this._current_conversation.chat_available(); - this._chat_box.set_enabled(flag); - } - - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_channel").renderTag({ - chatbox: this._chat_box.html_tag() - }); - this._container_conversation = this._html_tag.find(".container-chat"); - this._chat_box.html_tag().on('focus', event => { - if(this._current_conversation) - this._current_conversation.mark_read(); - }); - this.update_input_format_helper(); - } - - set_current_channel(channel_id: number, update_info_frame?: boolean) { - if(this._current_conversation && this._current_conversation.channel_id === channel_id) - return; - - let conversation = this.conversation(channel_id); - this._current_conversation = conversation; - - if(this._current_conversation) { - this._container_conversation.children().detach(); - this._container_conversation.append(conversation.html_tag()); - this._current_conversation.fix_view_size(); - this._current_conversation.fix_scroll(false); - this.update_chat_box(); + if((current_entry as Message).timestamp < message.timestamp) { + this._view_entries.splice(index, 0, entry); + previous = current_entry; + break; } - if(typeof(update_info_frame) === "undefined" || update_info_frame) - this.handle.info_frame().update_channel_text(); + } + if(!previous) + this._view_entries.push(entry); + + if(previous) + entry.html_element.insertAfter(previous.html_element); + else + entry.html_element.insertAfter(this._view_older_messages.html_element); /* last element is already the current element */ + + if(_new_message && (typeof(this._scroll_position) === "number" || this.handle.current_channel() !== this.channel_id || this.handle.handle.content_type() !== FrameContent.CHANNEL_CHAT)) { + if(typeof(this._first_unread_message) === "undefined") + this._first_unread_message = entry; + + this._first_unread_message_pointer.html_element.insertBefore(entry.html_element); + this._container_messages.trigger('scroll'); /* updates the new message stuff */ + } + if(typeof(update_view) !== "boolean" || update_view) + this.fix_scroll(true); + } + + /* update chat state */ + this._container_no_permissions.hide(); + this._container_no_permissions_shown = false; + if(update_view) this.handle.update_chat_box(); + } + + /* using a timeout here to not cause a force style recalculation */ + private _scroll_fix_timer: number; + private _scroll_animate: boolean; + + fix_scroll(animate: boolean) { + if(this._scroll_fix_timer) { + this._scroll_animate = this._scroll_animate && animate; + return; + } + + this._scroll_fix_timer = setTimeout(() => { + this._scroll_fix_timer = undefined; + + let offset; + if(this._first_unread_message) { + offset = this._first_unread_message.html_element[0].offsetTop; + } else if(typeof(this._scroll_position) !== "undefined") { + offset = this._scroll_position; + } else { + offset = this._container_messages[0].scrollHeight; } - current_channel() : number { return this._current_conversation ? this._current_conversation.channel_id : 0; } - - /* Used by notifychanneldeleted */ - delete_conversation(channel_id: number) { - const entry = this._conversations.find(e => e.channel_id === channel_id); - if(!entry) - return; - - this._conversations.remove(entry); - entry.html_tag().detach(); - entry.destroy(); + if(this._scroll_animate) { + this._container_messages.stop(true).animate({ + scrollTop: offset + }, 'slow'); + } else { + this._container_messages.stop(true).scrollTop(offset); } + }, 5); + } - reset() { - while(this._conversations.length > 0) - this.delete_conversation(this._conversations[0].channel_id); + fix_view_size() { + this._view_older_messages.html_element.toggleClass('shown', !!this._has_older_messages); + + let count = 0; + for(let index = 0; index < this._view_entries.length; index++) { + if('timestamp' in this._view_entries[index]) + count++; + + if(count > this._view_max_messages) { + this._view_entries.splice(index, this._view_entries.length - index).forEach(e => { + clearTimeout(e.update_timer); + e.html_element.remove(); + }); + this._has_older_messages = true; + this._view_older_messages.html_element.toggleClass('shown', true); + break; } + } + } - conversation(channel_id: number, create?: boolean) : Conversation { - let conversation = this._conversations.find(e => e.channel_id === channel_id); + chat_available() : boolean { + return !this._container_no_permissions_shown && !this._container_is_private_shown && !this._container_no_support_shown; + } - if(!conversation && channel_id >= 0 && (typeof (create) === "undefined" || create)) { - conversation = new Conversation(this, channel_id); - this._conversations.push(conversation); - conversation.fetch_last_messages(); - } - return conversation; - } - - on_show() { - if(this._current_conversation) - this._current_conversation.fix_scroll(false); - } - - update_input_format_helper() { - const tag = this._html_tag.find(".container-format-helper"); - if(settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { - tag.removeClass("hidden").text(tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more...")); - } else { - tag.addClass("hidden"); + text_send_failed(error: CommandResult | any) { + log.warn(LogCategory.CHAT, "Failed to send text message! (%o)", error); + //TODO: Log if message send failed? + if(error instanceof CommandResult) { + if(error.id == ErrorID.PERMISSION_ERROR) { + //TODO: Split up between channel_text_message_send permission and no view permission + if(error.json["failed_permid"] == 0) { + this._container_no_permissions_shown = true; + this._container_no_permissions.show(); + this.handle.update_chat_box(); } } } } + + set_flag_private(flag: boolean) { + if(this._flag_private === flag) + return; + + this._flag_private = flag; + this.update_private_state(); + if(!flag) + this.fetch_last_messages(); + } + + update_private_state() { + if(!this._flag_private) { + this._container_is_private.hide(); + this._container_is_private_shown = false; + } else { + const client = this.handle.handle.handle.getClient(); + if(client && client.currentChannel() && client.currentChannel().channelId === this.channel_id) { + this._container_is_private_shown = false; + this._container_is_private.hide(); + } else { + this._container_is_private.show(); + this._container_is_private_shown = true; + } + } + } + + delete_message(message: MessageData) { + //TODO A lot of checks! + //conversationmessagedelete cid=2 timestamp_begin= timestamp_end= cldbid= limit=1 + this.handle.handle.handle.serverConnection.send_command('conversationmessagedelete', { + cid: this.channel_id, + cldbid: message.sender_database_id, + + timestamp_begin: message.timestamp - 1, + timestamp_end: message.timestamp + 1, + + limit: 1 + }).then(() => { + return; /* in general it gets deleted via notify */ + }).catch(error => { + log.error(LogCategory.CHAT, tr("Failed to delete conversation message for conversation %o: %o"), this.channel_id, error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to delete message"), formatMessage(tr("Failed to delete conversation message{:br:}Error: {}"), error)).open(); + }); + log.debug(LogCategory.CLIENT, tr("Deleting text message %o"), message); + } + + delete_messages(begin: number, end: number, sender: number, limit: number) { + let count = 0; + for(const message of this._view_entries.slice()) { + if(!('sender_database_id' in message)) + continue; + + const cmsg = message as Message; + if(end != 0 && cmsg.timestamp > end) + continue; + if(begin != 0 && cmsg.timestamp < begin) + break; + + if(cmsg.sender_database_id !== sender) + continue; + + this._delete_message(message); + if(--count >= limit) + return; + } + + //TODO remove in cache? (_last_messages) + } + + private _delete_message(message: Message) { + if('html_element' in message) { + const cmessage = message as Message; + cmessage.html_element.remove(); + clearTimeout(cmessage.update_timer); + this._view_entries.remove(message as any); + } + + this._last_messages.remove(message); + } +} + +export class ConversationManager { + readonly handle: Frame; + + private _html_tag: JQuery; + private _chat_box: ChatBox; + + private _container_conversation: JQuery; + + private _conversations: Conversation[] = []; + private _current_conversation: Conversation | undefined; + + private _needed_listener = () => this.update_chat_box(); + + constructor(handle: Frame) { + this.handle = handle; + + this._chat_box = new ChatBox(); + this._build_html_tag(); + + this._chat_box.callback_text = text => { + if(!this._current_conversation) + return; + + const conv = this._current_conversation; + this.handle.handle.serverConnection.send_command("sendtextmessage", {targetmode: conv.channel_id == 0 ? 3 : 2, cid: conv.channel_id, msg: text}, {process_result: false}).catch(error => { + conv.text_send_failed(error); + }); + }; + this.update_chat_box(); + } + + initialize_needed_listener() { + this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); + this.handle.handle.permissions.register_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); + } + + html_tag() : JQuery { return this._html_tag; } + destroy() { + if(this.handle.handle.permissions) + this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND, this._needed_listener); + this.handle.handle.permissions.unregister_needed_permission(PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, this._needed_listener); + this._needed_listener = undefined; + + this._chat_box && this._chat_box.destroy(); + this._chat_box = undefined; + + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + this._container_conversation = undefined; + + for(const conversation of this._conversations) + conversation.destroy(); + this._conversations = []; + this._current_conversation = undefined; + } + + update_chat_box() { + let flag = true; + flag = flag && !!this._current_conversation; /* test if we have a conversation */ + flag = flag && !!this.handle.handle.permissions; /* test if we got permissions to test with */ + flag = flag && this.handle.handle.permissions.neededPermission(this._current_conversation.channel_id == 0 ? PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND : PermissionType.B_CLIENT_CHANNEL_TEXTMESSAGE_SEND).granted(1); + flag = flag && this._current_conversation.chat_available(); + this._chat_box.set_enabled(flag); + } + + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_channel").renderTag({ + chatbox: this._chat_box.html_tag() + }); + this._container_conversation = this._html_tag.find(".container-chat"); + this._chat_box.html_tag().on('focus', event => { + if(this._current_conversation) + this._current_conversation.mark_read(); + }); + this.update_input_format_helper(); + } + + set_current_channel(channel_id: number, update_info_frame?: boolean) { + if(this._current_conversation && this._current_conversation.channel_id === channel_id) + return; + + let conversation = this.conversation(channel_id); + this._current_conversation = conversation; + + if(this._current_conversation) { + this._container_conversation.children().detach(); + this._container_conversation.append(conversation.html_tag()); + this._current_conversation.fix_view_size(); + this._current_conversation.fix_scroll(false); + this.update_chat_box(); + } + if(typeof(update_info_frame) === "undefined" || update_info_frame) + this.handle.info_frame().update_channel_text(); + } + + current_channel() : number { return this._current_conversation ? this._current_conversation.channel_id : 0; } + + /* Used by notifychanneldeleted */ + delete_conversation(channel_id: number) { + const entry = this._conversations.find(e => e.channel_id === channel_id); + if(!entry) + return; + + this._conversations.remove(entry); + entry.html_tag().detach(); + entry.destroy(); + } + + reset() { + while(this._conversations.length > 0) + this.delete_conversation(this._conversations[0].channel_id); + } + + conversation(channel_id: number, create?: boolean) : Conversation { + let conversation = this._conversations.find(e => e.channel_id === channel_id); + + if(!conversation && channel_id >= 0 && (typeof (create) === "undefined" || create)) { + conversation = new Conversation(this, channel_id); + this._conversations.push(conversation); + conversation.fetch_last_messages(); + } + return conversation; + } + + on_show() { + if(this._current_conversation) + this._current_conversation.fix_scroll(false); + } + + update_input_format_helper() { + const tag = this._html_tag.find(".container-format-helper"); + if(settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { + tag.removeClass("hidden").text(tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more...")); + } else { + tag.addClass("hidden"); + } + } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/music_info.ts b/shared/js/ui/frames/side/music_info.ts index 41cbebbc..4aafa4ed 100644 --- a/shared/js/ui/frames/side/music_info.ts +++ b/shared/js/ui/frames/side/music_info.ts @@ -1,849 +1,856 @@ -namespace chat { - import PlayerState = connection.voice.PlayerState; +import {Frame, FrameContent} from "tc-shared/ui/frames/chat_frame"; +import * as events from "tc-shared/events"; +import {MusicClientEntry, SongInfo} from "tc-shared/ui/client"; +import {voice} from "tc-shared/connection/ConnectionBase"; +import PlayerState = voice.PlayerState; +import {LogCategory} from "tc-shared/log"; +import {CommandResult, ErrorID, PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration"; +import {createErrorModal, createInputModal} from "tc-shared/ui/elements/Modal"; +import * as log from "tc-shared/log"; +import * as image_preview from "../image_preview"; - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - interface LoadedSongData { - description: string; - title: string; - url: string; +interface LoadedSongData { + description: string; + title: string; + url: string; - length: number; - thumbnail?: string; + length: number; + thumbnail?: string; - metadata: {[key: string]: string}; + metadata: {[key: string]: string}; +} + +export class MusicInfo { + readonly events: events.Registry; + readonly handle: Frame; + + private _html_tag: JQuery; + private _container_playlist: JQuery; + + private _current_bot: MusicClientEntry | undefined; + private update_song_info: number = 0; /* timestamp when we force update the info */ + private time_select: { + active: boolean, + max_time: number, + current_select_time: number, + current_player_time: number + } = { active: false, current_select_time: 0, max_time: 0, current_player_time: 0}; + private song_reorder: { + active: boolean, + song_id: number, + previous_entry: number, + html: JQuery, + mouse?: {x: number, y: number}, + indicator: JQuery + } = { active: false, song_id: 0, previous_entry: 0, html: undefined, indicator: $.spawn("div").addClass("reorder-indicator") }; + + previous_frame_content: FrameContent; + + constructor(handle: Frame) { + this.events = new events.Registry(); + this.handle = handle; + + this.events.enable_debug("music-info"); + this.initialize_listener(); + this._build_html_tag(); + + this.set_current_bot(undefined, true); } - export class MusicInfo { - readonly events: events.Registry; - readonly handle: Frame; + html_tag() : JQuery { + return this._html_tag; + } - private _html_tag: JQuery; - private _container_playlist: JQuery; + destroy() { + this.set_current_bot(undefined); + this.events.destory(); - private _current_bot: MusicClientEntry | undefined; - private update_song_info: number = 0; /* timestamp when we force update the info */ - private time_select: { - active: boolean, - max_time: number, - current_select_time: number, - current_player_time: number - } = { active: false, current_select_time: 0, max_time: 0, current_player_time: 0}; - private song_reorder: { - active: boolean, - song_id: number, - previous_entry: number, - html: JQuery, - mouse?: {x: number, y: number}, - indicator: JQuery - } = { active: false, song_id: 0, previous_entry: 0, html: undefined, indicator: $.spawn("div").addClass("reorder-indicator") }; + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; - previous_frame_content: FrameContent; + this._current_bot = undefined; + this.previous_frame_content = undefined; + } - constructor(handle: Frame) { - this.events = new events.Registry(); - this.handle = handle; + private format_time(value: number) { + if(value == 0) return "--:--:--"; - this.events.enable_debug("music-info"); - this.initialize_listener(); - this._build_html_tag(); + value /= 1000; - this.set_current_bot(undefined, true); + let hours = 0, minutes = 0; + while(value >= 60 * 60) { + hours++; + value -= 60 * 60; } - html_tag() : JQuery { - return this._html_tag; + while(value >= 60) { + minutes++; + value -= 60; } - destroy() { - this.set_current_bot(undefined); - this.events.destory(); + return ("0" + hours).substr(-2) + ":" + ("0" + minutes).substr(-2) + ":" + ("0" + value.toFixed(0)).substr(-2); + }; - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_music_info").renderTag(); + this._container_playlist = this._html_tag.find(".container-playlist"); - this._current_bot = undefined; - this.previous_frame_content = undefined; - } + this._html_tag.find(".button-close").on('click', () => { + if(this.previous_frame_content === FrameContent.CLIENT_INFO) + this.previous_frame_content = FrameContent.NONE; - private format_time(value: number) { - if(value == 0) return "--:--:--"; + this.handle.set_content(this.previous_frame_content); + }); - value /= 1000; + this._html_tag.find(".button-reload-playlist").on('click', () => this.events.fire("action_playlist_reload")); + this._html_tag.find(".button-song-add").on('click', () => this.events.fire("action_song_add")); + this._html_tag.find(".thumbnail").on('click', event => { + const image = this._html_tag.find(".thumbnail img"); + const url = image.attr("x-thumbnail-url"); + if(!url) return; - let hours = 0, minutes = 0; - while(value >= 60 * 60) { - hours++; - value -= 60 * 60; - } + image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); + }); - while(value >= 60) { - minutes++; - value -= 60; - } + { + const button_play = this._html_tag.find(".control-buttons .button-play"); + const button_pause = this._html_tag.find(".control-buttons .button-pause"); - return ("0" + hours).substr(-2) + ":" + ("0" + minutes).substr(-2) + ":" + ("0" + value.toFixed(0)).substr(-2); - }; + button_play.on('click', () => this.events.fire("action_play")); + button_pause.on('click', () => this.events.fire("action_pause")); - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_music_info").renderTag(); - this._container_playlist = this._html_tag.find(".container-playlist"); + this.events.on(["bot_change", "bot_property_update"], event => { + if(event.type === "bot_property_update" && event.as<"bot_property_update">().properties.indexOf("player_state") == -1) return; - this._html_tag.find(".button-close").on('click', () => { - if(this.previous_frame_content === FrameContent.CLIENT_INFO) - this.previous_frame_content = FrameContent.NONE; - - this.handle.set_content(this.previous_frame_content); + button_play.toggleClass("hidden", this._current_bot === undefined || this._current_bot.properties.player_state < PlayerState.STOPPING); }); - this._html_tag.find(".button-reload-playlist").on('click', () => this.events.fire("action_playlist_reload")); - this._html_tag.find(".button-song-add").on('click', () => this.events.fire("action_song_add")); - this._html_tag.find(".thumbnail").on('click', event => { - const image = this._html_tag.find(".thumbnail img"); - const url = image.attr("x-thumbnail-url"); - if(!url) return; + this.events.on(["bot_change", "bot_property_update"], event => { + if(event.type === "bot_property_update" && event.as<"bot_property_update">().properties.indexOf("player_state") == -1) return; - image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); + button_pause.toggleClass("hidden", this._current_bot !== undefined && this._current_bot.properties.player_state >= PlayerState.STOPPING); }); - { - const button_play = this._html_tag.find(".control-buttons .button-play"); - const button_pause = this._html_tag.find(".control-buttons .button-pause"); + this._html_tag.find(".control-buttons .button-rewind").on('click', () => this.events.fire("action_rewind")); + this._html_tag.find(".control-buttons .button-forward").on('click', () => this.events.fire("action_forward")); + } - button_play.on('click', () => this.events.fire("action_play")); - button_pause.on('click', () => this.events.fire("action_pause")); + /* timeline updaters */ + { + const container = this._html_tag.find(".container-timeline"); - this.events.on(["bot_change", "bot_property_update"], event => { - if(event.type === "bot_property_update" && event.as<"bot_property_update">().properties.indexOf("player_state") == -1) return; + const timeline = container.find(".timeline"); + const indicator_playtime = container.find(".indicator-playtime"); + const indicator_buffered = container.find(".indicator-buffered"); + const thumb = container.find(".thumb"); - button_play.toggleClass("hidden", this._current_bot === undefined || this._current_bot.properties.player_state < PlayerState.STOPPING); - }); + const timestamp_current = container.find(".timestamps .current"); + const timestamp_max = container.find(".timestamps .max"); - this.events.on(["bot_change", "bot_property_update"], event => { - if(event.type === "bot_property_update" && event.as<"bot_property_update">().properties.indexOf("player_state") == -1) return; + thumb.on('mousedown', event => event.button === 0 && this.events.fire("playtime_move_begin")); - button_pause.toggleClass("hidden", this._current_bot !== undefined && this._current_bot.properties.player_state >= PlayerState.STOPPING); - }); + this.events.on(["bot_change", "player_song_change", "player_time_update", "playtime_move_end"], event => { + if(!this._current_bot) { + this.time_select.max_time = 0; + indicator_buffered.each((_, e) => { e.style.width = "0%"; }); + indicator_playtime.each((_, e) => { e.style.width = "0%"; }); + thumb.each((_, e) => { e.style.marginLeft = "0%"; }); - this._html_tag.find(".control-buttons .button-rewind").on('click', () => this.events.fire("action_rewind")); - this._html_tag.find(".control-buttons .button-forward").on('click', () => this.events.fire("action_forward")); - } + timestamp_current.text("--:--:--"); + timestamp_max.text("--:--:--"); + return; + } + if(event.type === "playtime_move_end" && !event.as<"playtime_move_end">().canceled) return; - /* timeline updaters */ - { - const container = this._html_tag.find(".container-timeline"); - - const timeline = container.find(".timeline"); - const indicator_playtime = container.find(".indicator-playtime"); - const indicator_buffered = container.find(".indicator-buffered"); - const thumb = container.find(".thumb"); - - const timestamp_current = container.find(".timestamps .current"); - const timestamp_max = container.find(".timestamps .max"); - - thumb.on('mousedown', event => event.button === 0 && this.events.fire("playtime_move_begin")); - - this.events.on(["bot_change", "player_song_change", "player_time_update", "playtime_move_end"], event => { - if(!this._current_bot) { - this.time_select.max_time = 0; - indicator_buffered.each((_, e) => { e.style.width = "0%"; }); - indicator_playtime.each((_, e) => { e.style.width = "0%"; }); - thumb.each((_, e) => { e.style.marginLeft = "0%"; }); - - timestamp_current.text("--:--:--"); - timestamp_max.text("--:--:--"); - return; - } - if(event.type === "playtime_move_end" && !event.as<"playtime_move_end">().canceled) return; - - const update_info = Date.now() > this.update_song_info; - this._current_bot.requestPlayerInfo(update_info ? 1000 : 60 * 1000).then(data => { - if(update_info) - this.display_song_info(data); - - let played, buffered; - if(event.type !== "player_time_update") { - played = data.player_replay_index; - buffered = data.player_buffered_index; - } else { - played = event.as<"player_time_update">().player_replay_index; - buffered = event.as<"player_time_update">().player_buffered_index; - } - - this.time_select.current_player_time = played; - this.time_select.max_time = data.player_max_index; - timestamp_max.text(data.player_max_index ? this.format_time(data.player_max_index) : "--:--:--"); - - if(this.time_select.active) - return; - - let wplayed, wbuffered; - if(data.player_max_index) { - wplayed = (played * 100 / data.player_max_index).toFixed(2) + "%"; - wbuffered = (buffered * 100 / data.player_max_index).toFixed(2) + "%"; - - timestamp_current.text(this.format_time(played)); - } else { - wplayed = "100%"; - wbuffered = "100%"; - - timestamp_current.text(this.format_time(played)); - } - - indicator_buffered.each((_, e) => { e.style.width = wbuffered; }); - indicator_playtime.each((_, e) => { e.style.width = wplayed; }); - thumb.each((_, e) => { e.style.marginLeft = wplayed; }); - }); - }); - - const move_callback = (event: MouseEvent) => { - const x_min = timeline.offset().left; - const x_max = x_min + timeline.width(); - - let current = event.pageX; - if(current < x_min) - current = x_min; - else if(current > x_max) - current = x_max; - - const percent = (current - x_min) / (x_max - x_min); - this.time_select.current_select_time = percent * this.time_select.max_time; - timestamp_current.text(this.format_time(this.time_select.current_select_time)); - - const w = (percent * 100).toFixed(2) + "%"; - indicator_playtime.each((_, e) => { e.style.width = w; }); - thumb.each((_, e) => { e.style.marginLeft = w; }); - }; - - const up_callback = (event: MouseEvent | FocusEvent) => { - if(event.type === "mouseup") - if((event as MouseEvent).button !== 0) return; - - this.events.fire("playtime_move_end", { - canceled: event.type !== "mouseup", - target_time: this.time_select.current_select_time - }); - }; - - this.events.on("playtime_move_begin", event => { - if(this.time_select.max_time <= 0) return; - - this.time_select.active = true; - indicator_buffered.each((_, e) => { e.style.width = "0"; }); - document.addEventListener("mousemove", move_callback); - document.addEventListener("mouseleave", up_callback); - document.addEventListener("blur", up_callback); - document.addEventListener("mouseup", up_callback); - document.body.style.userSelect = "none"; - }); - - this.events.on(["bot_change", "player_song_change", "playtime_move_end"], event => { - document.removeEventListener("mousemove", move_callback); - document.removeEventListener("mouseleave", up_callback); - document.removeEventListener("blur", up_callback); - document.removeEventListener("mouseup", up_callback); - document.body.style.userSelect = undefined; - this.time_select.active = false; - - if(event.type === "playtime_move_end") { - const data = event.as<"playtime_move_end">(); - if(data.canceled) return; - - const offset = data.target_time - this.time_select.current_player_time; - this.events.fire(offset > 0 ? "action_forward_ms" : "action_rewind_ms", {units: Math.abs(offset) }); - } - }); - } - - /* song info handlers */ - this.events.on(["bot_change", "player_song_change"], event => { - let song: SongInfo; - - /* update the player info so we dont get old data */ - if(this._current_bot) { - this.update_song_info = 0; - this._current_bot.requestPlayerInfo(1000).then(data => { + const update_info = Date.now() > this.update_song_info; + this._current_bot.requestPlayerInfo(update_info ? 1000 : 60 * 1000).then(data => { + if(update_info) this.display_song_info(data); - }).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to update current song for side bar: %o"), error); - }); - } - if(event.type === "bot_change") { - song = undefined; - } else { - song = event.as<"player_song_change">().song; - } - this.display_song_info(song); - }); - } - - private display_song_info(song: SongInfo) { - if(song) { - if(!song.song_loaded) { - console.log("Awaiting a loaded song info."); - this.update_song_info = 0; - } else { - console.log("Song info loaded."); - this.update_song_info = Date.now() + 60 * 1000; - } - } - - if(!song) song = new SongInfo(); - - const container_thumbnail = this._html_tag.find(".player .container-thumbnail"); - const container_info = this._html_tag.find(".player .container-song-info"); - - container_thumbnail.find("img") - .attr("src", song.song_thumbnail || "img/music/no-thumbnail.png") - .attr("x-thumbnail-url", encodeURIComponent(song.song_thumbnail)) - .css("cursor", song.song_thumbnail ? "pointer" : null); - - if(song.song_id) - container_info.find(".song-name").text(song.song_title || song.song_url).attr("title", song.song_title || song.song_url); - else - container_info.find(".song-name").text(tr("No song selected")); - if(song.song_description) { - container_info.find(".song-description").removeClass("hidden").text(song.song_description).attr("title", song.song_description); - } else { - container_info.find(".song-description").addClass("hidden").text(tr("Song has no description")).attr("title", tr("Song has no description")); - } - } - - private initialize_listener() { - //Must come at first! - this.events.on("player_song_change", event => { - if(!this._current_bot) return; - - this._current_bot.requestPlayerInfo(0); /* enforce an info refresh */ - }); - - /* bot property listener */ - const callback_property = event => this.events.fire("bot_property_update", { properties: event.properties }); - const callback_time_update = event => this.events.fire("player_time_update", event); - const callback_song_change = event => this.events.fire("player_song_change", event); - this.events.on("bot_change", event => { - if(event.old) { - event.old.events.off(callback_property); - event.old.events.off(callback_time_update); - event.old.events.off(callback_song_change); - event.old.events.disconnect_all(this.events); - } - if(event.new) { - event.new.events.on("property_update", callback_property); - - event.new.events.on("music_status_update", callback_time_update); - event.new.events.on("music_song_change", callback_song_change); - - event.new.events.connect("playlist_song_add", this.events); - event.new.events.connect("playlist_song_remove", this.events); - event.new.events.connect("playlist_song_reorder", this.events); - event.new.events.connect("playlist_song_loaded", this.events); - } - }); - - /* basic player actions */ - { - const action_map = { - "action_play": 1, - "action_pause": 2, - "action_forward": 3, - "action_rewind": 4, - "action_forward_ms": 5, - "action_rewind_ms": 6 - }; - - this.events.on(Object.keys(action_map) as any, event => { - if(!this._current_bot) return; - - const action_id = action_map[event.type]; - if(typeof action_id === "undefined") { - log.warn(LogCategory.GENERAL, tr("Invalid music bot action event detected: %s. This should not happen!"), event.type); - return; + let played, buffered; + if(event.type !== "player_time_update") { + played = data.player_replay_index; + buffered = data.player_buffered_index; + } else { + played = event.as<"player_time_update">().player_replay_index; + buffered = event.as<"player_time_update">().player_buffered_index; } - const data = { - bot_id: this._current_bot.properties.client_database_id, - action: action_id, - units: event.units - }; - this.handle.handle.serverConnection.send_command("musicbotplayeraction", data).catch(error => { - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; - log.error(LogCategory.CLIENT, tr("Failed to perform action %s on bot: %o"), event.type, error); - //TODO: Better error dialog - createErrorModal(tr("Failed to perform action."), tr("Failed to perform action for music bot.")).open(); - }); + this.time_select.current_player_time = played; + this.time_select.max_time = data.player_max_index; + timestamp_max.text(data.player_max_index ? this.format_time(data.player_max_index) : "--:--:--"); + + if(this.time_select.active) + return; + + let wplayed, wbuffered; + if(data.player_max_index) { + wplayed = (played * 100 / data.player_max_index).toFixed(2) + "%"; + wbuffered = (buffered * 100 / data.player_max_index).toFixed(2) + "%"; + + timestamp_current.text(this.format_time(played)); + } else { + wplayed = "100%"; + wbuffered = "100%"; + + timestamp_current.text(this.format_time(played)); + } + + indicator_buffered.each((_, e) => { e.style.width = wbuffered; }); + indicator_playtime.each((_, e) => { e.style.width = wplayed; }); + thumb.each((_, e) => { e.style.marginLeft = wplayed; }); + }); + }); + + const move_callback = (event: MouseEvent) => { + const x_min = timeline.offset().left; + const x_max = x_min + timeline.width(); + + let current = event.pageX; + if(current < x_min) + current = x_min; + else if(current > x_max) + current = x_max; + + const percent = (current - x_min) / (x_max - x_min); + this.time_select.current_select_time = percent * this.time_select.max_time; + timestamp_current.text(this.format_time(this.time_select.current_select_time)); + + const w = (percent * 100).toFixed(2) + "%"; + indicator_playtime.each((_, e) => { e.style.width = w; }); + thumb.each((_, e) => { e.style.marginLeft = w; }); + }; + + const up_callback = (event: MouseEvent | FocusEvent) => { + if(event.type === "mouseup") + if((event as MouseEvent).button !== 0) return; + + this.events.fire("playtime_move_end", { + canceled: event.type !== "mouseup", + target_time: this.time_select.current_select_time + }); + }; + + this.events.on("playtime_move_begin", event => { + if(this.time_select.max_time <= 0) return; + + this.time_select.active = true; + indicator_buffered.each((_, e) => { e.style.width = "0"; }); + document.addEventListener("mousemove", move_callback); + document.addEventListener("mouseleave", up_callback); + document.addEventListener("blur", up_callback); + document.addEventListener("mouseup", up_callback); + document.body.style.userSelect = "none"; + }); + + this.events.on(["bot_change", "player_song_change", "playtime_move_end"], event => { + document.removeEventListener("mousemove", move_callback); + document.removeEventListener("mouseleave", up_callback); + document.removeEventListener("blur", up_callback); + document.removeEventListener("mouseup", up_callback); + document.body.style.userSelect = undefined; + this.time_select.active = false; + + if(event.type === "playtime_move_end") { + const data = event.as<"playtime_move_end">(); + if(data.canceled) return; + + const offset = data.target_time - this.time_select.current_player_time; + this.events.fire(offset > 0 ? "action_forward_ms" : "action_rewind_ms", {units: Math.abs(offset) }); + } + }); + } + + /* song info handlers */ + this.events.on(["bot_change", "player_song_change"], event => { + let song: SongInfo; + + /* update the player info so we dont get old data */ + if(this._current_bot) { + this.update_song_info = 0; + this._current_bot.requestPlayerInfo(1000).then(data => { + this.display_song_info(data); + }).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to update current song for side bar: %o"), error); }); } - this.events.on("action_song_set", event => { + if(event.type === "bot_change") { + song = undefined; + } else { + song = event.as<"player_song_change">().song; + } + this.display_song_info(song); + }); + } + + private display_song_info(song: SongInfo) { + if(song) { + if(!song.song_loaded) { + console.log("Awaiting a loaded song info."); + this.update_song_info = 0; + } else { + console.log("Song info loaded."); + this.update_song_info = Date.now() + 60 * 1000; + } + } + + if(!song) song = new SongInfo(); + + const container_thumbnail = this._html_tag.find(".player .container-thumbnail"); + const container_info = this._html_tag.find(".player .container-song-info"); + + container_thumbnail.find("img") + .attr("src", song.song_thumbnail || "img/music/no-thumbnail.png") + .attr("x-thumbnail-url", encodeURIComponent(song.song_thumbnail)) + .css("cursor", song.song_thumbnail ? "pointer" : null); + + if(song.song_id) + container_info.find(".song-name").text(song.song_title || song.song_url).attr("title", song.song_title || song.song_url); + else + container_info.find(".song-name").text(tr("No song selected")); + if(song.song_description) { + container_info.find(".song-description").removeClass("hidden").text(song.song_description).attr("title", song.song_description); + } else { + container_info.find(".song-description").addClass("hidden").text(tr("Song has no description")).attr("title", tr("Song has no description")); + } + } + + private initialize_listener() { + //Must come at first! + this.events.on("player_song_change", event => { + if(!this._current_bot) return; + + this._current_bot.requestPlayerInfo(0); /* enforce an info refresh */ + }); + + /* bot property listener */ + const callback_property = event => this.events.fire("bot_property_update", { properties: event.properties }); + const callback_time_update = event => this.events.fire("player_time_update", event); + const callback_song_change = event => this.events.fire("player_song_change", event); + this.events.on("bot_change", event => { + if(event.old) { + event.old.events.off(callback_property); + event.old.events.off(callback_time_update); + event.old.events.off(callback_song_change); + event.old.events.disconnect_all(this.events); + } + if(event.new) { + event.new.events.on("property_update", callback_property); + + event.new.events.on("music_status_update", callback_time_update); + event.new.events.on("music_song_change", callback_song_change); + + event.new.events.connect("playlist_song_add", this.events); + event.new.events.connect("playlist_song_remove", this.events); + event.new.events.connect("playlist_song_reorder", this.events); + event.new.events.connect("playlist_song_loaded", this.events); + } + }); + + /* basic player actions */ + { + const action_map = { + "action_play": 1, + "action_pause": 2, + "action_forward": 3, + "action_rewind": 4, + "action_forward_ms": 5, + "action_rewind_ms": 6 + }; + + this.events.on(Object.keys(action_map) as any, event => { if(!this._current_bot) return; + const action_id = action_map[event.type]; + if(typeof action_id === "undefined") { + log.warn(LogCategory.GENERAL, tr("Invalid music bot action event detected: %s. This should not happen!"), event.type); + return; + } + const data = { + bot_id: this._current_bot.properties.client_database_id, + action: action_id, + units: event.units + }; + this.handle.handle.serverConnection.send_command("musicbotplayeraction", data).catch(error => { + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; + + log.error(LogCategory.CLIENT, tr("Failed to perform action %s on bot: %o"), event.type, error); + //TODO: Better error dialog + createErrorModal(tr("Failed to perform action."), tr("Failed to perform action for music bot.")).open(); + }); + }); + } + + this.events.on("action_song_set", event => { + if(!this._current_bot) return; + + const connection = this.handle.handle.serverConnection; + if(!connection || !connection.connected()) return; + + connection.send_command("playlistsongsetcurrent", { + playlist_id: this._current_bot.properties.client_playlist_id, + song_id: event.song_id + }).catch(error => { + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; + + log.error(LogCategory.CLIENT, tr("Failed to set current song on bot: %o"), event.type, error); + //TODO: Better error dialog + createErrorModal(tr("Failed to set song."), tr("Failed to set current replaying song.")).open(); + }) + }); + + this.events.on("action_song_add", () => { + if(!this._current_bot) return; + + createInputModal(tr("Enter song URL"), tr("Please enter the target song URL"), text => { + try { + new URL(text); + return true; + } catch(error) { + return false; + } + }, result => { + if(!result || !this._current_bot) return; + const connection = this.handle.handle.serverConnection; - if(!connection || !connection.connected()) return; - - connection.send_command("playlistsongsetcurrent", { + connection.send_command("playlistsongadd", { playlist_id: this._current_bot.properties.client_playlist_id, - song_id: event.song_id + url: result }).catch(error => { if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; - log.error(LogCategory.CLIENT, tr("Failed to set current song on bot: %o"), event.type, error); - //TODO: Better error dialog - createErrorModal(tr("Failed to set song."), tr("Failed to set current replaying song.")).open(); - }) + log.error(LogCategory.CLIENT, tr("Failed to add song to bot playlist: %o"), error); + + //TODO: Better error description + createErrorModal(tr("Failed to insert song"), tr("Failed to append song to the playlist.")).open(); + }); + }).open(); + }); + + this.events.on("action_song_delete", event => { + if(!this._current_bot) return; + + const connection = this.handle.handle.serverConnection; + if(!connection || !connection.connected()) return; + + connection.send_command("playlistsongremove", { + playlist_id: this._current_bot.properties.client_playlist_id, + song_id: event.song_id + }).catch(error => { + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; + + log.error(LogCategory.CLIENT, tr("Failed to delete song from bot playlist: %o"), error); + + //TODO: Better error description + createErrorModal(tr("Failed to delete song"), tr("Failed to remove song from the playlist.")).open(); + }); + }); + + /* bot subscription */ + this.events.on("bot_change", () => { + const connection = this.handle.handle.serverConnection; + if(!connection || !connection.connected()) return; + + const bot_id = this._current_bot ? this._current_bot.properties.client_database_id : 0; + this.handle.handle.serverConnection.send_command("musicbotsetsubscription", { bot_id: bot_id }).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to subscribe to displayed bot within the side bar: %o"), error); + }); + }); + + /* playlist stuff */ + this.events.on(["bot_change", "action_playlist_reload"], event => { + this.playlist_subscribe(true); + this.update_playlist(); + }); + + this.events.on("playlist_song_add", event => { + const animation = typeof event.insert_effect === "boolean" ? event.insert_effect : true; + const html_entry = this.build_playlist_entry(event.song, animation); + const playlist = this._container_playlist.find(".playlist"); + const previous = playlist.find(".entry[song-id=" + event.song.song_previous_song_id + "]"); + + if(previous.length) + html_entry.insertAfter(previous); + else + html_entry.appendTo(playlist); + if(event.song.song_loaded) + this.events.fire("playlist_song_loaded", { + html_entry: html_entry, + metadata: event.song.song_metadata, + success: true, + song_id: event.song.song_id + }); + if(animation) + setTimeout(() => html_entry.addClass("shown"), 50); + }); + + this.events.on("playlist_song_remove", event => { + const playlist = this._container_playlist.find(".playlist"); + const song = playlist.find(".entry[song-id=" + event.song_id + "]"); + song.addClass("deleted"); + setTimeout(() => song.remove(), 5000); /* to play some animations */ + }); + + this.events.on("playlist_song_reorder", event => { + const playlist = this._container_playlist.find(".playlist"); + const entry = playlist.find(".entry[song-id=" + event.song_id + "]"); + if(!entry) return; + + console.log(event); + const previous = playlist.find(".entry[song-id=" + event.previous_song_id + "]"); + if(previous.length) { + entry.insertAfter(previous); + } else { + entry.insertBefore(playlist.find(".entry")[0]); + } + }); + + this.events.on("playlist_song_loaded", event => { + const entry = event.html_entry || this._container_playlist.find(".playlist .entry[song-id=" + event.song_id + "]"); + + const thumbnail = entry.find(".container-thumbnail img"); + const name = entry.find(".name"); + const description = entry.find(".description"); + const length = entry.find(".length"); + + if(event.success) { + let meta: LoadedSongData; + try { + meta = JSON.parse(event.metadata); + } catch(error) { + log.warn(LogCategory.CLIENT, tr("Failed to decode song metadata")); + meta = { + description: "", + title: "", + metadata: {}, + length: 0, + url: entry.attr("song-url") + } + } + + if(!meta.title && meta.description) { + meta.title = meta.description.split("\n")[0]; + meta.description = meta.description.split("\n").slice(1).join("\n"); + } + meta.title = meta.title || meta.url; + + name.text(meta.title); + description.text(meta.description); + length.text(this.format_time(meta.length || 0)); + if(meta.thumbnail) { + thumbnail.attr("src", meta.thumbnail) + .attr("x-thumbnail-url", encodeURIComponent(meta.thumbnail)); + } + } else { + name.text(tr("failed to load ") + entry.attr("song-url")).attr("title", tr("failed to load ") + entry.attr("song-url")); + description.text(event.error_msg || tr("unknown error")).attr("title", event.error_msg || tr("unknown error")); + } + }); + + /* song reorder */ + { + const move_callback = (event: MouseEvent) => { + if(!this.song_reorder.html) return; + + this.song_reorder.html.each((_, e) => { + e.style.left = (event.pageX - this.song_reorder.mouse.x) + "px"; + e.style.top = (event.pageY - this.song_reorder.mouse.y) + "px"; + }); + + const entries = this._container_playlist.find(".playlist .entry"); + let before: HTMLElement; + for(const entry of entries) { + const off = $(entry).offset().top; + if(off > event.pageY) { + this.song_reorder.indicator.insertBefore(entry); + this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; + return; + } + + before = entry; + } + this.song_reorder.indicator.insertAfter(entries.last()); + this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; + }; + + const up_callback = (event: MouseEvent | FocusEvent) => { + if(event.type === "mouseup") + if((event as MouseEvent).button !== 0) return; + + this.events.fire("reorder_end", { + canceled: event.type !== "mouseup", + song_id: this.song_reorder.song_id, + entry: this.song_reorder.html, + previous_entry: this.song_reorder.previous_entry + }); + }; + + this.events.on("reorder_begin", event => { + this.song_reorder.song_id = event.song_id; + this.song_reorder.html = event.entry; + + const width = this.song_reorder.html.width() + "px"; + this.song_reorder.html.each((_, e) => { e.style.width = width; }); + this.song_reorder.active = true; + this.song_reorder.html.addClass("reordering"); + + document.addEventListener("mousemove", move_callback); + document.addEventListener("mouseleave", up_callback); + document.addEventListener("blur", up_callback); + document.addEventListener("mouseup", up_callback); + document.body.style.userSelect = "none"; }); - this.events.on("action_song_add", () => { - if(!this._current_bot) return; + this.events.on(["bot_change", "playlist_song_remove", "reorder_end"], event => { + if(event.type === "playlist_song_remove" && event.as<"playlist_song_remove">().song_id !== this.song_reorder.song_id) return; - createInputModal(tr("Enter song URL"), tr("Please enter the target song URL"), text => { - try { - new URL(text); - return true; - } catch(error) { - return false; - } - }, result => { - if(!result || !this._current_bot) return; + document.removeEventListener("mousemove", move_callback); + document.removeEventListener("mouseleave", up_callback); + document.removeEventListener("blur", up_callback); + document.removeEventListener("mouseup", up_callback); + document.body.style.userSelect = undefined; + + this.song_reorder.active = false; + this.song_reorder.indicator.remove(); + if(this.song_reorder.html) { + this.song_reorder.html.each((_, e) => { + e.style.width = null; + e.style.left = null; + e.style.top = null; + }); + this.song_reorder.html.removeClass("reordering"); + } + + if(event.type === "reorder_end") { + const data = event.as<"reorder_end">(); + if(data.canceled) return; const connection = this.handle.handle.serverConnection; - connection.send_command("playlistsongadd", { + if(!connection || !connection.connected()) return; + if(!this._current_bot) return; + + connection.send_command("playlistsongreorder", { playlist_id: this._current_bot.properties.client_playlist_id, - url: result + song_id: data.song_id, + song_previous_song_id: data.previous_entry }).catch(error => { if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; log.error(LogCategory.CLIENT, tr("Failed to add song to bot playlist: %o"), error); //TODO: Better error description - createErrorModal(tr("Failed to insert song"), tr("Failed to append song to the playlist.")).open(); + createErrorModal(tr("Failed to reorder song"), tr("Failed to reorder song within the playlist.")).open(); }); - }).open(); - }); - - this.events.on("action_song_delete", event => { - if(!this._current_bot) return; - - const connection = this.handle.handle.serverConnection; - if(!connection || !connection.connected()) return; - - connection.send_command("playlistsongremove", { - playlist_id: this._current_bot.properties.client_playlist_id, - song_id: event.song_id - }).catch(error => { - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; - - log.error(LogCategory.CLIENT, tr("Failed to delete song from bot playlist: %o"), error); - - //TODO: Better error description - createErrorModal(tr("Failed to delete song"), tr("Failed to remove song from the playlist.")).open(); - }); - }); - - /* bot subscription */ - this.events.on("bot_change", () => { - const connection = this.handle.handle.serverConnection; - if(!connection || !connection.connected()) return; - - const bot_id = this._current_bot ? this._current_bot.properties.client_database_id : 0; - this.handle.handle.serverConnection.send_command("musicbotsetsubscription", { bot_id: bot_id }).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to subscribe to displayed bot within the side bar: %o"), error); - }); - }); - - /* playlist stuff */ - this.events.on(["bot_change", "action_playlist_reload"], event => { - this.playlist_subscribe(true); - this.update_playlist(); - }); - - this.events.on("playlist_song_add", event => { - const animation = typeof event.insert_effect === "boolean" ? event.insert_effect : true; - const html_entry = this.build_playlist_entry(event.song, animation); - const playlist = this._container_playlist.find(".playlist"); - const previous = playlist.find(".entry[song-id=" + event.song.song_previous_song_id + "]"); - - if(previous.length) - html_entry.insertAfter(previous); - else - html_entry.appendTo(playlist); - if(event.song.song_loaded) - this.events.fire("playlist_song_loaded", { - html_entry: html_entry, - metadata: event.song.song_metadata, - success: true, - song_id: event.song.song_id - }); - if(animation) - setTimeout(() => html_entry.addClass("shown"), 50); - }); - - this.events.on("playlist_song_remove", event => { - const playlist = this._container_playlist.find(".playlist"); - const song = playlist.find(".entry[song-id=" + event.song_id + "]"); - song.addClass("deleted"); - setTimeout(() => song.remove(), 5000); /* to play some animations */ - }); - - this.events.on("playlist_song_reorder", event => { - const playlist = this._container_playlist.find(".playlist"); - const entry = playlist.find(".entry[song-id=" + event.song_id + "]"); - if(!entry) return; - - console.log(event); - const previous = playlist.find(".entry[song-id=" + event.previous_song_id + "]"); - if(previous.length) { - entry.insertAfter(previous); - } else { - entry.insertBefore(playlist.find(".entry")[0]); + console.log("Reorder to %d", data.previous_entry); } }); - this.events.on("playlist_song_loaded", event => { - const entry = event.html_entry || this._container_playlist.find(".playlist .entry[song-id=" + event.song_id + "]"); - - const thumbnail = entry.find(".container-thumbnail img"); - const name = entry.find(".name"); - const description = entry.find(".description"); - const length = entry.find(".length"); - - if(event.success) { - let meta: LoadedSongData; - try { - meta = JSON.parse(event.metadata); - } catch(error) { - log.warn(LogCategory.CLIENT, tr("Failed to decode song metadata")); - meta = { - description: "", - title: "", - metadata: {}, - length: 0, - url: entry.attr("song-url") - } - } - - if(!meta.title && meta.description) { - meta.title = meta.description.split("\n")[0]; - meta.description = meta.description.split("\n").slice(1).join("\n"); - } - meta.title = meta.title || meta.url; - - name.text(meta.title); - description.text(meta.description); - length.text(this.format_time(meta.length || 0)); - if(meta.thumbnail) { - thumbnail.attr("src", meta.thumbnail) - .attr("x-thumbnail-url", encodeURIComponent(meta.thumbnail)); - } - } else { - name.text(tr("failed to load ") + entry.attr("song-url")).attr("title", tr("failed to load ") + entry.attr("song-url")); - description.text(event.error_msg || tr("unknown error")).attr("title", event.error_msg || tr("unknown error")); + this.events.on(["bot_change", "player_song_change"], event => { + if(!this._current_bot) { + this._html_tag.find(".playlist .current-song").removeClass("current-song"); + return; } - }); - /* song reorder */ - { - const move_callback = (event: MouseEvent) => { - if(!this.song_reorder.html) return; - - this.song_reorder.html.each((_, e) => { - e.style.left = (event.pageX - this.song_reorder.mouse.x) + "px"; - e.style.top = (event.pageY - this.song_reorder.mouse.y) + "px"; - }); - - const entries = this._container_playlist.find(".playlist .entry"); - let before: HTMLElement; - for(const entry of entries) { - const off = $(entry).offset().top; - if(off > event.pageY) { - this.song_reorder.indicator.insertBefore(entry); - this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; - return; - } - - before = entry; - } - this.song_reorder.indicator.insertAfter(entries.last()); - this.song_reorder.previous_entry = before ? parseInt(before.attributes.getNamedItem("song-id").value) : 0; - }; - - const up_callback = (event: MouseEvent | FocusEvent) => { - if(event.type === "mouseup") - if((event as MouseEvent).button !== 0) return; - - this.events.fire("reorder_end", { - canceled: event.type !== "mouseup", - song_id: this.song_reorder.song_id, - entry: this.song_reorder.html, - previous_entry: this.song_reorder.previous_entry - }); - }; - - this.events.on("reorder_begin", event => { - this.song_reorder.song_id = event.song_id; - this.song_reorder.html = event.entry; - - const width = this.song_reorder.html.width() + "px"; - this.song_reorder.html.each((_, e) => { e.style.width = width; }); - this.song_reorder.active = true; - this.song_reorder.html.addClass("reordering"); - - document.addEventListener("mousemove", move_callback); - document.addEventListener("mouseleave", up_callback); - document.addEventListener("blur", up_callback); - document.addEventListener("mouseup", up_callback); - document.body.style.userSelect = "none"; - }); - - this.events.on(["bot_change", "playlist_song_remove", "reorder_end"], event => { - if(event.type === "playlist_song_remove" && event.as<"playlist_song_remove">().song_id !== this.song_reorder.song_id) return; - - document.removeEventListener("mousemove", move_callback); - document.removeEventListener("mouseleave", up_callback); - document.removeEventListener("blur", up_callback); - document.removeEventListener("mouseup", up_callback); - document.body.style.userSelect = undefined; - - this.song_reorder.active = false; - this.song_reorder.indicator.remove(); - if(this.song_reorder.html) { - this.song_reorder.html.each((_, e) => { - e.style.width = null; - e.style.left = null; - e.style.top = null; - }); - this.song_reorder.html.removeClass("reordering"); - } - - if(event.type === "reorder_end") { - const data = event.as<"reorder_end">(); - if(data.canceled) return; - - const connection = this.handle.handle.serverConnection; - if(!connection || !connection.connected()) return; - if(!this._current_bot) return; - - connection.send_command("playlistsongreorder", { - playlist_id: this._current_bot.properties.client_playlist_id, - song_id: data.song_id, - song_previous_song_id: data.previous_entry - }).catch(error => { - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; - - log.error(LogCategory.CLIENT, tr("Failed to add song to bot playlist: %o"), error); - - //TODO: Better error description - createErrorModal(tr("Failed to reorder song"), tr("Failed to reorder song within the playlist.")).open(); - }); - console.log("Reorder to %d", data.previous_entry); - } - }); - - this.events.on(["bot_change", "player_song_change"], event => { - if(!this._current_bot) { - this._html_tag.find(".playlist .current-song").removeClass("current-song"); - return; - } - - this._current_bot.requestPlayerInfo(1000).then(data => { - const song_id = data ? data.song_id : 0; - this._html_tag.find(".playlist .current-song").removeClass("current-song"); - this._html_tag.find(".playlist .entry[song-id=" + song_id + "]").addClass("current-song"); - }); - }); - } - } - - set_current_bot(client: MusicClientEntry | undefined, enforce?: boolean) { - if(client) client.updateClientVariables(); /* just to ensure */ - if(client === this._current_bot && (typeof(enforce) === "undefined" || !enforce)) - return; - - const old = this._current_bot; - this._current_bot = client; - this.events.fire("bot_change", { - new: client, - old: old - }); - } - - current_bot() : MusicClientEntry | undefined { - return this._current_bot; - } - - private sort_songs(data: PlaylistSong[]) { - const result = []; - - let appendable: PlaylistSong[] = []; - for(const song of data) { - if(song.song_id == 0 || data.findIndex(e => e.song_id === song.song_previous_song_id) == -1) - result.push(song); - else - appendable.push(song); - } - - let iters; - while (appendable.length) { - do { - iters = 0; - const left: PlaylistSong[] = []; - for(const song of appendable) { - const index = data.findIndex(e => e.song_id === song.song_previous_song_id); - if(index == -1) { - left.push(song); - continue; - } - - result.splice(index + 1, 0, song); - iters++; - } - appendable = left; - } while(iters > 0); - - if(appendable.length) - result.push(appendable.pop_front()); - } - - return result; - } - - /* playlist stuff */ - update_playlist() { - this.playlist_subscribe(true); - - this._container_playlist.find(".overlay").toggleClass("hidden", true); - const playlist = this._container_playlist.find(".playlist"); - playlist.empty(); - - if(!this.handle.handle.serverConnection || !this.handle.handle.serverConnection.connected() || !this._current_bot) { - this._container_playlist.find(".overlay-empty").removeClass("hidden"); - return; - } - - const overlay_loading = this._container_playlist.find(".overlay-loading"); - overlay_loading.removeClass("hidden"); - - this._current_bot.updateClientVariables(true).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to update music bot variables: %o"), error); - }).then(() => { - this.handle.handle.serverConnection.command_helper.request_playlist_songs(this._current_bot.properties.client_playlist_id).then(songs => { - this.playlist_subscribe(false); /* we're allowed to see the playlist */ - if(!songs) { - this._container_playlist.find(".overlay-empty").removeClass("hidden"); - return; - } - - for(const song of this.sort_songs(songs)) - this.events.fire("playlist_song_add", { song: song, insert_effect: false }); - }).catch(error => { - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { - this._container_playlist.find(".overlay-no-permissions").removeClass("hidden"); - return; - } - log.error(LogCategory.CLIENT, tr("Failed to load bot playlist: %o"), error); - this._container_playlist.find(".overlay.overlay-error").removeClass("hidden"); - }).then(() => { - overlay_loading.addClass("hidden"); + this._current_bot.requestPlayerInfo(1000).then(data => { + const song_id = data ? data.song_id : 0; + this._html_tag.find(".playlist .current-song").removeClass("current-song"); + this._html_tag.find(".playlist .entry[song-id=" + song_id + "]").addClass("current-song"); }); }); } - - private _playlist_subscribed = false; - private playlist_subscribe(unsubscribe: boolean) { - if(!this.handle.handle.serverConnection) return; - - if(unsubscribe || !this._current_bot) { - if(!this._playlist_subscribed) return; - this._playlist_subscribed = false; - - this.handle.handle.serverConnection.send_command("playlistsetsubscription", {playlist_id: 0}).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to unsubscribe from last playlist: %o"), error); - }); - } else { - this.handle.handle.serverConnection.send_command("playlistsetsubscription", { - playlist_id: this._current_bot.properties.client_playlist_id - }).then(() => this._playlist_subscribed = true).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to subscribe to bots playlist: %o"), error); - }); - } - } - - private build_playlist_entry(data: PlaylistSong, insert_effect: boolean) : JQuery { - const tag = $("#tmpl_frame_music_playlist_entry").renderTag(); - tag.attr({ - "song-id": data.song_id, - "song-url": data.song_url - }); - - const thumbnail = tag.find(".container-thumbnail img"); - const name = tag.find(".name"); - const description = tag.find(".description"); - const length = tag.find(".length"); - - tag.find(".button-delete").on('click', () => this.events.fire("action_song_delete", { song_id: data.song_id })); - tag.find(".container-thumbnail").on('click', event => { - const target = tag.find(".container-thumbnail img"); - const url = target.attr("x-thumbnail-url"); - if(!url) return; - - image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); - }); - tag.on('dblclick', event => this.events.fire("action_song_set", { song_id: data.song_id })); - name.text(tr("loading...")); - description.text(data.song_url); - - tag.on('mousedown', event => { - if(event.button !== 0) return; - - this.song_reorder.mouse = { - x: event.pageX, - y: event.pageY - }; - - const baseOff = tag.offset(); - const off = { x: event.pageX - baseOff.left, y: event.pageY - baseOff.top }; - const move_listener = (event: MouseEvent) => { - const distance = Math.pow(event.pageX - this.song_reorder.mouse.x, 2) + Math.pow(event.pageY - this.song_reorder.mouse.y, 2); - if(distance < 50) return; - - document.removeEventListener("blur", up_listener); - document.removeEventListener("mouseup", up_listener); - document.removeEventListener("mousemove", move_listener); - - this.song_reorder.mouse = off; - this.events.fire("reorder_begin", { - entry: tag, - song_id: data.song_id - }); - }; - - const up_listener = event => { - if(event.type === "mouseup" && event.button !== 0) return; - - document.removeEventListener("blur", up_listener); - document.removeEventListener("mouseup", up_listener); - document.removeEventListener("mousemove", move_listener); - }; - - document.addEventListener("blur", up_listener); - document.addEventListener("mouseup", up_listener); - document.addEventListener("mousemove", move_listener); - }); - - if(this._current_bot) { - this._current_bot.requestPlayerInfo(60 * 1000).then(pdata => { - if(pdata.song_id === data.song_id) - tag.addClass("current-song"); - }); - } - - if(insert_effect) { - tag.removeClass("shown"); - tag.addClass("animation"); - } - return tag; - } + } + + set_current_bot(client: MusicClientEntry | undefined, enforce?: boolean) { + if(client) client.updateClientVariables(); /* just to ensure */ + if(client === this._current_bot && (typeof(enforce) === "undefined" || !enforce)) + return; + + const old = this._current_bot; + this._current_bot = client; + this.events.fire("bot_change", { + new: client, + old: old + }); + } + + current_bot() : MusicClientEntry | undefined { + return this._current_bot; + } + + private sort_songs(data: PlaylistSong[]) { + const result = []; + + let appendable: PlaylistSong[] = []; + for(const song of data) { + if(song.song_id == 0 || data.findIndex(e => e.song_id === song.song_previous_song_id) == -1) + result.push(song); + else + appendable.push(song); + } + + let iters; + while (appendable.length) { + do { + iters = 0; + const left: PlaylistSong[] = []; + for(const song of appendable) { + const index = data.findIndex(e => e.song_id === song.song_previous_song_id); + if(index == -1) { + left.push(song); + continue; + } + + result.splice(index + 1, 0, song); + iters++; + } + appendable = left; + } while(iters > 0); + + if(appendable.length) + result.push(appendable.pop_front()); + } + + return result; + } + + /* playlist stuff */ + update_playlist() { + this.playlist_subscribe(true); + + this._container_playlist.find(".overlay").toggleClass("hidden", true); + const playlist = this._container_playlist.find(".playlist"); + playlist.empty(); + + if(!this.handle.handle.serverConnection || !this.handle.handle.serverConnection.connected() || !this._current_bot) { + this._container_playlist.find(".overlay-empty").removeClass("hidden"); + return; + } + + const overlay_loading = this._container_playlist.find(".overlay-loading"); + overlay_loading.removeClass("hidden"); + + this._current_bot.updateClientVariables(true).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to update music bot variables: %o"), error); + }).then(() => { + this.handle.handle.serverConnection.command_helper.request_playlist_songs(this._current_bot.properties.client_playlist_id).then(songs => { + this.playlist_subscribe(false); /* we're allowed to see the playlist */ + if(!songs) { + this._container_playlist.find(".overlay-empty").removeClass("hidden"); + return; + } + + for(const song of this.sort_songs(songs)) + this.events.fire("playlist_song_add", { song: song, insert_effect: false }); + }).catch(error => { + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { + this._container_playlist.find(".overlay-no-permissions").removeClass("hidden"); + return; + } + log.error(LogCategory.CLIENT, tr("Failed to load bot playlist: %o"), error); + this._container_playlist.find(".overlay.overlay-error").removeClass("hidden"); + }).then(() => { + overlay_loading.addClass("hidden"); + }); + }); + } + + private _playlist_subscribed = false; + private playlist_subscribe(unsubscribe: boolean) { + if(!this.handle.handle.serverConnection) return; + + if(unsubscribe || !this._current_bot) { + if(!this._playlist_subscribed) return; + this._playlist_subscribed = false; + + this.handle.handle.serverConnection.send_command("playlistsetsubscription", {playlist_id: 0}).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to unsubscribe from last playlist: %o"), error); + }); + } else { + this.handle.handle.serverConnection.send_command("playlistsetsubscription", { + playlist_id: this._current_bot.properties.client_playlist_id + }).then(() => this._playlist_subscribed = true).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to subscribe to bots playlist: %o"), error); + }); + } + } + + private build_playlist_entry(data: PlaylistSong, insert_effect: boolean) : JQuery { + const tag = $("#tmpl_frame_music_playlist_entry").renderTag(); + tag.attr({ + "song-id": data.song_id, + "song-url": data.song_url + }); + + const thumbnail = tag.find(".container-thumbnail img"); + const name = tag.find(".name"); + const description = tag.find(".description"); + const length = tag.find(".length"); + + tag.find(".button-delete").on('click', () => this.events.fire("action_song_delete", { song_id: data.song_id })); + tag.find(".container-thumbnail").on('click', event => { + const target = tag.find(".container-thumbnail img"); + const url = target.attr("x-thumbnail-url"); + if(!url) return; + + image_preview.preview_image(decodeURIComponent(url), decodeURIComponent(url)); + }); + tag.on('dblclick', event => this.events.fire("action_song_set", { song_id: data.song_id })); + name.text(tr("loading...")); + description.text(data.song_url); + + tag.on('mousedown', event => { + if(event.button !== 0) return; + + this.song_reorder.mouse = { + x: event.pageX, + y: event.pageY + }; + + const baseOff = tag.offset(); + const off = { x: event.pageX - baseOff.left, y: event.pageY - baseOff.top }; + const move_listener = (event: MouseEvent) => { + const distance = Math.pow(event.pageX - this.song_reorder.mouse.x, 2) + Math.pow(event.pageY - this.song_reorder.mouse.y, 2); + if(distance < 50) return; + + document.removeEventListener("blur", up_listener); + document.removeEventListener("mouseup", up_listener); + document.removeEventListener("mousemove", move_listener); + + this.song_reorder.mouse = off; + this.events.fire("reorder_begin", { + entry: tag, + song_id: data.song_id + }); + }; + + const up_listener = event => { + if(event.type === "mouseup" && event.button !== 0) return; + + document.removeEventListener("blur", up_listener); + document.removeEventListener("mouseup", up_listener); + document.removeEventListener("mousemove", move_listener); + }; + + document.addEventListener("blur", up_listener); + document.addEventListener("mouseup", up_listener); + document.addEventListener("mousemove", move_listener); + }); + + if(this._current_bot) { + this._current_bot.requestPlayerInfo(60 * 1000).then(pdata => { + if(pdata.song_id === data.song_id) + tag.addClass("current-song"); + }); + } + + if(insert_effect) { + tag.removeClass("shown"); + tag.addClass("animation"); + } + return tag; } } \ No newline at end of file diff --git a/shared/js/ui/frames/side/private_conversations.ts b/shared/js/ui/frames/side/private_conversations.ts index f85ffe7c..b702d07c 100644 --- a/shared/js/ui/frames/side/private_conversations.ts +++ b/shared/js/ui/frames/side/private_conversations.ts @@ -1,896 +1,904 @@ /* the bar on the right with the chats (Channel & Client) */ -namespace chat { - declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; - declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +import {settings, Settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import {format, helpers} from "tc-shared/ui/frames/side/chat_helper"; +import {bbcode_chat} from "tc-shared/ui/frames/chat"; +import {Frame} from "tc-shared/ui/frames/chat_frame"; +import {ChatBox} from "tc-shared/ui/frames/side/chat_box"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import * as log from "tc-shared/log"; +import * as htmltags from "tc-shared/ui/htmltags"; - export type PrivateConversationViewEntry = { - html_tag: JQuery; +declare function setInterval(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; +declare function setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number; + +export type PrivateConversationViewEntry = { + html_tag: JQuery; +} + +export type PrivateConversationMessageData = { + message_id: string; + message: string; + sender: "self" | "partner"; + + sender_name: string; + sender_unique_id: string; + sender_client_id: number; + + timestamp: number; +}; + +export type PrivateConversationViewMessage = PrivateConversationMessageData & PrivateConversationViewEntry & { + time_update_id: number; +}; +export type PrivateConversationViewSpacer = PrivateConversationViewEntry; + +export enum PrivateConversationState { + OPEN, + CLOSED, + DISCONNECTED, + DISCONNECTED_SELF, +} + +export type DisplayedMessage = { + timestamp: number; + + message: PrivateConversationViewMessage | PrivateConversationViewEntry; + message_type: "spacer" | "message"; + + /* structure as following + 1. time pointer + 2. unread + 3. message + */ + tag_message: JQuery; + tag_unread: PrivateConversationViewSpacer | undefined; + tag_timepointer: PrivateConversationViewSpacer | undefined; +} + +export class PrivateConveration { + readonly handle: PrivateConverations; + private _html_entry_tag: JQuery; + private _message_history: PrivateConversationMessageData[] = []; + + private _callback_message: (text: string) => any; + + private _state: PrivateConversationState; + + private _last_message_updater_id: number; + private _last_typing: number = 0; + private _typing_timeout: number = 4000; + private _typing_timeout_task: number; + + _scroll_position: number | undefined; /* undefined to follow bottom | position for special stuff */ + _html_message_container: JQuery; /* only set when this chat is selected! */ + + client_unique_id: string; + client_id: number; + client_name: string; + + private _displayed_messages: DisplayedMessage[] = []; + private _displayed_messages_length: number = 500; + private _spacer_unread_message: DisplayedMessage; + + constructor(handle: PrivateConverations, client_unique_id: string, client_name: string, client_id: number) { + this.handle = handle; + this.client_name = client_name; + this.client_unique_id = client_unique_id; + this.client_id = client_id; + this._state = PrivateConversationState.OPEN; + + this._build_entry_tag(); + this.set_unread_flag(false); + + this.load_history(); } - export type PrivateConversationMessageData = { - message_id: string; - message: string; - sender: "self" | "partner"; + private history_key() { return this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier + "_" + this.client_unique_id; } + private load_history() { + helpers.history.load_history(this.history_key()).then((data: PrivateConversationMessageData[]) => { + if(!data) return; - sender_name: string; - sender_unique_id: string; - sender_client_id: number; - - timestamp: number; - }; - - export type PrivateConversationViewMessage = PrivateConversationMessageData & PrivateConversationViewEntry & { - time_update_id: number; - }; - export type PrivateConversationViewSpacer = PrivateConversationViewEntry; - - export enum PrivateConversationState { - OPEN, - CLOSED, - DISCONNECTED, - DISCONNECTED_SELF, - } - - export type DisplayedMessage = { - timestamp: number; - - message: PrivateConversationViewMessage | PrivateConversationViewEntry; - message_type: "spacer" | "message"; - - /* structure as following - 1. time pointer - 2. unread - 3. message - */ - tag_message: JQuery; - tag_unread: PrivateConversationViewSpacer | undefined; - tag_timepointer: PrivateConversationViewSpacer | undefined; - } - - export class PrivateConveration { - readonly handle: PrivateConverations; - private _html_entry_tag: JQuery; - private _message_history: PrivateConversationMessageData[] = []; - - private _callback_message: (text: string) => any; - - private _state: PrivateConversationState; - - private _last_message_updater_id: number; - private _last_typing: number = 0; - private _typing_timeout: number = 4000; - private _typing_timeout_task: number; - - _scroll_position: number | undefined; /* undefined to follow bottom | position for special stuff */ - _html_message_container: JQuery; /* only set when this chat is selected! */ - - client_unique_id: string; - client_id: number; - client_name: string; - - private _displayed_messages: DisplayedMessage[] = []; - private _displayed_messages_length: number = 500; - private _spacer_unread_message: DisplayedMessage; - - constructor(handle: PrivateConverations, client_unique_id: string, client_name: string, client_id: number) { - this.handle = handle; - this.client_name = client_name; - this.client_unique_id = client_unique_id; - this.client_id = client_id; - this._state = PrivateConversationState.OPEN; - - this._build_entry_tag(); - this.set_unread_flag(false); - - this.load_history(); - } - - private history_key() { return this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier + "_" + this.client_unique_id; } - private load_history() { - helpers.history.load_history(this.history_key()).then((data: PrivateConversationMessageData[]) => { - if(!data) return; - - const flag_unread = !!this._spacer_unread_message; - for(const message of data.slice(data.length > this._displayed_messages_length ? data.length - this._displayed_messages_length : 0)) { - this.append_message(message.message, { - type: message.sender, - name: message.sender_name, - unique_id: message.sender_unique_id, - client_id: message.sender_client_id - }, new Date(message.timestamp), false); - } - - if(!flag_unread) - this.set_unread_flag(false); - - this.fix_scroll(false); - this.save_history(); - }).catch(error => { - log.warn(LogCategory.CHAT, tr("Failed to load private conversation history for user %s on server %s: %o"), - this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); - }) - } - - private save_history() { - helpers.history.save_history(this.history_key(), this._message_history).catch(error => { - log.warn(LogCategory.CHAT, tr("Failed to save private conversation history for user %s on server %s: %o"), - this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); - }); - } - - entry_tag() : JQuery { - return this._html_entry_tag; - } - - destroy() { - this._html_message_container = undefined; /* we do not own this container */ - - this.clear_messages(false); - - this._html_entry_tag && this._html_entry_tag.remove(); - this._html_entry_tag = undefined; - - this._message_history = undefined; - if(this._typing_timeout_task) - clearTimeout(this._typing_timeout_task); - } - - private _2d_flat(array: T[][]) : T[] { - const result = []; - for(const a of array) - result.push(...a.filter(e => typeof(e) !== "undefined")); - return result; - } - - messages_tags() : JQuery[] { - return this._2d_flat(this._displayed_messages.slice().reverse().map(e => [ - e.tag_timepointer ? e.tag_timepointer.html_tag : undefined, - e.tag_unread ? e.tag_unread.html_tag : undefined, - e.tag_message - ])); - } - - append_message(message: string, sender: { - type: "self" | "partner"; - name: string; - unique_id: string; - client_id: number; - }, timestamp?: Date, save_history?: boolean) { - const message_date = timestamp || new Date(); - const message_timestamp = message_date.getTime(); - - const packed_message = { - message: message, - sender: sender.type, - sender_name: sender.name, - sender_client_id: sender.client_id, - sender_unique_id: sender.unique_id, - timestamp: message_date.getTime(), - message_id: 'undefined' - }; - - /* first of all register message in message history */ - { - let index = 0; - for(;index < this._message_history.length; index++) { - if(this._message_history[index].timestamp > message_timestamp) - continue; - this._message_history.splice(index, 0, packed_message); - break; - } - - if(index > 100) - return; /* message is too old to be displayed */ - - if(index >= this._message_history.length) - this._message_history.push(packed_message); - - while(this._message_history.length > 100) - this._message_history.pop(); + const flag_unread = !!this._spacer_unread_message; + for(const message of data.slice(data.length > this._displayed_messages_length ? data.length - this._displayed_messages_length : 0)) { + this.append_message(message.message, { + type: message.sender, + name: message.sender_name, + unique_id: message.sender_unique_id, + client_id: message.sender_client_id + }, new Date(message.timestamp), false); } - if(sender.type === "partner") { - clearTimeout(this._typing_timeout_task); - this._typing_timeout_task = 0; + if(!flag_unread) + this.set_unread_flag(false); - if(this.typing_active()) { - this._last_typing = 0; - this.typing_expired(); - } else { - this._update_message_timestamp(); - } + this.fix_scroll(false); + this.save_history(); + }).catch(error => { + log.warn(LogCategory.CHAT, tr("Failed to load private conversation history for user %s on server %s: %o"), + this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); + }) + } + + private save_history() { + helpers.history.save_history(this.history_key(), this._message_history).catch(error => { + log.warn(LogCategory.CHAT, tr("Failed to save private conversation history for user %s on server %s: %o"), + this.client_unique_id, this.handle.handle.handle.channelTree.server.properties.virtualserver_unique_identifier, error); + }); + } + + entry_tag() : JQuery { + return this._html_entry_tag; + } + + destroy() { + this._html_message_container = undefined; /* we do not own this container */ + + this.clear_messages(false); + + this._html_entry_tag && this._html_entry_tag.remove(); + this._html_entry_tag = undefined; + + this._message_history = undefined; + if(this._typing_timeout_task) + clearTimeout(this._typing_timeout_task); + } + + private _2d_flat(array: T[][]) : T[] { + const result = []; + for(const a of array) + result.push(...a.filter(e => typeof(e) !== "undefined")); + return result; + } + + messages_tags() : JQuery[] { + return this._2d_flat(this._displayed_messages.slice().reverse().map(e => [ + e.tag_timepointer ? e.tag_timepointer.html_tag : undefined, + e.tag_unread ? e.tag_unread.html_tag : undefined, + e.tag_message + ])); + } + + append_message(message: string, sender: { + type: "self" | "partner"; + name: string; + unique_id: string; + client_id: number; + }, timestamp?: Date, save_history?: boolean) { + const message_date = timestamp || new Date(); + const message_timestamp = message_date.getTime(); + + const packed_message = { + message: message, + sender: sender.type, + sender_name: sender.name, + sender_client_id: sender.client_id, + sender_unique_id: sender.unique_id, + timestamp: message_date.getTime(), + message_id: 'undefined' + }; + + /* first of all register message in message history */ + { + let index = 0; + for(;index < this._message_history.length; index++) { + if(this._message_history[index].timestamp > message_timestamp) + continue; + this._message_history.splice(index, 0, packed_message); + break; + } + + if(index > 100) + return; /* message is too old to be displayed */ + + if(index >= this._message_history.length) + this._message_history.push(packed_message); + + while(this._message_history.length > 100) + this._message_history.pop(); + } + + if(sender.type === "partner") { + clearTimeout(this._typing_timeout_task); + this._typing_timeout_task = 0; + + if(this.typing_active()) { + this._last_typing = 0; + this.typing_expired(); } else { this._update_message_timestamp(); } - - if(typeof(save_history) !== "boolean" || save_history) - this.save_history(); - - /* insert in view */ - { - const basic_view_entry = this._build_message(packed_message); - - this._register_displayed_message({ - timestamp: basic_view_entry.timestamp, - message: basic_view_entry, - message_type: "message", - tag_message: basic_view_entry.html_tag, - tag_timepointer: undefined, - tag_unread: undefined - }, true); - } - } - - private _displayed_message_first_tag(message: DisplayedMessage) { - const tp = message.tag_timepointer ? message.tag_timepointer.html_tag : undefined; - const tu = message.tag_unread ? message.tag_unread.html_tag : undefined; - return tp || tu || message.tag_message; - } - - private _destroy_displayed_message(message: DisplayedMessage, update_pointers: boolean) { - if(update_pointers) { - const index = this._displayed_messages.indexOf(message); - if(index != -1 && index > 0) { - const next = this._displayed_messages[index - 1]; - if(!next.tag_timepointer && message.tag_timepointer) { - next.tag_timepointer = message.tag_timepointer; - message.tag_timepointer = undefined; - } - if(!next.tag_unread && message.tag_unread) { - this._spacer_unread_message = next; - next.tag_unread = message.tag_unread; - message.tag_unread = undefined; - } - } - - if(message == this._spacer_unread_message) - this._spacer_unread_message = undefined; - } - - this._displayed_messages.remove(message); - if(message.tag_timepointer) - this._destroy_view_entry(message.tag_timepointer); - - if(message.tag_unread) - this._destroy_view_entry(message.tag_unread); - - this._destroy_view_entry(message.message); - } - - clear_messages(save?: boolean) { - this._message_history = []; - while(this._displayed_messages.length > 0) { - this._destroy_displayed_message(this._displayed_messages[0], false); - } - - this._spacer_unread_message = undefined; - - this._update_message_timestamp(); - if(save) - this.save_history(); - } - - fix_scroll(animate: boolean) { - if(!this._html_message_container) - return; - - let offset; - if(this._spacer_unread_message) { - offset = this._displayed_message_first_tag(this._spacer_unread_message)[0].offsetTop; - } else if(typeof(this._scroll_position) !== "undefined") { - offset = this._scroll_position; - } else { - offset = this._html_message_container[0].scrollHeight; - } - if(animate) { - this._html_message_container.stop(true).animate({ - scrollTop: offset - }, 'slow'); - } else { - this._html_message_container.stop(true).scrollTop(offset); - } - } - - private _update_message_timestamp() { - if(this._last_message_updater_id) - clearTimeout(this._last_message_updater_id); - - if(!this._html_entry_tag) - return; /* we got deleted, not need for updates */ - - if(this.typing_active()) { - this._html_entry_tag.find(".last-message").text(tr("currently typing...")); - return; - } - - const last_message = this._message_history[0]; - if(!last_message) { - this._html_entry_tag.find(".last-message").text(tr("no history")); - return; - } - - const timestamp = new Date(last_message.timestamp); - let time = format.date.format_chat_time(timestamp); - this._html_entry_tag.find(".last-message").text(time.result); - - if(time.next_update > 0) { - this._last_message_updater_id = setTimeout(() => this._update_message_timestamp(), time.next_update); - } else { - this._last_message_updater_id = 0; - } - } - - private _destroy_message(message: PrivateConversationViewMessage) { - if(message.time_update_id) - clearTimeout(message.time_update_id); - } - - private _build_message(message: PrivateConversationMessageData) : PrivateConversationViewMessage { - const result = message as PrivateConversationViewMessage; - if(result.html_tag) - return result; - - const timestamp = new Date(message.timestamp); - let time = format.date.format_chat_time(timestamp); - result.html_tag = $("#tmpl_frame_chat_private_message").renderTag({ - timestamp: time.result, - message_id: message.message_id, - client_name: htmltags.generate_client_object({ - add_braces: false, - client_name: message.sender_name, - client_unique_id: message.sender_unique_id, - client_id: message.sender_client_id - }), - message: MessageHelper.bbcode_chat(message.message), - avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: message.sender_client_id}, message.sender_unique_id) - }); - if(time.next_update > 0) { - const _updater = () => { - time = format.date.format_chat_time(timestamp); - result.html_tag.find(".info .timestamp").text(time.result); - if(time.next_update > 0) - result.time_update_id = setTimeout(_updater, time.next_update); - else - result.time_update_id = 0; - }; - result.time_update_id = setTimeout(_updater, time.next_update); - } else { - result.time_update_id = 0; - } - - return result; - } - - private _build_spacer(message: string, type: "date" | "new" | "disconnect" | "disconnect_self" | "reconnect" | "closed" | "error") : PrivateConversationViewSpacer { - const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ - message: message - }).addClass("type-" + type); - return { - html_tag: tag - } - } - - private _register_displayed_message(message: DisplayedMessage, update_new: boolean) { - const message_date = new Date(message.timestamp); - - /* before := older message; after := newer message */ - let entry_before: DisplayedMessage, entry_after: DisplayedMessage; - let index = 0; - for(;index < this._displayed_messages.length; index++) { - if(this._displayed_messages[index].timestamp > message.timestamp) - continue; - - entry_after = index > 0 ? this._displayed_messages[index - 1] : undefined; - entry_before = this._displayed_messages[index]; - this._displayed_messages.splice(index, 0, message); - break; - } - if(index >= this._displayed_messages_length) { - return; /* message is out of view region */ - } - - if(index >= this._displayed_messages.length) { - entry_before = undefined; - entry_after = this._displayed_messages.last(); - this._displayed_messages.push(message); - } - - while(this._displayed_messages.length > this._displayed_messages_length) - this._destroy_displayed_message(this._displayed_messages.last(), true); - - const flag_new_message = update_new && index == 0 && (message.message_type === "spacer" || (message.message).sender === "partner"); - - /* Timeline for before - now */ - { - let append_pointer = false; - - if(entry_before) { - if(!helpers.date.same_day(message.timestamp, entry_before.timestamp)) { - append_pointer = true; - } - } else { - append_pointer = true; - } - if(append_pointer) { - const diff = format.date.date_format(message_date, new Date()); - if(diff == format.date.ColloquialFormat.YESTERDAY) - message.tag_timepointer = this._build_spacer(tr("Yesterday"), "date"); - else if(diff == format.date.ColloquialFormat.TODAY) - message.tag_timepointer = this._build_spacer(tr("Today"), "date"); - else if(diff == format.date.ColloquialFormat.GENERAL) - message.tag_timepointer = this._build_spacer(format.date.format_date_general(message_date, false), "date"); - } - } - - /* Timeline not and after */ - { - if(entry_after) { - if(helpers.date.same_day(message_date, entry_after.timestamp)) { - if(entry_after.tag_timepointer) { - this._destroy_view_entry(entry_after.tag_timepointer); - entry_after.tag_timepointer = undefined; - } - } else if(!entry_after.tag_timepointer) { - const diff = format.date.date_format(new Date(entry_after.timestamp), new Date()); - if(diff == format.date.ColloquialFormat.YESTERDAY) - entry_after.tag_timepointer = this._build_spacer(tr("Yesterday"), "date"); - else if(diff == format.date.ColloquialFormat.TODAY) - entry_after.tag_timepointer = this._build_spacer(tr("Today"), "date"); - else if(diff == format.date.ColloquialFormat.GENERAL) - entry_after.tag_timepointer = this._build_spacer(format.date.format_date_general(message_date, false), "date"); - - entry_after.tag_timepointer.html_tag.insertBefore(entry_after.tag_message); - } - } - } - - /* new message flag */ - if(flag_new_message) { - if(!this._spacer_unread_message) { - this._spacer_unread_message = message; - message.tag_unread = this._build_spacer(tr("Unread messages"), "new"); - - this.set_unread_flag(true); - } - } - - if(this._html_message_container) { - if(entry_before) { - message.tag_message.insertAfter(entry_before.tag_message); - } else if(entry_after) { - message.tag_message.insertBefore(this._displayed_message_first_tag(entry_after)); - } else { - this._html_message_container.append(message.tag_message); - } - - /* first time pointer */ - if(message.tag_timepointer) - message.tag_timepointer.html_tag.insertBefore(message.tag_message); - - /* the unread */ - if(message.tag_unread) - message.tag_unread.html_tag.insertBefore(message.tag_message); - } - - this.fix_scroll(true); - } - - private _destroy_view_entry(entry: PrivateConversationViewEntry) { - if(!entry.html_tag) - return; - entry.html_tag.remove(); - if('sender' in entry) - this._destroy_message(entry); - } - - private _build_entry_tag() { - this._html_entry_tag = $("#tmpl_frame_chat_private_entry").renderTag({ - client_name: this.client_name, - last_time: tr("error no timestamp"), - avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: this.client_id}, this.client_unique_id) - }); - this._html_entry_tag.on('click', event => { - if(event.isDefaultPrevented()) - return; - - this.handle.set_selected_conversation(this); - }); - this._html_entry_tag.find('.button-close').on('click', event => { - event.preventDefault(); - this.close_conversation(); - }); + } else { this._update_message_timestamp(); } - update_avatar() { - const container = this._html_entry_tag.find(".container-avatar"); - container.find(".avatar").remove(); - container.append(this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: this.client_id}, this.client_unique_id)); - } + if(typeof(save_history) !== "boolean" || save_history) + this.save_history(); - close_conversation() { - this.handle.delete_conversation(this, true); - } + /* insert in view */ + { + const basic_view_entry = this._build_message(packed_message); - set_client_name(name: string) { - if(this.client_name === name) - return; - this.client_name = name; - this._html_entry_tag.find(".client-name").text(name); - } - - set_unread_flag(flag: boolean, update_chat_counter?: boolean) { - /* unread message pointer */ - if(flag != (typeof(this._spacer_unread_message) !== "undefined")) { - if(flag) { - if(this._displayed_messages.length > 0) /* without messages we cant be unread */ - return; - - if(!this._spacer_unread_message) { - this._spacer_unread_message = this._displayed_messages[0]; - this._spacer_unread_message.tag_unread = this._build_spacer(tr("Unread messages"), "new"); - this._spacer_unread_message.tag_unread.html_tag.insertBefore(this._spacer_unread_message.tag_message); - } - } else { - const ctree = this.handle.handle.handle.channelTree; - if(ctree && ctree.tag_tree() && this.client_id) - ctree.tag_tree().find(".marker-text-unread[private-conversation='" + this.client_id + "']").addClass("hidden"); - - if(this._spacer_unread_message) { - this._destroy_view_entry(this._spacer_unread_message.tag_unread); - this._spacer_unread_message.tag_unread = undefined; - this._spacer_unread_message = undefined; - } - } - } - - /* general notify */ - this._html_entry_tag.toggleClass("unread", flag); - if(typeof(update_chat_counter) !== "boolean" || update_chat_counter) - this.handle.handle.info_frame().update_chat_counter(); - } - - is_unread() : boolean { return !!this._spacer_unread_message; } - - private _append_state_change(state: "disconnect" | "disconnect_self" | "reconnect" | "closed") { - let message; - if(state == "closed") - message = tr("Your chat partner has closed the conversation"); - else if(state == "reconnect") - message = this._state === PrivateConversationState.DISCONNECTED_SELF ?tr("You've reconnected to the server") : tr("Your chat partner has reconnected"); - else if(state === "disconnect") - message = tr("Your chat partner has disconnected"); - else - message = tr("You've disconnected from the server"); - - const spacer = this._build_spacer(message, state); this._register_displayed_message({ - timestamp: Date.now(), - message: spacer, - message_type: "spacer", - tag_message: spacer.html_tag, - tag_timepointer: undefined, - tag_unread: undefined - }, state === "disconnect"); - } - - state() : PrivateConversationState { - return this._state; - } - - set_state(state: PrivateConversationState) { - if(this._state == state) - return; - - if(state == PrivateConversationState.DISCONNECTED) { - this._append_state_change("disconnect"); - this.client_id = 0; - } else if(state == PrivateConversationState.OPEN && this._state != PrivateConversationState.CLOSED) - this._append_state_change("reconnect"); - else if(state == PrivateConversationState.CLOSED) - this._append_state_change("closed"); - else if(state == PrivateConversationState.DISCONNECTED_SELF) - this._append_state_change("disconnect_self"); - - this._state = state; - } - - set_text_callback(callback: (text: string) => any, update_enabled_state?: boolean) { - this._callback_message = callback; - if(typeof (update_enabled_state) !== "boolean" || update_enabled_state) - this.handle.update_chatbox_state(); - } - - chat_enabled() { - return typeof(this._callback_message) !== "undefined" && (this._state == PrivateConversationState.OPEN || this._state == PrivateConversationState.CLOSED); - } - - append_error(message: string, date?: number) { - const spacer = this._build_spacer(message, "error"); - this._register_displayed_message({ - timestamp: date || Date.now(), - message: spacer, - message_type: "spacer", - tag_message: spacer.html_tag, + timestamp: basic_view_entry.timestamp, + message: basic_view_entry, + message_type: "message", + tag_message: basic_view_entry.html_tag, tag_timepointer: undefined, tag_unread: undefined }, true); } + } - call_message(message: string) { - if(this._callback_message) - this._callback_message(message); - else { - log.warn(LogCategory.CHAT, tr("Dropping conversation message for client %o because of no message callback."), { - client_name: this.client_name, - client_id: this.client_id, - client_unique_id: this.client_unique_id - }); + private _displayed_message_first_tag(message: DisplayedMessage) { + const tp = message.tag_timepointer ? message.tag_timepointer.html_tag : undefined; + const tu = message.tag_unread ? message.tag_unread.html_tag : undefined; + return tp || tu || message.tag_message; + } + + private _destroy_displayed_message(message: DisplayedMessage, update_pointers: boolean) { + if(update_pointers) { + const index = this._displayed_messages.indexOf(message); + if(index != -1 && index > 0) { + const next = this._displayed_messages[index - 1]; + if(!next.tag_timepointer && message.tag_timepointer) { + next.tag_timepointer = message.tag_timepointer; + message.tag_timepointer = undefined; + } + if(!next.tag_unread && message.tag_unread) { + this._spacer_unread_message = next; + next.tag_unread = message.tag_unread; + message.tag_unread = undefined; + } } + + if(message == this._spacer_unread_message) + this._spacer_unread_message = undefined; } - private typing_expired() { - this._update_message_timestamp(); - if(this.handle.current_conversation() === this) - this.handle.update_typing_state(); + this._displayed_messages.remove(message); + if(message.tag_timepointer) + this._destroy_view_entry(message.tag_timepointer); + + if(message.tag_unread) + this._destroy_view_entry(message.tag_unread); + + this._destroy_view_entry(message.message); + } + + clear_messages(save?: boolean) { + this._message_history = []; + while(this._displayed_messages.length > 0) { + this._destroy_displayed_message(this._displayed_messages[0], false); } - trigger_typing() { - let _new = Date.now() - this._last_typing > this._typing_timeout; - this._last_typing = Date.now(); + this._spacer_unread_message = undefined; - if(this._typing_timeout_task) - clearTimeout(this._typing_timeout_task); + this._update_message_timestamp(); + if(save) + this.save_history(); + } - if(_new) - this._update_message_timestamp(); - if(this.handle.current_conversation() === this) - this.handle.update_typing_state(); + fix_scroll(animate: boolean) { + if(!this._html_message_container) + return; - this._typing_timeout_task = setTimeout(() => this.typing_expired(), this._typing_timeout); + let offset; + if(this._spacer_unread_message) { + offset = this._displayed_message_first_tag(this._spacer_unread_message)[0].offsetTop; + } else if(typeof(this._scroll_position) !== "undefined") { + offset = this._scroll_position; + } else { + offset = this._html_message_container[0].scrollHeight; } - - typing_active() { - return Date.now() - this._last_typing < this._typing_timeout; + if(animate) { + this._html_message_container.stop(true).animate({ + scrollTop: offset + }, 'slow'); + } else { + this._html_message_container.stop(true).scrollTop(offset); } } - export class PrivateConverations { - readonly handle: Frame; - private _chat_box: ChatBox; - private _html_tag: JQuery; + private _update_message_timestamp() { + if(this._last_message_updater_id) + clearTimeout(this._last_message_updater_id); - private _container_conversation: JQuery; - private _container_conversation_messages: JQuery; - private _container_conversation_list: JQuery; - private _container_typing: JQuery; + if(!this._html_entry_tag) + return; /* we got deleted, not need for updates */ - private _html_no_chats: JQuery; - private _conversations: PrivateConveration[] = []; + if(this.typing_active()) { + this._html_entry_tag.find(".last-message").text(tr("currently typing...")); + return; + } - private _current_conversation: PrivateConveration = undefined; - private _select_read_timer: number; + const last_message = this._message_history[0]; + if(!last_message) { + this._html_entry_tag.find(".last-message").text(tr("no history")); + return; + } - constructor(handle: Frame) { - this.handle = handle; - this._chat_box = new ChatBox(); - this._build_html_tag(); + const timestamp = new Date(last_message.timestamp); + let time = format.date.format_chat_time(timestamp); + this._html_entry_tag.find(".last-message").text(time.result); - this.update_chatbox_state(); - this.update_typing_state(); - this._chat_box.callback_text = message => { - if(!this._current_conversation) { - log.warn(LogCategory.CHAT, tr("Dropping conversation message because of no active conversation.")); - return; - } - this._current_conversation.call_message(message); + if(time.next_update > 0) { + this._last_message_updater_id = setTimeout(() => this._update_message_timestamp(), time.next_update); + } else { + this._last_message_updater_id = 0; + } + } + + private _destroy_message(message: PrivateConversationViewMessage) { + if(message.time_update_id) + clearTimeout(message.time_update_id); + } + + private _build_message(message: PrivateConversationMessageData) : PrivateConversationViewMessage { + const result = message as PrivateConversationViewMessage; + if(result.html_tag) + return result; + + const timestamp = new Date(message.timestamp); + let time = format.date.format_chat_time(timestamp); + result.html_tag = $("#tmpl_frame_chat_private_message").renderTag({ + timestamp: time.result, + message_id: message.message_id, + client_name: htmltags.generate_client_object({ + add_braces: false, + client_name: message.sender_name, + client_unique_id: message.sender_unique_id, + client_id: message.sender_client_id + }), + message: bbcode_chat(message.message), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: message.sender_client_id}, message.sender_unique_id) + }); + if(time.next_update > 0) { + const _updater = () => { + time = format.date.format_chat_time(timestamp); + result.html_tag.find(".info .timestamp").text(time.result); + if(time.next_update > 0) + result.time_update_id = setTimeout(_updater, time.next_update); + else + result.time_update_id = 0; }; + result.time_update_id = setTimeout(_updater, time.next_update); + } else { + result.time_update_id = 0; + } - this._chat_box.callback_typing = () => { - if(!this._current_conversation) { - log.warn(LogCategory.CHAT, tr("Dropping conversation typing action because of no active conversation.")); - return; + return result; + } + + private _build_spacer(message: string, type: "date" | "new" | "disconnect" | "disconnect_self" | "reconnect" | "closed" | "error") : PrivateConversationViewSpacer { + const tag = $("#tmpl_frame_chat_private_spacer").renderTag({ + message: message + }).addClass("type-" + type); + return { + html_tag: tag + } + } + + private _register_displayed_message(message: DisplayedMessage, update_new: boolean) { + const message_date = new Date(message.timestamp); + + /* before := older message; after := newer message */ + let entry_before: DisplayedMessage, entry_after: DisplayedMessage; + let index = 0; + for(;index < this._displayed_messages.length; index++) { + if(this._displayed_messages[index].timestamp > message.timestamp) + continue; + + entry_after = index > 0 ? this._displayed_messages[index - 1] : undefined; + entry_before = this._displayed_messages[index]; + this._displayed_messages.splice(index, 0, message); + break; + } + if(index >= this._displayed_messages_length) { + return; /* message is out of view region */ + } + + if(index >= this._displayed_messages.length) { + entry_before = undefined; + entry_after = this._displayed_messages.last(); + this._displayed_messages.push(message); + } + + while(this._displayed_messages.length > this._displayed_messages_length) + this._destroy_displayed_message(this._displayed_messages.last(), true); + + const flag_new_message = update_new && index == 0 && (message.message_type === "spacer" || (message.message).sender === "partner"); + + /* Timeline for before - now */ + { + let append_pointer = false; + + if(entry_before) { + if(!helpers.date.same_day(message.timestamp, entry_before.timestamp)) { + append_pointer = true; } - - const connection = this.handle.handle.serverConnection; - if(!connection || !connection.connected()) - return; - - connection.send_command("clientchatcomposing", { - clid: this._current_conversation.client_id - }); + } else { + append_pointer = true; + } + if(append_pointer) { + const diff = format.date.date_format(message_date, new Date()); + if(diff == format.date.ColloquialFormat.YESTERDAY) + message.tag_timepointer = this._build_spacer(tr("Yesterday"), "date"); + else if(diff == format.date.ColloquialFormat.TODAY) + message.tag_timepointer = this._build_spacer(tr("Today"), "date"); + else if(diff == format.date.ColloquialFormat.GENERAL) + message.tag_timepointer = this._build_spacer(format.date.format_date_general(message_date, false), "date"); } } - clear_client_ids() { - this._conversations.forEach(e => { - e.client_id = 0; - e.set_state(PrivateConversationState.DISCONNECTED_SELF); - }); + /* Timeline not and after */ + { + if(entry_after) { + if(helpers.date.same_day(message_date, entry_after.timestamp)) { + if(entry_after.tag_timepointer) { + this._destroy_view_entry(entry_after.tag_timepointer); + entry_after.tag_timepointer = undefined; + } + } else if(!entry_after.tag_timepointer) { + const diff = format.date.date_format(new Date(entry_after.timestamp), new Date()); + if(diff == format.date.ColloquialFormat.YESTERDAY) + entry_after.tag_timepointer = this._build_spacer(tr("Yesterday"), "date"); + else if(diff == format.date.ColloquialFormat.TODAY) + entry_after.tag_timepointer = this._build_spacer(tr("Today"), "date"); + else if(diff == format.date.ColloquialFormat.GENERAL) + entry_after.tag_timepointer = this._build_spacer(format.date.format_date_general(message_date, false), "date"); + + entry_after.tag_timepointer.html_tag.insertBefore(entry_after.tag_message); + } + } } - html_tag() : JQuery { return this._html_tag; } - destroy() { - this._chat_box && this._chat_box.destroy(); - this._chat_box = undefined; + /* new message flag */ + if(flag_new_message) { + if(!this._spacer_unread_message) { + this._spacer_unread_message = message; + message.tag_unread = this._build_spacer(tr("Unread messages"), "new"); - for(const conversation of this._conversations) - conversation.destroy(); - this._conversations = []; - this._current_conversation = undefined; + this.set_unread_flag(true); + } + } + if(this._html_message_container) { + if(entry_before) { + message.tag_message.insertAfter(entry_before.tag_message); + } else if(entry_after) { + message.tag_message.insertBefore(this._displayed_message_first_tag(entry_after)); + } else { + this._html_message_container.append(message.tag_message); + } + + /* first time pointer */ + if(message.tag_timepointer) + message.tag_timepointer.html_tag.insertBefore(message.tag_message); + + /* the unread */ + if(message.tag_unread) + message.tag_unread.html_tag.insertBefore(message.tag_message); + } + + this.fix_scroll(true); + } + + private _destroy_view_entry(entry: PrivateConversationViewEntry) { + if(!entry.html_tag) + return; + entry.html_tag.remove(); + if('sender' in entry) + this._destroy_message(entry); + } + + private _build_entry_tag() { + this._html_entry_tag = $("#tmpl_frame_chat_private_entry").renderTag({ + client_name: this.client_name, + last_time: tr("error no timestamp"), + avatar: this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: this.client_id}, this.client_unique_id) + }); + this._html_entry_tag.on('click', event => { + if(event.isDefaultPrevented()) + return; + + this.handle.set_selected_conversation(this); + }); + this._html_entry_tag.find('.button-close').on('click', event => { + event.preventDefault(); + this.close_conversation(); + }); + this._update_message_timestamp(); + } + + update_avatar() { + const container = this._html_entry_tag.find(".container-avatar"); + container.find(".avatar").remove(); + container.append(this.handle.handle.handle.fileManager.avatars.generate_chat_tag({id: this.client_id}, this.client_unique_id)); + } + + close_conversation() { + this.handle.delete_conversation(this, true); + } + + set_client_name(name: string) { + if(this.client_name === name) + return; + this.client_name = name; + this._html_entry_tag.find(".client-name").text(name); + } + + set_unread_flag(flag: boolean, update_chat_counter?: boolean) { + /* unread message pointer */ + if(flag != (typeof(this._spacer_unread_message) !== "undefined")) { + if(flag) { + if(this._displayed_messages.length > 0) /* without messages we cant be unread */ + return; + + if(!this._spacer_unread_message) { + this._spacer_unread_message = this._displayed_messages[0]; + this._spacer_unread_message.tag_unread = this._build_spacer(tr("Unread messages"), "new"); + this._spacer_unread_message.tag_unread.html_tag.insertBefore(this._spacer_unread_message.tag_message); + } + } else { + const ctree = this.handle.handle.handle.channelTree; + if(ctree && ctree.tag_tree() && this.client_id) + ctree.tag_tree().find(".marker-text-unread[private-conversation='" + this.client_id + "']").addClass("hidden"); + + if(this._spacer_unread_message) { + this._destroy_view_entry(this._spacer_unread_message.tag_unread); + this._spacer_unread_message.tag_unread = undefined; + this._spacer_unread_message = undefined; + } + } + } + + /* general notify */ + this._html_entry_tag.toggleClass("unread", flag); + if(typeof(update_chat_counter) !== "boolean" || update_chat_counter) + this.handle.handle.info_frame().update_chat_counter(); + } + + is_unread() : boolean { return !!this._spacer_unread_message; } + + private _append_state_change(state: "disconnect" | "disconnect_self" | "reconnect" | "closed") { + let message; + if(state == "closed") + message = tr("Your chat partner has closed the conversation"); + else if(state == "reconnect") + message = this._state === PrivateConversationState.DISCONNECTED_SELF ?tr("You've reconnected to the server") : tr("Your chat partner has reconnected"); + else if(state === "disconnect") + message = tr("Your chat partner has disconnected"); + else + message = tr("You've disconnected from the server"); + + const spacer = this._build_spacer(message, state); + this._register_displayed_message({ + timestamp: Date.now(), + message: spacer, + message_type: "spacer", + tag_message: spacer.html_tag, + tag_timepointer: undefined, + tag_unread: undefined + }, state === "disconnect"); + } + + state() : PrivateConversationState { + return this._state; + } + + set_state(state: PrivateConversationState) { + if(this._state == state) + return; + + if(state == PrivateConversationState.DISCONNECTED) { + this._append_state_change("disconnect"); + this.client_id = 0; + } else if(state == PrivateConversationState.OPEN && this._state != PrivateConversationState.CLOSED) + this._append_state_change("reconnect"); + else if(state == PrivateConversationState.CLOSED) + this._append_state_change("closed"); + else if(state == PrivateConversationState.DISCONNECTED_SELF) + this._append_state_change("disconnect_self"); + + this._state = state; + } + + set_text_callback(callback: (text: string) => any, update_enabled_state?: boolean) { + this._callback_message = callback; + if(typeof (update_enabled_state) !== "boolean" || update_enabled_state) + this.handle.update_chatbox_state(); + } + + chat_enabled() { + return typeof(this._callback_message) !== "undefined" && (this._state == PrivateConversationState.OPEN || this._state == PrivateConversationState.CLOSED); + } + + append_error(message: string, date?: number) { + const spacer = this._build_spacer(message, "error"); + this._register_displayed_message({ + timestamp: date || Date.now(), + message: spacer, + message_type: "spacer", + tag_message: spacer.html_tag, + tag_timepointer: undefined, + tag_unread: undefined + }, true); + } + + call_message(message: string) { + if(this._callback_message) + this._callback_message(message); + else { + log.warn(LogCategory.CHAT, tr("Dropping conversation message for client %o because of no message callback."), { + client_name: this.client_name, + client_id: this.client_id, + client_unique_id: this.client_unique_id + }); + } + } + + private typing_expired() { + this._update_message_timestamp(); + if(this.handle.current_conversation() === this) + this.handle.update_typing_state(); + } + + trigger_typing() { + let _new = Date.now() - this._last_typing > this._typing_timeout; + this._last_typing = Date.now(); + + if(this._typing_timeout_task) + clearTimeout(this._typing_timeout_task); + + if(_new) + this._update_message_timestamp(); + if(this.handle.current_conversation() === this) + this.handle.update_typing_state(); + + this._typing_timeout_task = setTimeout(() => this.typing_expired(), this._typing_timeout); + } + + typing_active() { + return Date.now() - this._last_typing < this._typing_timeout; + } +} + +export class PrivateConverations { + readonly handle: Frame; + private _chat_box: ChatBox; + private _html_tag: JQuery; + + private _container_conversation: JQuery; + private _container_conversation_messages: JQuery; + private _container_conversation_list: JQuery; + private _container_typing: JQuery; + + private _html_no_chats: JQuery; + private _conversations: PrivateConveration[] = []; + + private _current_conversation: PrivateConveration = undefined; + private _select_read_timer: number; + + constructor(handle: Frame) { + this.handle = handle; + this._chat_box = new ChatBox(); + this._build_html_tag(); + + this.update_chatbox_state(); + this.update_typing_state(); + this._chat_box.callback_text = message => { + if(!this._current_conversation) { + log.warn(LogCategory.CHAT, tr("Dropping conversation message because of no active conversation.")); + return; + } + this._current_conversation.call_message(message); + }; + + this._chat_box.callback_typing = () => { + if(!this._current_conversation) { + log.warn(LogCategory.CHAT, tr("Dropping conversation typing action because of no active conversation.")); + return; + } + + const connection = this.handle.handle.serverConnection; + if(!connection || !connection.connected()) + return; + + connection.send_command("clientchatcomposing", { + clid: this._current_conversation.client_id + }); + } + } + + clear_client_ids() { + this._conversations.forEach(e => { + e.client_id = 0; + e.set_state(PrivateConversationState.DISCONNECTED_SELF); + }); + } + + html_tag() : JQuery { return this._html_tag; } + destroy() { + this._chat_box && this._chat_box.destroy(); + this._chat_box = undefined; + + for(const conversation of this._conversations) + conversation.destroy(); + this._conversations = []; + this._current_conversation = undefined; + + clearTimeout(this._select_read_timer); + + this._html_tag && this._html_tag.remove(); + this._html_tag = undefined; + + } + + current_conversation() : PrivateConveration | undefined { return this._current_conversation; } + + conversations() : PrivateConveration[] { return this._conversations; } + create_conversation(client_uid: string, client_name: string, client_id: number) : PrivateConveration { + const conv = new PrivateConveration(this, client_uid, client_name, client_id); + this._conversations.push(conv); + this._html_no_chats.hide(); + + this._container_conversation_list.append(conv.entry_tag()); + this.handle.info_frame().update_chat_counter(); + return conv; + } + delete_conversation(conv: PrivateConveration, update_chat_couner?: boolean) { + if(!this._conversations.remove(conv)) + return; + //TODO: May animate? + conv.destroy(); + conv.clear_messages(false); + this._html_no_chats.toggle(this._conversations.length == 0); + if(conv === this._current_conversation) + this.set_selected_conversation(undefined); + if(update_chat_couner || typeof(update_chat_couner) !== "boolean") + this.handle.info_frame().update_chat_counter(); + } + find_conversation(partner: { name: string; unique_id: string; client_id: number }, mode: { create: boolean, attach: boolean }) : PrivateConveration | undefined { + for(const conversation of this.conversations()) + if(conversation.client_id == partner.client_id && (!partner.unique_id || conversation.client_unique_id == partner.unique_id)) { + if(conversation.state() != PrivateConversationState.OPEN) + conversation.set_state(PrivateConversationState.OPEN); + return conversation; + } + + let conv: PrivateConveration; + if(mode.attach) { + for(const conversation of this.conversations()) + if(conversation.client_unique_id == partner.unique_id && conversation.state() != PrivateConversationState.OPEN) { + conversation.set_state(PrivateConversationState.OPEN); + conversation.client_id = partner.client_id; + conversation.set_client_name(partner.name); + + conv = conversation; + break; + } + } + + if(mode.create && !conv) { + conv = this.create_conversation(partner.unique_id, partner.name, partner.client_id); + conv.client_id = partner.client_id; + conv.set_client_name(partner.name); + } + + if(conv) { + conv.set_text_callback(message => { + log.debug(LogCategory.CLIENT, tr("Sending text message %s to %o"), message, partner); + this.handle.handle.serverConnection.send_command("sendtextmessage", {"targetmode": 1, "target": partner.client_id, "msg": message}).catch(error => { + if(error instanceof CommandResult) { + if(error.id == ErrorID.CLIENT_INVALID_ID) { + conv.set_state(PrivateConversationState.DISCONNECTED); + conv.set_text_callback(undefined); + } else if(error.id == ErrorID.PERMISSION_ERROR) { + /* may notify for no permissions? */ + } else { + conv.append_error(tr("Failed to send message: ") + (error.extra_message || error.message)); + } + } else { + conv.append_error(tr("Failed to send message. Lookup the console for more details")); + log.error(LogCategory.CHAT, tr("Failed to send conversation message: %o"), error); + } + }); + }); + } + return conv; + } + + clear_conversations() { + while(this._conversations.length > 0) + this.delete_conversation(this._conversations[0], false); + this.handle.info_frame().update_chat_counter(); + } + + set_selected_conversation(conv: PrivateConveration | undefined) { + if(conv === this._current_conversation) + return; + + if(this._select_read_timer) clearTimeout(this._select_read_timer); - this._html_tag && this._html_tag.remove(); - this._html_tag = undefined; + if(this._current_conversation) + this._current_conversation._html_message_container = undefined; - } - - current_conversation() : PrivateConveration | undefined { return this._current_conversation; } - - conversations() : PrivateConveration[] { return this._conversations; } - create_conversation(client_uid: string, client_name: string, client_id: number) : PrivateConveration { - const conv = new PrivateConveration(this, client_uid, client_name, client_id); - this._conversations.push(conv); - this._html_no_chats.hide(); - - this._container_conversation_list.append(conv.entry_tag()); - this.handle.info_frame().update_chat_counter(); - return conv; - } - delete_conversation(conv: PrivateConveration, update_chat_couner?: boolean) { - if(!this._conversations.remove(conv)) - return; - //TODO: May animate? - conv.destroy(); - conv.clear_messages(false); - this._html_no_chats.toggle(this._conversations.length == 0); - if(conv === this._current_conversation) - this.set_selected_conversation(undefined); - if(update_chat_couner || typeof(update_chat_couner) !== "boolean") - this.handle.info_frame().update_chat_counter(); - } - find_conversation(partner: { name: string; unique_id: string; client_id: number }, mode: { create: boolean, attach: boolean }) : PrivateConveration | undefined { - for(const conversation of this.conversations()) - if(conversation.client_id == partner.client_id && (!partner.unique_id || conversation.client_unique_id == partner.unique_id)) { - if(conversation.state() != PrivateConversationState.OPEN) - conversation.set_state(PrivateConversationState.OPEN); - return conversation; - } - - let conv: PrivateConveration; - if(mode.attach) { - for(const conversation of this.conversations()) - if(conversation.client_unique_id == partner.unique_id && conversation.state() != PrivateConversationState.OPEN) { - conversation.set_state(PrivateConversationState.OPEN); - conversation.client_id = partner.client_id; - conversation.set_client_name(partner.name); - - conv = conversation; - break; - } - } - - if(mode.create && !conv) { - conv = this.create_conversation(partner.unique_id, partner.name, partner.client_id); - conv.client_id = partner.client_id; - conv.set_client_name(partner.name); - } - - if(conv) { - conv.set_text_callback(message => { - log.debug(LogCategory.CLIENT, tr("Sending text message %s to %o"), message, partner); - this.handle.handle.serverConnection.send_command("sendtextmessage", {"targetmode": 1, "target": partner.client_id, "msg": message}).catch(error => { - if(error instanceof CommandResult) { - if(error.id == ErrorID.CLIENT_INVALID_ID) { - conv.set_state(PrivateConversationState.DISCONNECTED); - conv.set_text_callback(undefined); - } else if(error.id == ErrorID.PERMISSION_ERROR) { - /* may notify for no permissions? */ - } else { - conv.append_error(tr("Failed to send message: ") + (error.extra_message || error.message)); - } - } else { - conv.append_error(tr("Failed to send message. Lookup the console for more details")); - log.error(LogCategory.CHAT, tr("Failed to send conversation message: %o", error)); - } - }); - }); - } - return conv; - } - - clear_conversations() { - while(this._conversations.length > 0) - this.delete_conversation(this._conversations[0], false); - this.handle.info_frame().update_chat_counter(); - } - - set_selected_conversation(conv: PrivateConveration | undefined) { - if(conv === this._current_conversation) - return; - - if(this._select_read_timer) - clearTimeout(this._select_read_timer); - - if(this._current_conversation) - this._current_conversation._html_message_container = undefined; - - this._container_conversation_list.find(".selected").removeClass("selected"); - this._container_conversation_messages.children().detach(); - this._current_conversation = conv; - if(!this._current_conversation) { - this.update_chatbox_state(); - return; - } - - this._current_conversation._html_message_container = this._container_conversation_messages; - const messages = this._current_conversation.messages_tags(); - /* TODO: Check if the messages are empty and display "No messages" */ - this._container_conversation_messages.append(...messages); - - if(this._current_conversation.is_unread() && false) { - this._select_read_timer = setTimeout(() => { - this._current_conversation.set_unread_flag(false, true); - }, 20 * 1000); /* Lets guess you've read the new messages within 5 seconds */ - } - this._current_conversation.fix_scroll(false); - this._current_conversation.entry_tag().addClass("selected"); + this._container_conversation_list.find(".selected").removeClass("selected"); + this._container_conversation_messages.children().detach(); + this._current_conversation = conv; + if(!this._current_conversation) { this.update_chatbox_state(); + return; } - update_chatbox_state() { - this._chat_box.set_enabled(!!this._current_conversation && this._current_conversation.chat_enabled()); + this._current_conversation._html_message_container = this._container_conversation_messages; + const messages = this._current_conversation.messages_tags(); + /* TODO: Check if the messages are empty and display "No messages" */ + this._container_conversation_messages.append(...messages); + + if(this._current_conversation.is_unread() && false) { + this._select_read_timer = setTimeout(() => { + this._current_conversation.set_unread_flag(false, true); + }, 20 * 1000); /* Lets guess you've read the new messages within 5 seconds */ } + this._current_conversation.fix_scroll(false); + this._current_conversation.entry_tag().addClass("selected"); + this.update_chatbox_state(); + } - update_typing_state() { - this._container_typing.toggleClass("hidden", !this._current_conversation || !this._current_conversation.typing_active()); - } + update_chatbox_state() { + this._chat_box.set_enabled(!!this._current_conversation && this._current_conversation.chat_enabled()); + } - private _build_html_tag() { - this._html_tag = $("#tmpl_frame_chat_private").renderTag({ - chatbox: this._chat_box.html_tag() - }).dividerfy(); - this._container_conversation = this._html_tag.find(".conversation"); - this._container_conversation.on('click', event => { /* lets think if a user clicks within that field that he has read the messages */ - if(this._current_conversation) - this._current_conversation.set_unread_flag(false, true); /* only updates everything if the state changes */ - }); + update_typing_state() { + this._container_typing.toggleClass("hidden", !this._current_conversation || !this._current_conversation.typing_active()); + } - this._container_conversation_messages = this._container_conversation.find(".messages"); - this._container_conversation_messages.on('scroll', event => { - if(!this._current_conversation) - return; - - const current_view = this._container_conversation_messages[0].scrollTop + this._container_conversation_messages[0].clientHeight + this._container_conversation_messages[0].clientHeight * .125; - if(current_view > this._container_conversation_messages[0].scrollHeight) - this._current_conversation._scroll_position = undefined; - else - this._current_conversation._scroll_position = this._container_conversation_messages[0].scrollTop; - }); - - this._container_conversation_list = this._html_tag.find(".conversation-list"); - this._html_no_chats = this._container_conversation_list.find(".no-chats"); - this._container_typing = this._html_tag.find(".container-typing"); - this.update_input_format_helper(); - } - - try_input_focus() { - this._chat_box.focus_input(); - } - - on_show() { + private _build_html_tag() { + this._html_tag = $("#tmpl_frame_chat_private").renderTag({ + chatbox: this._chat_box.html_tag() + }).dividerfy(); + this._container_conversation = this._html_tag.find(".conversation"); + this._container_conversation.on('click', event => { /* lets think if a user clicks within that field that he has read the messages */ if(this._current_conversation) - this._current_conversation.fix_scroll(false); - } + this._current_conversation.set_unread_flag(false, true); /* only updates everything if the state changes */ + }); - update_input_format_helper() { - const tag = this._html_tag.find(".container-format-helper"); - if(settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { - tag.removeClass("hidden").text(tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more...")); - } else { - tag.addClass("hidden"); - } + this._container_conversation_messages = this._container_conversation.find(".messages"); + this._container_conversation_messages.on('scroll', event => { + if(!this._current_conversation) + return; + + const current_view = this._container_conversation_messages[0].scrollTop + this._container_conversation_messages[0].clientHeight + this._container_conversation_messages[0].clientHeight * .125; + if(current_view > this._container_conversation_messages[0].scrollHeight) + this._current_conversation._scroll_position = undefined; + else + this._current_conversation._scroll_position = this._container_conversation_messages[0].scrollTop; + }); + + this._container_conversation_list = this._html_tag.find(".conversation-list"); + this._html_no_chats = this._container_conversation_list.find(".no-chats"); + this._container_typing = this._html_tag.find(".container-typing"); + this.update_input_format_helper(); + } + + try_input_focus() { + this._chat_box.focus_input(); + } + + on_show() { + if(this._current_conversation) + this._current_conversation.fix_scroll(false); + } + + update_input_format_helper() { + const tag = this._html_tag.find(".container-format-helper"); + if(settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)) { + tag.removeClass("hidden").text(tr("*italic*, **bold**, ~~strikethrough~~, `code`, and more...")); + } else { + tag.addClass("hidden"); } } } \ No newline at end of file diff --git a/shared/js/ui/htmltags.ts b/shared/js/ui/htmltags.ts index b208043f..c9180c0a 100644 --- a/shared/js/ui/htmltags.ts +++ b/shared/js/ui/htmltags.ts @@ -1,253 +1,236 @@ -namespace htmltags { - let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0}; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {ClientEntry} from "tc-shared/ui/client"; +import {htmlEscape} from "tc-shared/ui/frames/chat"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; + +let mouse_coordinates: {x: number, y: number} = {x: 0, y: 0}; + +function initialize() { + document.addEventListener('mousemove', event => { + mouse_coordinates.x = event.pageX; + mouse_coordinates.y = event.pageY; + }); +} +initialize(); + +export interface ClientProperties { + client_id: number, + client_unique_id: string, + client_name: string, + add_braces?: boolean, + client_database_id?: number; /* not yet used */ +} + +export interface ChannelProperties { + channel_id: number, + channel_name: string, + channel_display_name?: string, + add_braces?: boolean +} + +/* required for the bbcodes */ +function generate_client_open(properties: ClientProperties) : string { + let result = ""; + + /* build the opening tag:
*/ + result = result + "
"; + return result; +} + +export function generate_client(properties: ClientProperties) : string { + let result = generate_client_open(properties); + /* content */ + { + if(properties.add_braces) + result = result + "\""; + + result = result + htmlEscape(properties.client_name || "undefined").join(" "); + if(properties.add_braces) + result = result + "\""; + } + + /* close tag */ + { + result += "
"; + } + return result; +} + +export function generate_client_object(properties: ClientProperties) : JQuery { + return $(this.generate_client(properties)); +} + +/* required for the bbcodes */ +function generate_channel_open(properties: ChannelProperties) : string { + let result = ""; + + /* build the opening tag:
*/ + result = result + "
"; + return result; +} + +export function generate_channel(properties: ChannelProperties) : string { + let result = generate_channel_open(properties); + /* content */ + { + if(properties.add_braces) + result = result + "\""; + result = result + htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" "); + if(properties.add_braces) + result = result + "\""; + } + + /* close tag */ + { + result += "
"; + } + return result; +} + +export function generate_channel_object(properties: ChannelProperties) : JQuery { + return $(this.generate_channel(properties)); +} + + +export namespace callbacks { + export function callback_context_client(element: JQuery) { + const client_id = parseInt(element.attr("client-id") || "0"); + const client_unique_id = decodeURIComponent(element.attr("client-unique-id") || ""); + /* we ignore the name, we cant find clients by name because the name is too volatile*/ + + let client: ClientEntry; + + const current_connection = server_connections.active_connection_handler(); + if(current_connection && current_connection.channelTree) { + if(!client && client_id) { + client = current_connection.channelTree.findClient(client_id); + if(client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) { + client = undefined; /* client id dosn't match anymore, lets search for the unique id */ + } + } + if(!client && client_unique_id) + client = current_connection.channelTree.find_client_by_unique_id(client_unique_id); + + if(!client) { + if(current_connection.channelTree.server.properties.virtualserver_unique_identifier === client_unique_id) { + current_connection.channelTree.server.spawnContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return; + } + } + } + if(!client) { + + /* we may should open a "offline" menu? */ + log.debug(LogCategory.GENERAL, "Failed to resolve client from html tag. Client id: %o, Client unique id: %o, Client name: %o", + client_id, + client_unique_id, + decodeURIComponent(element.attr("client-name")) + ); + return false; + } + + client.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return false; + } + + export function callback_context_channel(element: JQuery) { + const channel_id = parseInt(element.attr("channel-id") || "0"); + + const current_connection = server_connections.active_connection_handler(); + let channel: ChannelEntry; + if(current_connection && current_connection.channelTree) { + channel = current_connection.channelTree.findChannel(channel_id); + } + + if(!channel) + return false; + + channel.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); + return false; + } +} + +declare const xbbcode; +namespace bbcodes { + /* the = because we sometimes get that */ + //const url_client_regex = /?client:\/\/(?[0-9]+)\/(?[a-zA-Z0-9+=#]+)~(?(?:[^%]|%[0-9A-Fa-f]{2})+)$/g; + const url_client_regex = /client:\/\/([0-9]+)\/([a-zA-Z0-9+=/#]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; /* IDK which browsers already support group naming */ + const url_channel_regex = /channel:\/\/([0-9]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; function initialize() { - document.addEventListener('mousemove', event => { - mouse_coordinates.x = event.pageX; - mouse_coordinates.y = event.pageY; + const origin_url = xbbcode.register.find_parser('url'); + xbbcode.register.register_parser({ + tag: 'url', + build_html_tag_open(layer): string { + if(layer.options) { + if(layer.options.match(url_channel_regex)) { + const groups = url_channel_regex.exec(layer.options); + + return generate_channel_open({ + add_braces: false, + channel_id: parseInt(groups[1]), + channel_name: decodeURIComponent(groups[2]) + }); + } else if(layer.options.match(url_client_regex)) { + const groups = url_client_regex.exec(layer.options); + + return generate_client_open({ + add_braces: false, + client_id: parseInt(groups[1]), + client_unique_id: groups[2], + client_name: decodeURIComponent(groups[3]) + }); + } + } + return origin_url.build_html_tag_open(layer); + }, + build_html_tag_close(layer): string { + if(layer.options) { + if(layer.options.match(url_client_regex)) + return "
"; + if(layer.options.match(url_channel_regex)) + return "
"; + } + return origin_url.build_html_tag_close(layer); + } }); } initialize(); - - export interface ClientProperties { - client_id: number, - client_unique_id: string, - client_name: string, - add_braces?: boolean, - client_database_id?: number; /* not yet used */ - } - - export interface ChannelProperties { - channel_id: number, - channel_name: string, - channel_display_name?: string, - add_braces?: boolean - } - - /* required for the bbcodes */ - function generate_client_open(properties: ClientProperties) : string { - let result = ""; - - /* build the opening tag:
*/ - result = result + "
"; - return result; - } - - export function generate_client(properties: ClientProperties) : string { - let result = generate_client_open(properties); - /* content */ - { - if(properties.add_braces) - result = result + "\""; - - result = result + MessageHelper.htmlEscape(properties.client_name || "undefined").join(" "); - if(properties.add_braces) - result = result + "\""; - } - - /* close tag */ - { - result += "
"; - } - return result; - } - - export function generate_client_object(properties: ClientProperties) : JQuery { - return $(this.generate_client(properties)); - } - - /* required for the bbcodes */ - function generate_channel_open(properties: ChannelProperties) : string { - let result = ""; - - /* build the opening tag:
*/ - result = result + "
"; - return result; - } - - export function generate_channel(properties: ChannelProperties) : string { - let result = generate_channel_open(properties); - /* content */ - { - if(properties.add_braces) - result = result + "\""; - result = result + MessageHelper.htmlEscape(properties.channel_display_name || properties.channel_name || "undefined").join(" "); - if(properties.add_braces) - result = result + "\""; - } - - /* close tag */ - { - result += "
"; - } - return result; - } - - export function generate_channel_object(properties: ChannelProperties) : JQuery { - return $(this.generate_channel(properties)); - } - - - export namespace callbacks { - export function callback_context_client(element: JQuery) { - const client_id = parseInt(element.attr("client-id") || "0"); - const client_unique_id = decodeURIComponent(element.attr("client-unique-id") || ""); - /* we ignore the name, we cant find clients by name because the name is too volatile*/ - - let client: ClientEntry; - - const current_connection = server_connections.active_connection_handler(); - if(current_connection && current_connection.channelTree) { - if(!client && client_id) { - client = current_connection.channelTree.findClient(client_id); - if(client && (client_unique_id && client.properties.client_unique_identifier != client_unique_id)) { - client = undefined; /* client id dosn't match anymore, lets search for the unique id */ - } - } - if(!client && client_unique_id) - client = current_connection.channelTree.find_client_by_unique_id(client_unique_id); - - if(!client) { - if(current_connection.channelTree.server.properties.virtualserver_unique_identifier === client_unique_id) { - current_connection.channelTree.server.spawnContextMenu(mouse_coordinates.x, mouse_coordinates.y); - return; - } - } - } - if(!client) { - - /* we may should open a "offline" menu? */ - log.debug(LogCategory.GENERAL, "Failed to resolve client from html tag. Client id: %o, Client unique id: %o, Client name: %o", - client_id, - client_unique_id, - decodeURIComponent(element.attr("client-name")) - ); - return false; - } - - client.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); - return false; - } - - export function callback_context_channel(element: JQuery) { - const channel_id = parseInt(element.attr("channel-id") || "0"); - - const current_connection = server_connections.active_connection_handler(); - let channel: ChannelEntry; - if(current_connection && current_connection.channelTree) { - channel = current_connection.channelTree.findChannel(channel_id); - } - - if(!channel) - return false; - - channel.showContextMenu(mouse_coordinates.x, mouse_coordinates.y); - return false; - } - } - - namespace bbcodes { - /* the = because we sometimes get that */ - //const url_client_regex = /?client:\/\/(?[0-9]+)\/(?[a-zA-Z0-9+=#]+)~(?(?:[^%]|%[0-9A-Fa-f]{2})+)$/g; - const url_client_regex = /client:\/\/([0-9]+)\/([a-zA-Z0-9+=/#]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; /* IDK which browsers already support group naming */ - const url_channel_regex = /channel:\/\/([0-9]+)~((?:[^%]|%[0-9A-Fa-f]{2})+)$/g; - - function initialize() { - const origin_url = xbbcode.register.find_parser('url'); - xbbcode.register.register_parser({ - tag: 'url', - build_html_tag_open(layer): string { - if(layer.options) { - if(layer.options.match(url_channel_regex)) { - const groups = url_channel_regex.exec(layer.options); - - return generate_channel_open({ - add_braces: false, - channel_id: parseInt(groups[1]), - channel_name: decodeURIComponent(groups[2]) - }); - } else if(layer.options.match(url_client_regex)) { - const groups = url_client_regex.exec(layer.options); - - return generate_client_open({ - add_braces: false, - client_id: parseInt(groups[1]), - client_unique_id: groups[2], - client_name: decodeURIComponent(groups[3]) - }); - } - } - return origin_url.build_html_tag_open(layer); - }, - build_html_tag_close(layer): string { - if(layer.options) { - if(layer.options.match(url_client_regex)) - return "
"; - if(layer.options.match(url_channel_regex)) - return "
"; - } - return origin_url.build_html_tag_close(layer); - } - }) - /* - "img": { - openTag: function(params,content) { - let myUrl; - - if (!params) { - myUrl = content.replace(/<.*?>/g,""); - } else { - myUrl = params.substr(1); - } - - urlPattern.lastIndex = 0; - if ( !urlPattern.test( myUrl ) ) { - myUrl = "#"; - } - - return ''; - }, - closeTag: function(params,content) { - return ''; - } - }, - */ - } - initialize(); - } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalAbout.ts b/shared/js/ui/modal/ModalAbout.ts index 39207f1c..693c5068 100644 --- a/shared/js/ui/modal/ModalAbout.ts +++ b/shared/js/ui/modal/ModalAbout.ts @@ -1,55 +1,54 @@ -/// -/// -/// +import {createModal} from "tc-shared/ui/elements/Modal"; +import * as loader from "tc-loader"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; -namespace Modals { - function format_date(date: number) { - const d = new Date(date); +function format_date(date: number) { + const d = new Date(date); - return ('00' + d.getDay()).substr(-2) + "." + ('00' + d.getMonth()).substr(-2) + "." + d.getFullYear() + " - " + ('00' + d.getHours()).substr(-2) + ":" + ('00' + d.getMinutes()).substr(-2); - } + return ('00' + d.getDay()).substr(-2) + "." + ('00' + d.getMonth()).substr(-2) + "." + d.getFullYear() + " - " + ('00' + d.getHours()).substr(-2) + ":" + ('00' + d.getMinutes()).substr(-2); +} - export function spawnAbout() { - const app_version = (() => { - const version_node = document.getElementById("app_version"); - if(!version_node) return undefined; +export function spawnAbout() { + const app_version = (() => { + const version_node = document.getElementById("app_version"); + if(!version_node) return undefined; - const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; - if(!version) return undefined; + const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; + if(!version) return undefined; - if(version == "unknown" || version.replace(/0+/, "").length == 0) - return undefined; + if(version == "unknown" || version.replace(/0+/, "").length == 0) + return undefined; - return version; - })(); + return version; + })(); - const connectModal = createModal({ - header: tr("About"), - body: () => { - let tag = $("#tmpl_about").renderTag({ - client: !app.is_web(), + const connectModal = createModal({ + header: tr("About"), + body: () => { + let tag = $("#tmpl_about").renderTag({ + client: loader.version().type !== "web", - version_client: app.is_web() ? app_version || "in-dev" : "loading...", - version_ui: app_version || "in-dev", + version_client: loader.version().type === "web" ? app_version || "in-dev" : "loading...", + version_ui: app_version || "in-dev", - version_timestamp: !!app_version ? format_date(Date.now()) : "--" - }); - return tag; - }, - footer: null, - - width: "60em" - }); - connectModal.htmlTag.find(".modal-body").addClass("modal-about"); - connectModal.open(); - - if(!app.is_web()) { - (window as any).native.client_version().then(version => { - connectModal.htmlTag.find(".version-client").text(version); - }).catch(error => { - log.error(LogCategory.GENERAL, tr("Failed to load client version: %o"), error); - connectModal.htmlTag.find(".version-client").text("unknown"); + version_timestamp: !!app_version ? format_date(Date.now()) : "--" }); - } + return tag; + }, + footer: null, + + width: "60em" + }); + connectModal.htmlTag.find(".modal-body").addClass("modal-about"); + connectModal.open(); + + if(loader.version().type !== "web") { + (window as any).native.client_version().then(version => { + connectModal.htmlTag.find(".version-client").text(version); + }).catch(error => { + log.error(LogCategory.GENERAL, tr("Failed to load client version: %o"), error); + connectModal.htmlTag.find(".version-client").text("unknown"); + }); } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalAvatar.ts b/shared/js/ui/modal/ModalAvatar.ts index c5fcac24..9a9fdf71 100644 --- a/shared/js/ui/modal/ModalAvatar.ts +++ b/shared/js/ui/modal/ModalAvatar.ts @@ -1,74 +1,72 @@ -/// -/// -/// +//TODO: Test if we could render this image and not only the browser by knowing the type. +import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal"; +import {tra} from "tc-shared/i18n/localize"; +import {arrayBufferBase64} from "tc-shared/utils/buffers"; -namespace Modals { - //TODO: Test if we could render this image and not only the browser by knowing the type. - export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) { - const modal = createModal({ - header: tr("Avatar Upload"), - footer: undefined, - body: () => { - return $("#tmpl_avatar_upload").renderTag({}); - } +export function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any) { + const modal = createModal({ + header: tr("Avatar Upload"), + footer: undefined, + body: () => { + return $("#tmpl_avatar_upload").renderTag({}); + } + }); + + let _data_submitted = false; + let _current_avatar; + + modal.htmlTag.find(".button-select").on('click', event => { + modal.htmlTag.find(".file-inputs").trigger('click'); + }); + + modal.htmlTag.find(".button-delete").on('click', () => { + if(_data_submitted) + return; + _data_submitted = true; + modal.close(); + callback_data(null); + }); + + modal.htmlTag.find(".button-cancel").on('click', () => modal.close()); + const button_upload = modal.htmlTag.find(".button-upload"); + button_upload.on('click', event => (!_data_submitted) && (_data_submitted = true, modal.close(), true) && callback_data(_current_avatar)); + + const set_avatar = (data: string | undefined, type?: string) => { + _current_avatar = data ? arrayBufferBase64(data) : undefined; + button_upload.prop("disabled", !_current_avatar); + modal.htmlTag.find(".preview img").attr("src", data ? ("data:image/" + type + ";base64," + data) : "img/style/avatar.png"); + }; + + const input_node = modal.htmlTag.find(".file-inputs")[0] as HTMLInputElement; + input_node.multiple = false; + + modal.htmlTag.find(".file-inputs").on('change', event => { + console.log("Files: %o", input_node.files); + + const read_file = (file: File) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = error => reject(error); + + reader.readAsDataURL(file); }); - let _data_submitted = false; - let _current_avatar; + (async () => { + const data = await read_file(input_node.files[0]); - modal.htmlTag.find(".button-select").on('click', event => { - modal.htmlTag.find(".file-inputs").trigger('click'); - }); - - modal.htmlTag.find(".button-delete").on('click', () => { - if(_data_submitted) + if(!data.startsWith("data:image/")) { + console.error(tr("Failed to load file %s: Invalid data media type (%o)"), input_node.files[0].name, data); + createErrorModal(tr("Icon upload failed"), tra("Failed to select avatar {}.
File is not an image", input_node.files[0].name)).open(); return; - _data_submitted = true; - modal.close(); - callback_data(null); - }); + } + const semi = data.indexOf(';'); + const type = data.substring(11, semi); + console.log(tr("Given image has type %s"), type); - modal.htmlTag.find(".button-cancel").on('click', () => modal.close()); - const button_upload = modal.htmlTag.find(".button-upload"); - button_upload.on('click', event => (!_data_submitted) && (_data_submitted = true, modal.close(), true) && callback_data(_current_avatar)); - - const set_avatar = (data: string | undefined, type?: string) => { - _current_avatar = data ? arrayBufferBase64(data) : undefined; - button_upload.prop("disabled", !_current_avatar); - modal.htmlTag.find(".preview img").attr("src", data ? ("data:image/" + type + ";base64," + data) : "img/style/avatar.png"); - }; - - const input_node = modal.htmlTag.find(".file-inputs")[0] as HTMLInputElement; - input_node.multiple = false; - - modal.htmlTag.find(".file-inputs").on('change', event => { - console.log("Files: %o", input_node.files); - - const read_file = (file: File) => new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result as string); - reader.onerror = error => reject(error); - - reader.readAsDataURL(file); - }); - - (async () => { - const data = await read_file(input_node.files[0]); - - if(!data.startsWith("data:image/")) { - console.error(tr("Failed to load file %s: Invalid data media type (%o)"), input_node.files[0].name, data); - createErrorModal(tr("Icon upload failed"), tra("Failed to select avatar {}.
File is not an image", input_node.files[0].name)).open(); - return; - } - const semi = data.indexOf(';'); - const type = data.substring(11, semi); - console.log(tr("Given image has type %s"), type); - - set_avatar(data.substr(semi + 8 /* 8 bytes := base64, */), type); - })(); - }); - set_avatar(undefined); - modal.close_listener.push(() => !_data_submitted && callback_data(undefined)); - modal.open(); - } + set_avatar(data.substr(semi + 8 /* 8 bytes := base64, */), type); + })(); + }); + set_avatar(undefined); + modal.close_listener.push(() => !_data_submitted && callback_data(undefined)); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalAvatarList.ts b/shared/js/ui/modal/ModalAvatarList.ts index 021430f2..1131c40e 100644 --- a/shared/js/ui/modal/ModalAvatarList.ts +++ b/shared/js/ui/modal/ModalAvatarList.ts @@ -1,162 +1,166 @@ -/// -/// -/// +import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal"; +import {LogCategory} from "tc-shared/log"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {base64_encode_ab} from "tc-shared/utils/buffers"; +import {media_image_type} from "tc-shared/FileManager"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {ClientEntry} from "tc-shared/ui/client"; +import * as log from "tc-shared/log"; -namespace Modals { - const avatar_to_uid = (id: string) => { - const buffer = new Uint8Array(id.length / 2); - for(let index = 0; index < id.length; index += 2) { - const upper_nibble = id.charCodeAt(index) - 97; - const lower_nibble = id.charCodeAt(index + 1) - 97; - buffer[index / 2] = (upper_nibble << 4) | lower_nibble; +const avatar_to_uid = (id: string) => { + const buffer = new Uint8Array(id.length / 2); + for(let index = 0; index < id.length; index += 2) { + const upper_nibble = id.charCodeAt(index) - 97; + const lower_nibble = id.charCodeAt(index + 1) - 97; + buffer[index / 2] = (upper_nibble << 4) | lower_nibble; + } + return base64_encode_ab(buffer); +}; + +export const human_file_size = (size: number) => { + if(size < 1000) + return size + "B"; + const exp = Math.floor(Math.log2(size) / 10); + return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB"; +}; + +declare const moment; +export function spawnAvatarList(client: ConnectionHandler) { + const modal = createModal({ + header: tr("Avatars"), + footer: undefined, + body: () => { + const template = $("#tmpl_avatar_list").renderTag({}); + + return template; } - return base64_encode_ab(buffer); - }; + }); - export const human_file_size = (size: number) => { - if(size < 1000) - return size + "B"; - const exp = Math.floor(Math.log2(size) / 10); - return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB"; - }; + let callback_download: () => any; + let callback_delete: () => any; - export function spawnAvatarList(client: ConnectionHandler) { - const modal = createModal({ - header: tr("Avatars"), - footer: undefined, - body: () => { - const template = $("#tmpl_avatar_list").renderTag({}); + const button_download = modal.htmlTag.find(".button-download"); + const button_delete = modal.htmlTag.find(".button-delete"); + const container_list = modal.htmlTag.find(".container-list .list-entries-container"); + const list_entries = container_list.find(".list-entries"); + const container_info = modal.htmlTag.find(".container-info"); + const overlay_no_user = container_info.find(".disabled-overlay").show(); - return template; + const set_selected_avatar = (unique_id: string, avatar_id: string, size: number) => { + button_download.prop("disabled", true); + callback_download = undefined; + if(!unique_id) { + overlay_no_user.show(); + return; + } + + const tag_username = container_info.find(".property-username"); + const tag_unique_id = container_info.find(".property-unique-id"); + const tag_avatar_id = container_info.find(".property-avatar-id"); + const container_avatar = container_info.find(".container-image"); + const tag_image_bytes = container_info.find(".property-image-size"); + const tag_image_width = container_info.find(".property-image-width").val(tr("loading...")); + const tag_image_height = container_info.find(".property-image-height").val(tr("loading...")); + const tag_image_type = container_info.find(".property-image-type").val(tr("loading...")); + + tag_username.val("unknown"); + tag_unique_id.val(unique_id); + tag_avatar_id.val(avatar_id); + tag_image_bytes.val(size); + + container_avatar.empty().append(client.fileManager.avatars.generate_tag(avatar_id, undefined, { + callback_image: image => { + tag_image_width.val(image[0].naturalWidth + 'px'); + tag_image_height.val(image[0].naturalHeight + 'px'); + }, + callback_avatar: avatar => { + tag_image_type.val(media_image_type(avatar.type)); + button_download.prop("disabled", false); + + callback_download = () => { + const element = $.spawn("a") + .text("download") + .attr("href", avatar.url) + .attr("download", "avatar-" + unique_id + "." + media_image_type(avatar.type, true)) + .css("display", "none") + .appendTo($("body")); + element[0].click(); + element.remove(); + }; } - }); + })); - let callback_download: () => any; - let callback_delete: () => any; - - const button_download = modal.htmlTag.find(".button-download"); - const button_delete = modal.htmlTag.find(".button-delete"); - const container_list = modal.htmlTag.find(".container-list .list-entries-container"); - const list_entries = container_list.find(".list-entries"); - const container_info = modal.htmlTag.find(".container-info"); - const overlay_no_user = container_info.find(".disabled-overlay").show(); - - const set_selected_avatar = (unique_id: string, avatar_id: string, size: number) => { - button_download.prop("disabled", true); - callback_download = undefined; - if(!unique_id) { - overlay_no_user.show(); - return; - } - - const tag_username = container_info.find(".property-username"); - const tag_unique_id = container_info.find(".property-unique-id"); - const tag_avatar_id = container_info.find(".property-avatar-id"); - const container_avatar = container_info.find(".container-image"); - const tag_image_bytes = container_info.find(".property-image-size"); - const tag_image_width = container_info.find(".property-image-width").val(tr("loading...")); - const tag_image_height = container_info.find(".property-image-height").val(tr("loading...")); - const tag_image_type = container_info.find(".property-image-type").val(tr("loading...")); - - tag_username.val("unknown"); - tag_unique_id.val(unique_id); - tag_avatar_id.val(avatar_id); - tag_image_bytes.val(size); - - container_avatar.empty().append(client.fileManager.avatars.generate_tag(avatar_id, undefined, { - callback_image: image => { - tag_image_width.val(image[0].naturalWidth + 'px'); - tag_image_height.val(image[0].naturalHeight + 'px'); - }, - callback_avatar: avatar => { - tag_image_type.val(media_image_type(avatar.type)); - button_download.prop("disabled", false); - - callback_download = () => { - const element = $.spawn("a") - .text("download") - .attr("href", avatar.url) - .attr("download", "avatar-" + unique_id + "." + media_image_type(avatar.type, true)) - .css("display", "none") - .appendTo($("body")); - element[0].click(); - element.remove(); - }; + callback_delete = () => { + spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this avatar?"), result => { + if(result) { + createErrorModal(tr("Not implemented"), tr("Avatar delete hasn't implemented yet")).open(); + //TODO Implement avatar delete } - })); - - callback_delete = () => { - spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this avatar?"), result => { - if(result) { - createErrorModal(tr("Not implemented"), tr("Avatar delete hasn't implemented yet")).open(); - //TODO Implement avatar delete - } - }); - }; - overlay_no_user.hide(); - }; - set_selected_avatar(undefined, undefined, 0); - - const update_avatar_list = () => { - const template_entry = $("#tmpl_avatar_list-list_entry"); - list_entries.empty(); - - client.fileManager.requestFileList("/").then(files => { - const username_resolve: {[unique_id: string]:((name:string) => any)[]} = {}; - for(const entry of files) { - const avatar_id = entry.name.substr('avatar_'.length); - const unique_id = avatar_to_uid(avatar_id); - - const tag = template_entry.renderTag({ - username: 'loading', - unique_id: unique_id, - size: human_file_size(entry.size), - timestamp: moment(entry.datetime * 1000).format('YY-MM-DD HH:mm') - }); - - (username_resolve[unique_id] || (username_resolve[unique_id] = [])).push(name => { - const tag_username = tag.find(".column-username").empty(); - if(name) { - tag_username.append(ClientEntry.chatTag(0, name, unique_id, false)); - } else { - tag_username.text("unknown"); - } - }); - list_entries.append(tag); - - tag.on('click', () => { - list_entries.find('.selected').removeClass('selected'); - tag.addClass('selected'); - - set_selected_avatar(unique_id, avatar_id, entry.size); - }); - } - - if(container_list.hasScrollBar()) - container_list.addClass("scrollbar"); - - client.serverConnection.command_helper.info_from_uid(...Object.keys(username_resolve)).then(result => { - for(const info of result) { - username_resolve[info.client_unique_id].forEach(e => e(info.client_nickname)); - delete username_resolve[info.client_unique_id]; - } - for(const uid of Object.keys(username_resolve)) { - (username_resolve[uid] || []).forEach(e => e(undefined)); - } - }).catch(error => { - log.error(LogCategory.GENERAL, tr("Failed to fetch usernames from avatar names. Error: %o"), error); - createErrorModal(tr("Failed to fetch usernames"), tr("Failed to fetch usernames related to their avatar names"), undefined).open(); - }) - }).catch(error => { - //TODO: Display no perms error - log.error(LogCategory.GENERAL, tr("Failed to receive avatar list. Error: %o"), error); - createErrorModal(tr("Failed to list avatars"), tr("Failed to receive avatar list."), undefined).open(); }); }; + overlay_no_user.hide(); + }; + set_selected_avatar(undefined, undefined, 0); - button_download.on('click', () => (callback_download || (() => {}))()); - button_delete.on('click', () => (callback_delete || (() => {}))()); - setTimeout(() => update_avatar_list(), 250); - modal.open(); - } + const update_avatar_list = () => { + const template_entry = $("#tmpl_avatar_list-list_entry"); + list_entries.empty(); + + client.fileManager.requestFileList("/").then(files => { + const username_resolve: {[unique_id: string]:((name:string) => any)[]} = {}; + for(const entry of files) { + const avatar_id = entry.name.substr('avatar_'.length); + const unique_id = avatar_to_uid(avatar_id); + + const tag = template_entry.renderTag({ + username: 'loading', + unique_id: unique_id, + size: human_file_size(entry.size), + timestamp: moment(entry.datetime * 1000).format('YY-MM-DD HH:mm') + }); + + (username_resolve[unique_id] || (username_resolve[unique_id] = [])).push(name => { + const tag_username = tag.find(".column-username").empty(); + if(name) { + tag_username.append(ClientEntry.chatTag(0, name, unique_id, false)); + } else { + tag_username.text("unknown"); + } + }); + list_entries.append(tag); + + tag.on('click', () => { + list_entries.find('.selected').removeClass('selected'); + tag.addClass('selected'); + + set_selected_avatar(unique_id, avatar_id, entry.size); + }); + } + + if(container_list.hasScrollBar()) + container_list.addClass("scrollbar"); + + client.serverConnection.command_helper.info_from_uid(...Object.keys(username_resolve)).then(result => { + for(const info of result) { + username_resolve[info.client_unique_id].forEach(e => e(info.client_nickname)); + delete username_resolve[info.client_unique_id]; + } + for(const uid of Object.keys(username_resolve)) { + (username_resolve[uid] || []).forEach(e => e(undefined)); + } + }).catch(error => { + log.error(LogCategory.GENERAL, tr("Failed to fetch usernames from avatar names. Error: %o"), error); + createErrorModal(tr("Failed to fetch usernames"), tr("Failed to fetch usernames related to their avatar names"), undefined).open(); + }) + }).catch(error => { + //TODO: Display no perms error + log.error(LogCategory.GENERAL, tr("Failed to receive avatar list. Error: %o"), error); + createErrorModal(tr("Failed to list avatars"), tr("Failed to receive avatar list."), undefined).open(); + }); + }; + + button_download.on('click', () => (callback_download || (() => {}))()); + button_delete.on('click', () => (callback_delete || (() => {}))()); + setTimeout(() => update_avatar_list(), 250); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalBanClient.ts b/shared/js/ui/modal/ModalBanClient.ts index bb7b0627..a479a68d 100644 --- a/shared/js/ui/modal/ModalBanClient.ts +++ b/shared/js/ui/modal/ModalBanClient.ts @@ -2,179 +2,183 @@ /// /// -namespace Modals { - export type BanEntry = { - name?: string; - unique_id: string; - } - export function spawnBanClient(client: ConnectionHandler, entries: BanEntry | BanEntry[], callback: (data: { - length: number, - reason: string, - no_name: boolean, - no_ip: boolean, - no_hwid: boolean - }) => void) { - const max_ban_time = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value; +import PermissionType from "tc-shared/permission/PermissionType"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {createModal} from "tc-shared/ui/elements/Modal"; +import {duration_data} from "tc-shared/ui/modal/ModalBanList"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; - const permission_criteria_hwid = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_HWID).granted(1); - const permission_criteria_ip = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_IP).granted(1); - const permission_criteria_name = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_NAME).granted(1); +export type BanEntry = { + name?: string; + unique_id: string; +} +export function spawnBanClient(client: ConnectionHandler, entries: BanEntry | BanEntry[], callback: (data: { + length: number, + reason: string, + no_name: boolean, + no_ip: boolean, + no_hwid: boolean +}) => void) { + const max_ban_time = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value; - const modal = createModal({ - header: Array.isArray(entries) ? tr("Ban clients") : tr("Ban client"), - body: function () { - let template = $("#tmpl_client_ban").renderTag({entries: entries}); + const permission_criteria_hwid = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_HWID).granted(1); + const permission_criteria_ip = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_IP).granted(1); + const permission_criteria_name = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_NAME).granted(1); - let update_duration; - let update_button_ok; - const button_ok = template.find(".button-apply"); - const button_cancel = template.find(".button-cancel"); + const modal = createModal({ + header: Array.isArray(entries) ? tr("Ban clients") : tr("Ban client"), + body: function () { + let template = $("#tmpl_client_ban").renderTag({entries: entries}); - const input_duration_value = template.find(".container-duration input").on('change keyup', () => update_duration()); - const input_duration_type = template.find(".container-duration select").on('change keyup', () => update_duration()); + let update_duration; + let update_button_ok; + const button_ok = template.find(".button-apply"); + const button_cancel = template.find(".button-cancel"); - const container_reason = template.find(".container-reason"); + const input_duration_value = template.find(".container-duration input").on('change keyup', () => update_duration()); + const input_duration_type = template.find(".container-duration select").on('change keyup', () => update_duration()); - const criteria_nickname = template.find(".criteria.nickname input") - .prop('checked', permission_criteria_name).prop("disabled", !permission_criteria_name) - .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_name); + const container_reason = template.find(".container-reason"); - const criteria_ip_address = template.find(".criteria.ip-address input") - .prop('checked', permission_criteria_ip).prop("disabled", !permission_criteria_ip) - .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_ip); + const criteria_nickname = template.find(".criteria.nickname input") + .prop('checked', permission_criteria_name).prop("disabled", !permission_criteria_name) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_name); - const criteria_hardware_id = template.find(".criteria.hardware-id input") - .prop('checked', permission_criteria_hwid).prop("disabled", !permission_criteria_hwid) - .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_hwid); + const criteria_ip_address = template.find(".criteria.ip-address input") + .prop('checked', permission_criteria_ip).prop("disabled", !permission_criteria_ip) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_ip); - /* duration input handler */ - { - const tooltip_duration_max = template.find(".tooltip-max-time a.max"); + const criteria_hardware_id = template.find(".criteria.hardware-id input") + .prop('checked', permission_criteria_hwid).prop("disabled", !permission_criteria_hwid) + .firstParent(".checkbox").toggleClass("disabled", !permission_criteria_hwid); - update_duration = () => { - const type = input_duration_type.val() as string; - const value = parseInt(input_duration_value.val() as string); - const disabled = input_duration_type.prop("disabled"); + /* duration input handler */ + { + const tooltip_duration_max = template.find(".tooltip-max-time a.max"); - input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); - if(type !== "perm") { - if(input_duration_value.attr("x-saved-value")) { - input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); - input_duration_value.attr("x-saved-value", null); - } + update_duration = () => { + const type = input_duration_type.val() as string; + const value = parseInt(input_duration_value.val() as string); + const disabled = input_duration_type.prop("disabled"); - const selected_option = input_duration_type.find("option[value='" + type + "']"); - const max = parseInt(selected_option.attr("duration-max")); + input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); + if(type !== "perm") { + if(input_duration_value.attr("x-saved-value")) { + input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); + input_duration_value.attr("x-saved-value", null); + } - input_duration_value.attr("max", max); - if((value > max && max != -1) || value < 1) { - input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); - } else { - input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); - } + const selected_option = input_duration_type.find("option[value='" + type + "']"); + const max = parseInt(selected_option.attr("duration-max")); - if(max != -1) - tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + ""); - else - tooltip_duration_max.html(tr("You're allowed to ban permanent.")); + input_duration_value.attr("max", max); + if((value > max && max != -1) || value < 1) { + input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); } else { - if(value && !Number.isNaN(value)) - input_duration_value.attr("x-saved-value", value); - input_duration_value.attr("placeholder", tr("for ever")).val(null); + input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); + } + + if(max != -1) + tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + ""); + else tooltip_duration_max.html(tr("You're allowed to ban permanent.")); - } - update_button_ok && update_button_ok(); - }; + } else { + if(value && !Number.isNaN(value)) + input_duration_value.attr("x-saved-value", value); + input_duration_value.attr("placeholder", tr("for ever")).val(null); + tooltip_duration_max.html(tr("You're allowed to ban permanent.")); + } + update_button_ok && update_button_ok(); + }; - /* initialize ban time */ - Promise.resolve(max_ban_time).catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { - let unlimited = max_time == 0 || max_time == -1; - if(unlimited || typeof(max_time) === "undefined") max_time = 0; + /* initialize ban time */ + Promise.resolve(max_ban_time).catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { + let unlimited = max_time == 0 || max_time == -1; + if(unlimited || typeof(max_time) === "undefined") max_time = 0; - for(const value of Object.keys(duration_data)) { - input_duration_type.find("option[value='" + value + "']") - .prop("disabled", !unlimited && max_time >= duration_data[value].scale) - .attr("duration-scale", duration_data[value].scale) - .attr("duration-max", unlimited ? -1 : Math.floor(max_time / duration_data[value].scale)); - } - - input_duration_type.find("option[value='perm']") - .prop("disabled", !unlimited) - .attr("duration-scale", 0) - .attr("duration-max", -1); - update_duration(); - }); + for(const value of Object.keys(duration_data)) { + input_duration_type.find("option[value='" + value + "']") + .prop("disabled", !unlimited && max_time >= duration_data[value].scale) + .attr("duration-scale", duration_data[value].scale) + .attr("duration-max", unlimited ? -1 : Math.floor(max_time / duration_data[value].scale)); + } + input_duration_type.find("option[value='perm']") + .prop("disabled", !unlimited) + .attr("duration-scale", 0) + .attr("duration-max", -1); update_duration(); - } + }); - /* ban reason */ - { - const input = container_reason.find("textarea"); + update_duration(); + } - const insert_tag = (open: string, close: string) => { - if(input.prop("disabled")) - return; + /* ban reason */ + { + const input = container_reason.find("textarea"); - const node = input[0] as HTMLTextAreaElement; - if (node.selectionStart || node.selectionStart == 0) { - const startPos = node.selectionStart; - const endPos = node.selectionEnd; - node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); - node.selectionEnd = endPos + open.length; - node.selectionStart = node.selectionEnd; - } else { - node.value += open + close; - node.selectionEnd = node.value.length - close.length; - node.selectionStart = node.selectionEnd; - } + const insert_tag = (open: string, close: string) => { + if(input.prop("disabled")) + return; - input.focus().trigger('change'); - }; + const node = input[0] as HTMLTextAreaElement; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; + } else { + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; + } - container_reason.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); - container_reason.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); - container_reason.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); - container_reason.find(".button-color input").on('change', event => { - insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') + input.focus().trigger('change'); + }; + + container_reason.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container_reason.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container_reason.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container_reason.find(".button-color input").on('change', event => { + insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') + }); + } + + /* buttons */ + { + button_cancel.on('click', event => modal.close()); + button_ok.on('click', event => { + const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); + + modal.close(); + callback({ + length: Math.floor(duration / 1000), + reason: container_reason.find("textarea").val() as string, + + no_hwid: !criteria_hardware_id.find("input").prop("checked"), + no_ip: !criteria_ip_address.find("input").prop("checked"), + no_name: !criteria_nickname.find("input").prop("checked") }); - } + }); - /* buttons */ - { - button_cancel.on('click', event => modal.close()); - button_ok.on('click', event => { - const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); + const inputs = template.find(".input-boxed"); + update_button_ok = () => { + const invalid = [...inputs].find(e => $(e).hasClass("is-invalid")); + button_ok.prop('disabled', !!invalid); + }; + update_button_ok(); + } - modal.close(); - callback({ - length: Math.floor(duration / 1000), - reason: container_reason.find("textarea").val() as string, + tooltip.initialize(template); + return template.children(); + }, + footer: null, - no_hwid: !criteria_hardware_id.find("input").prop("checked"), - no_ip: !criteria_ip_address.find("input").prop("checked"), - no_name: !criteria_nickname.find("input").prop("checked") - }); - }); + min_width: "10em", + width: "30em" + }); + modal.open(); - const inputs = template.find(".input-boxed"); - update_button_ok = () => { - const invalid = [...inputs].find(e => $(e).hasClass("is-invalid")); - button_ok.prop('disabled', !!invalid); - }; - update_button_ok(); - } - - tooltip(template); - return template.children(); - }, - footer: null, - - min_width: "10em", - width: "30em" - }); - modal.open(); - - modal.htmlTag.find(".modal-body").addClass("modal-ban-client"); - } + modal.htmlTag.find(".modal-body").addClass("modal-ban-client"); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index e5460964..20f1cc31 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -1,921 +1,924 @@ -/// -/// -/// -/// +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {createErrorModal, createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {SingleCommandHandler} from "tc-shared/connection/ConnectionBase"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import * as htmltags from "tc-shared/ui/htmltags"; +import {format_time, formatMessage} from "tc-shared/ui/frames/chat"; -namespace Modals { - export function openBanList(client: ConnectionHandler) { - let modal: Modal; +export function openBanList(client: ConnectionHandler) { + let modal: Modal; - let _callback_bans; - let _callback_triggers; - const single_ban_handler: connection.SingleCommandHandler = { - command: "notifybanlist", - function: command => { - const json = command.arguments; + let _callback_bans; + let _callback_triggers; + const single_ban_handler: SingleCommandHandler = { + command: "notifybanlist", + function: command => { + const json = command.arguments; - let bans: BanEntry[] = []; - for(const entry of json) { - bans.push({ - server_id: parseInt(entry["sid"]), - banid: parseInt(entry["banid"]), - ip: entry["ip"], - name: entry["name"], - unique_id: entry["uid"], - hardware_id: entry["hwid"], + let bans: BanEntry[] = []; + for(const entry of json) { + bans.push({ + server_id: parseInt(entry["sid"]), + banid: parseInt(entry["banid"]), + ip: entry["ip"], + name: entry["name"], + unique_id: entry["uid"], + hardware_id: entry["hwid"], - timestamp_created: (parseInt(entry["created"]) * 1000), - timestamp_expire: (parseInt(entry["duration"]) > 0 ? parseInt(entry["created"]) * 1000 + parseInt(entry["duration"]) * 1000 : 0), + timestamp_created: (parseInt(entry["created"]) * 1000), + timestamp_expire: (parseInt(entry["duration"]) > 0 ? parseInt(entry["created"]) * 1000 + parseInt(entry["duration"]) * 1000 : 0), - invoker_name: entry["invokername"], - invoker_database_id: parseInt(entry["invokercldbid"]), - invoker_unique_id: entry["invokeruid"], - reason: entry["reason"], + invoker_name: entry["invokername"], + invoker_database_id: parseInt(entry["invokercldbid"]), + invoker_unique_id: entry["invokeruid"], + reason: entry["reason"], - enforcements: parseInt(entry["enforcements"]), - flag_own: entry["invokeruid"] == client.getClient().properties.client_unique_identifier - }); - } - - _callback_bans(bans); - return false; /* do not remove me */ - } - }; - const single_trigger_handler: connection.SingleCommandHandler = { - command: "notifybantriggerlist", - function: command => { - //TODO: Test the server id in the response? - const json = command.arguments; - - let triggers: TriggerEntry[] = []; - for(const entry of json) { - triggers.push({ - unique_id: entry["client_unique_identifier"], - client_nickname: entry["client_nickname"], - hardware_id: entry["client_hardware_identifier"], - connection_ip: entry["connection_client_ip"], - - timestamp: parseInt(entry["timestamp"]) - }); - } - - _callback_triggers(triggers); - return false; /* do not remove me */ - } - }; - - const controller: BanListController = { - request_list(_callback): Promise { - _callback_bans = _callback; - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 2500); - - const cleanup = () => { - clearTimeout(timeout); - _callback_bans = undefined; - }; - - Promise.all([ - client.serverConnection.send_command("banlist", { sid: 0 }, {process_result: false}).catch(error => { - //TODO: May lookup for permissions - }), - client.serverConnection.send_command("banlist").catch(async error => { - if(error instanceof CommandResult) - if(error.id === ErrorID.EMPTY_RESULT) - return; - throw error; - }) - ]).then(() => { - if(_callback_bans) resolve(); - cleanup(); - }).catch(error => { - if(_callback_bans) reject(error); - cleanup(); - }); + enforcements: parseInt(entry["enforcements"]), + flag_own: entry["invokeruid"] == client.getClient().properties.client_unique_identifier }); - }, - request_trigger_list(ban, _callback): Promise { - _callback_triggers = _callback; - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 2500); + } - const cleanup = () => { - clearTimeout(timeout); - _callback_triggers = undefined; - }; + _callback_bans(bans); + return false; /* do not remove me */ + } + }; + const single_trigger_handler: SingleCommandHandler = { + command: "notifybantriggerlist", + function: command => { + //TODO: Test the server id in the response? + const json = command.arguments; - const data = {banid: ban.ban_id}; - if(typeof ban.server_id !== "undefined") - data["sid"] = ban.server_id; - client.serverConnection.send_command("bantriggerlist", data).catch(async error => { + let triggers: TriggerEntry[] = []; + for(const entry of json) { + triggers.push({ + unique_id: entry["client_unique_identifier"], + client_nickname: entry["client_nickname"], + hardware_id: entry["client_hardware_identifier"], + connection_ip: entry["connection_client_ip"], + + timestamp: parseInt(entry["timestamp"]) + }); + } + + _callback_triggers(triggers); + return false; /* do not remove me */ + } + }; + + const controller: BanListController = { + request_list(_callback): Promise { + _callback_bans = _callback; + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 2500); + + const cleanup = () => { + clearTimeout(timeout); + _callback_bans = undefined; + }; + + Promise.all([ + client.serverConnection.send_command("banlist", { sid: 0 }, {process_result: false}).catch(error => { + //TODO: May lookup for permissions + }), + client.serverConnection.send_command("banlist").catch(async error => { if(error instanceof CommandResult) if(error.id === ErrorID.EMPTY_RESULT) return; throw error; - }).then(() => { - if(_callback_triggers) resolve(); - cleanup(); - }).catch(error => { - if(_callback_triggers) reject(error); - cleanup(); - }); + }) + ]).then(() => { + if(_callback_bans) resolve(); + cleanup(); + }).catch(error => { + if(_callback_bans) reject(error); + cleanup(); }); - }, - async max_bantime(): Promise { - const value = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value || 0; - return value == -2 ? 0 : value; - }, - async permission_add(): Promise { - return [ - client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE).granted(1), - client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE_GLOBAL).granted(1) - ]; - }, - async permission_edit(): Promise { - return [ - client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT).granted(1), - client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT_GLOBAL).granted(1) && false - ]; - }, - add_ban(entry: BanEntry): Promise { - const data = {}; + }); + }, + request_trigger_list(ban, _callback): Promise { + _callback_triggers = _callback; + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 2500); - if(entry.ip) data["ip"] = entry.ip; - if(entry.name) data["name"] = entry.name; - if(entry.unique_id) data["uid"] = entry.unique_id; - if(entry.hardware_id) data["hwid"] = entry.hardware_id; - if(entry.reason) data["banreason"] = entry.reason; - if(entry.timestamp_expire) data["time"] = Math.floor((entry.timestamp_expire - entry.timestamp_created) / 1000); - if(typeof(entry.server_id) === "number") data["sid"] = entry.server_id; - - return client.serverConnection.send_command("banadd", data).then(e => { if(!e.success) throw e; }); - }, - edit_ban(data: any): Promise { - return client.serverConnection.send_command("banedit", data).then(e => { if(!e.success) throw e; }); - }, - delete_ban(entry_id, server_id): Promise { - const data = { - banid: entry_id + const cleanup = () => { + clearTimeout(timeout); + _callback_triggers = undefined; }; - if(typeof(server_id) === "number") - data["sid"] = server_id; - return client.serverConnection.send_command("bandel", data).then(e => { if(!e.success) throw e; }); - } - }; - - modal = createModal({ - header: tr("Server Banlist"), - body: () => generate_dom(controller), - footer: null, - - width: '60em' - }); - - client.serverConnection.command_handler_boss().register_single_handler(single_ban_handler); - client.serverConnection.command_handler_boss().register_single_handler(single_trigger_handler); - modal.close_listener.push(() => { - client.serverConnection.command_handler_boss().remove_single_handler(single_ban_handler); - client.serverConnection.command_handler_boss().remove_single_handler(single_trigger_handler); - }); - - //TODO: Test without dividerfy! - modal.htmlTag.dividerfy(); - modal.htmlTag.find(".modal-body").addClass("modal-ban-list"); - modal.open(); - } - - interface BanEntry { - server_id: number; - banid: number; - - name?: string; - name_type?: number; - - unique_id?: string; - ip?: string; - hardware_id?: string; - - reason: string; - invoker_name: string; - invoker_unique_id?: string; - invoker_database_id?: number; - - timestamp_created: number; - timestamp_expire: number; - - enforcements: number; - - flag_own?: boolean; - } - - interface TriggerEntry { - unique_id?: string; - client_nickname?: string; - hardware_id?: string; - connection_ip: string; - - timestamp: number; - } - - interface BanListController { - request_list(callback_bans: (entries: BanEntry[]) => any) : Promise; - request_trigger_list(ban: {ban_id: number, server_id: number | undefined}, callback_triggers: (entries: TriggerEntry[]) => any) : Promise; - - max_bantime() : Promise; - permission_edit() : Promise; - permission_add() : Promise; - - add_ban(entry: BanEntry) : Promise; - edit_ban(data: any) : Promise; - delete_ban(entry_id: number, server_id: number | undefined) : Promise; - } - - //Note: This object must be sorted (from shortest to longest)! - export const duration_data = { - "sec": { - "text": tr("Seconds"), - "1-text": tr("Second"), - - scale: 1 + const data = {banid: ban.ban_id}; + if(typeof ban.server_id !== "undefined") + data["sid"] = ban.server_id; + client.serverConnection.send_command("bantriggerlist", data).catch(async error => { + if(error instanceof CommandResult) + if(error.id === ErrorID.EMPTY_RESULT) + return; + throw error; + }).then(() => { + if(_callback_triggers) resolve(); + cleanup(); + }).catch(error => { + if(_callback_triggers) reject(error); + cleanup(); + }); + }); }, - "min": { - "text": tr("Minutes"), - "1-text": tr("Minute"), - - scale: 60 + async max_bantime(): Promise { + const value = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value || 0; + return value == -2 ? 0 : value; }, - "hours": { - "text": tr("Hours"), - "1-text": tr("Hour"), - - scale: 3600 + async permission_add(): Promise { + return [ + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE).granted(1), + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_CREATE_GLOBAL).granted(1) + ]; }, - "days": { - "text": tr("Days"), - "1-text": tr("Day"), - - scale: 86400 + async permission_edit(): Promise { + return [ + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT).granted(1), + client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_EDIT_GLOBAL).granted(1) && false + ]; }, + add_ban(entry: BanEntry): Promise { + const data = {}; + + if(entry.ip) data["ip"] = entry.ip; + if(entry.name) data["name"] = entry.name; + if(entry.unique_id) data["uid"] = entry.unique_id; + if(entry.hardware_id) data["hwid"] = entry.hardware_id; + if(entry.reason) data["banreason"] = entry.reason; + if(entry.timestamp_expire) data["time"] = Math.floor((entry.timestamp_expire - entry.timestamp_created) / 1000); + if(typeof(entry.server_id) === "number") data["sid"] = entry.server_id; + + return client.serverConnection.send_command("banadd", data).then(e => { if(!e.success) throw e; }); + }, + edit_ban(data: any): Promise { + return client.serverConnection.send_command("banedit", data).then(e => { if(!e.success) throw e; }); + }, + delete_ban(entry_id, server_id): Promise { + const data = { + banid: entry_id + }; + if(typeof(server_id) === "number") + data["sid"] = server_id; + + return client.serverConnection.send_command("bandel", data).then(e => { if(!e.success) throw e; }); + } }; - function generate_dom(controller: BanListController) : JQuery { - const template = $("#tmpl_ban_list").renderTag(); - - let callback_ban_filter: ((text: string, flag_own: boolean, highlight_own: boolean) => boolean)[] = []; - let callback_trigger_filter: ((text: string) => boolean)[] = []; - let selected_ban: BanEntry | undefined; - let update_edit_window: (switch_to: boolean) => any; - let update_ban_filter: () => any; - let update_trigger_filter: () => any; - - const container_ban = template.find(".container-banlist"); - const container_ban_entries = container_ban.find(".container-list .body"); - const container_ban_entries_empty = container_ban.find(".container-list .container-empty"); - const container_ban_entries_error = container_ban.find(".container-list .container-error"); - - const container_trigger = template.find(".container-triggerlist").hide(); - const container_trigger_entries = container_trigger.find(".container-list .body"); - const container_trigger_entries_empty = container_trigger.find(".container-list .container-empty"); - const container_trigger_entries_error = container_trigger.find(".container-list .container-error"); - - const button_apply = template.find(".button-apply"); - let button_apply_state = [false, false]; /* first index is add; second index is edit */ - let update_category_inputs: (() => any)[] = [undefined, undefined]; - let button_apply_state_index = 1; - - const category_add = template.find(".left .head .category-add"); - const category_edit = template.find(".left .head .category-edit"); - const container_add = template.find(".left .container-add"); - const container_add_no_permissions = template.find(".left .container-add .container-no-permissions"); - const container_edit = template.find(".left .container-edit"); - - const seperator_top = template.find(".container-seperator .top"); - - /* [local; global] */ - let permission_edit: boolean[] = [false, false], permission_add: boolean[] = [false, false]; - - container_add_no_permissions.hide(); - controller.permission_add().then(result => permission_add = result).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to query ban add permissions: %o"), error); - }).then(() => { - if(permission_add[0] !== permission_add[1]) { - const input_global = container_add.find(".group-global input"); - input_global.prop("checked", permission_add[1]).prop("disabled", true).firstParent(".checkbox").addClass("disabled"); - } else if(!permission_add[0]) - container_add_no_permissions.show(); - }); - - controller.permission_edit().then(result => permission_edit = result).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to query ban edit permissions: %o"), error); - }).then(() => { - if(selected_ban) update_edit_window(false); - }); - - /* category switch */ - { - category_add.on('click', event => { - container_add.removeClass("hidden"); - category_add.addClass("selected"); - - container_edit.addClass("hidden"); - category_edit.removeClass("selected"); - - seperator_top.css({opacity: 1}); - - button_apply_state_index = 0; - button_apply.prop("disabled", !button_apply_state[0]).text(tr("Add ban")); - update_category_inputs[button_apply_state_index](); - }); - - category_edit.on('click', event => { - if(!selected_ban) return; - - container_add.addClass("hidden"); - category_add.removeClass("selected"); - - container_edit.removeClass("hidden"); - category_edit.addClass("selected"); - - seperator_top.css({opacity: 0}); - - button_apply_state_index = 1; - button_apply.prop("disabled", !button_apply_state[1]).text(tr("Save ban")); - update_category_inputs[button_apply_state_index](); - }); - } - - const build_ban_entry = (entry: BanEntry, selected: boolean) => { - let button_delete; - const tag = $.spawn("div").addClass("entry" + (entry.server_id > 0 ? "" : " global") + (selected ? " selected" : "")).append( - $.spawn("div").addClass("column column-key").append( - entry.name ? $.spawn("div").append(entry.name) : undefined, - entry.ip ? $.spawn("div").append(entry.ip) : undefined, - entry.unique_id ? $.spawn("div").append(entry.unique_id) : undefined, - entry.hardware_id ? $.spawn("div").append(entry.hardware_id) : undefined - ), - $.spawn("div").addClass("column column-reason").text(entry.reason), - $.spawn("div").addClass("column column-expires").text(entry.timestamp_expire ? moment(entry.timestamp_expire).format('DD.MM.YYYY hh:mm') : tr("Never")), - $.spawn("div").addClass("column column-delete").append( - button_delete = $.spawn("div").addClass("button-delete").append( - $.spawn("div").addClass("icon_em client-delete") - ) - ) - ); - - tag.on('click', event => { - if(selected_ban === entry || event.isDefaultPrevented()) return; - selected_ban = entry; - - container_ban_entries.find(".entry.selected").removeClass("selected"); - tag.addClass("selected"); - - update_edit_window(true); - }); - - button_delete.on('click', event => { - event.preventDefault(); - - controller.delete_ban(entry.banid, entry.server_id).then(() => { - tag.css({opacity: 1}).animate({opacity: 0}, 250, () => tag.animate({"max-height": 0}, 250, () => tag.remove())); - if(entry === selected_ban) { - selected_ban = undefined; - update_edit_window(false); - } - }).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to delete ban: %o"), error); - if(error instanceof CommandResult) - error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; - createErrorModal(tr("Failed to delete ban"), MessageHelper.formatMessage(tr("Failed to delete ban. {:br:}Error: {}"), error)).open(); - }); - }); - - if(selected) { - selected_ban = entry; - update_edit_window(false); - } - - const lower_mesh = - (entry.reason || "").toLowerCase() + " " + - (entry.unique_id || "").toLowerCase() + " " + - (entry.name || "").toLowerCase() + " " + - (entry.ip || "").toLowerCase() + " " + - (entry.hardware_id || "").toLowerCase(); - callback_ban_filter.push((text, flag_own, highlight_own) => { - if(text && lower_mesh.indexOf(text) == -1) { - tag.hide(); - return false; - } - - if(flag_own && !entry.flag_own) { - tag.hide(); - return false; - } - - tag.show().toggleClass( - "highlight", - highlight_own && - entry.flag_own - ); - return true; - }); - - return tag; - }; - - const update_banlist = (selected_ban?: number) => { - callback_ban_filter = []; - - container_ban_entries.find(".entry").remove(); - container_ban_entries_error.hide(); - container_ban_entries_empty.show().find("a").text(tr("Loading...")); - - let bans = []; - controller.request_list(_bans => bans.push(..._bans)).then(() => { - if(bans.length) { - container_ban_entries.append(...bans.map(e => build_ban_entry(e, e.banid === selected_ban))); - container_ban_entries_empty.hide(); - } else { - container_ban_entries_empty.find("a").text(tr("No bans registered")); - } - update_ban_filter(); - }).catch(error => { - log.info(LogCategory.CLIENT, tr("Failed to update ban list: %o"), error); - if(error instanceof CommandResult) - error = error.id === ErrorID.PERMISSION_ERROR ? tr("no permissions") : error.extra_message || error.message; - container_ban_entries_error.show().find("a").text(tr("Failed to receive banlist: ") + error); - container_ban_entries_empty.hide(); - }); - }; - - const build_trigger_entry = (entry: TriggerEntry) => { - const spawn_key_value = (key, value, reason) => { - return $.spawn("div").addClass("property").toggleClass("highlighted", reason).append( - $.spawn("div").addClass("key").text(key + ": "), - $.spawn("div").addClass("value").text(value) - ); - }; - - let cause_name = !!selected_ban.name && !!entry.client_nickname.match(selected_ban.name); - let cause_uid = !cause_name && !!selected_ban.unique_id && selected_ban.unique_id.toLowerCase() === (entry.unique_id || "").toLowerCase(); - let cause_ip = !cause_uid && !!selected_ban.ip && selected_ban.ip.toLowerCase() === (entry.connection_ip || "").toLowerCase(); - let cause_hwid = !cause_ip && !!selected_ban.hardware_id && selected_ban.hardware_id.toLowerCase() === (entry.hardware_id || "").toLowerCase(); - - /* we guess that IP is the cause because we dont see the IP and there is no other reason */ - if(!cause_name && !cause_uid && !cause_ip && !cause_hwid && entry.connection_ip === "hidden") - cause_ip = true; - - const time_str = moment(entry.timestamp).format('DD.MM.YYYY hh:mm'); - - const tag = $.spawn("div").addClass("entry").append( - $.spawn("div").addClass("column column-properties").append( - entry.client_nickname ? spawn_key_value(tr("Nickname"), entry.client_nickname, cause_name) : undefined, - entry.connection_ip ? spawn_key_value(tr("IP"), entry.connection_ip, cause_ip) : undefined, - entry.unique_id ? spawn_key_value(tr("Unique ID"), entry.unique_id, cause_uid) : undefined, - entry.hardware_id ? spawn_key_value(tr("Hardware ID"), entry.hardware_id, cause_hwid) : undefined - ), - $.spawn("div").addClass("column column-timestamp").text(time_str) - ); - - const lower_mesh = - (entry.unique_id || "").toLowerCase() + " " + - (entry.client_nickname || "").toLowerCase() + " " + - (entry.connection_ip || "").toLowerCase() + " " + - (entry.hardware_id || "").toLowerCase() + " " + - time_str + " " + - entry.timestamp; - callback_trigger_filter.push(text => { - if(text && lower_mesh.indexOf(text) == -1) { - tag.hide(); - return false; - } - - tag.show(); - return true; - }); - - return tag; - }; - - const update_triggerlist = () => { - callback_trigger_filter = []; - - container_trigger_entries.find(".entry").remove(); - container_trigger_entries_error.hide(); - container_trigger_entries_empty.show().find("a").text(tr("Loading...")); - - let triggers = []; - controller.request_trigger_list({ - ban_id: selected_ban.banid, - server_id: selected_ban.server_id - }, _triggers => triggers.push(..._triggers)).then(() => { - if(triggers.length) { - container_trigger_entries.append(...triggers.sort((a, b) => b.timestamp - a.timestamp).map(e => build_trigger_entry(e))); - container_trigger_entries_empty.hide(); - } else { - container_trigger_entries_empty.find("a").text(tr("No triggers logged")); - } - - update_trigger_filter(); - }).catch(error => { - log.info(LogCategory.CLIENT, tr("Failed to update trigger list: %o"), error); - if(error instanceof CommandResult) - error = error.id === ErrorID.PERMISSION_ERROR ? tr("no permissions") : error.extra_message || error.message; - container_trigger_entries_error.show().find("a").text(tr("Failed to receive trigger list: ") + error); - container_trigger_entries_empty.hide(); - }); - }; - - const show_triggerlist = () => { - container_trigger.show(); - }; - - /* general input field rules */ - const initialize_fields = (tag: JQuery, index: number) => { - const input_name = tag.find(".group-name input").on('change keyup', () => update_category_inputs[index]()); - const input_ip = tag.find(".group-ip input").on('change keyup', () => update_category_inputs[index]()); - const input_uid = tag.find(".group-unique-id input").on('change keyup', () => update_category_inputs[index]()); - const input_hwid = tag.find(".group-hwid input").on('change keyup', () => update_category_inputs[index]()); - const input_reason = tag.find(".group-reason textarea").on('change keyup', () => update_category_inputs[index]()); - //const input_global = tag.find(".group-global input"); - const input_duration_value = tag.find(".group-duration input").on('change keyup', () => update_category_inputs[index]()); - const input_duration_type = tag.find(".group-duration select").on('change keyup', () => update_category_inputs[index]()); - const tooltip_duration_max = tag.find(".tooltip-max-time a.max"); - - update_category_inputs[index] = () => { - let _criteria_set = false; - let _input_invalid = false; - - { - //TODO: Check if in regex mode or not - const value = input_name.val() as string || ""; - if(value.length > 255) { - _input_invalid = true; - input_name.firstParent(".input-boxed").addClass("is-invalid"); - } else { - _criteria_set = _criteria_set || !!value; - input_name.firstParent(".input-boxed").removeClass("is-invalid"); - } - } - - { - //TODO: Check if in regex mode or not - const value = input_ip.val() as string || ""; - if(value.length > 255) { - _input_invalid = true; - input_ip.firstParent(".input-boxed").addClass("is-invalid"); - } else { - _criteria_set = _criteria_set || !!value; - input_ip.firstParent(".input-boxed").removeClass("is-invalid"); - } - } - - { - const value = input_uid.val() as string || ""; - try { - if(value && atob(value).length != 20) throw ""; - - _criteria_set = _criteria_set || !!value; - input_uid.firstParent(".input-boxed").removeClass("is-invalid"); - } catch(e) { - _input_invalid = true; - input_uid.firstParent(".input-boxed").addClass("is-invalid"); - } - } - - { - const value = input_hwid.val() as string || ""; - if(value.length > 255) { - _input_invalid = true; - input_hwid.firstParent(".input-boxed").addClass("is-invalid"); - } else { - _criteria_set = _criteria_set || !!value; - input_hwid.firstParent(".input-boxed").removeClass("is-invalid"); - } - } - - { - const value = input_reason.val() as string || ""; - if(value.length > 512) { - _input_invalid = true; - input_reason.firstParent(".input-boxed").addClass("is-invalid"); - } else { - input_reason.firstParent(".input-boxed").removeClass("is-invalid"); - } - } - - { - const type = input_duration_type.val() as string; - const value = parseInt(input_duration_value.val() as string); - const disabled = input_duration_type.prop("disabled"); - - input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); - if(type !== "perm") { - if(input_duration_value.attr("x-saved-value")) { - input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); - input_duration_value.attr("x-saved-value", null); - } - - const selected_option = input_duration_type.find("option[value='" + type + "']"); - const max = parseInt(selected_option.attr("duration-max")); - - input_duration_value.attr("max", max); - if((value > max && max != -1) || value < 1) { - _input_invalid = true; - input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); - } else { - input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); - } - - if(max != -1) - tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + ""); - else - tooltip_duration_max.html(tr("You're allowed to ban permanent.")); - } else { - if(value && !Number.isNaN(value)) - input_duration_value.attr("x-saved-value", value); - input_duration_value.attr("placeholder", tr("for ever")).val(null); - tooltip_duration_max.html(tr("You're allowed to ban permanent.")); - } - } - - button_apply.prop("disabled", !(button_apply_state[button_apply_state_index] = _criteria_set && !_input_invalid)); - }; - - /* initialize ban time */ - controller.max_bantime().catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { - let unlimited = max_time == 0 || max_time == -1; - if(unlimited) max_time = 0; - - for(const value of Object.keys(duration_data)) { - input_duration_type.find("option[value='" + value + "']") - .prop("disabled", !unlimited && max_time >= duration_data[value].scale) - .attr("duration-scale", duration_data[value].scale) - .attr("duration-max", unlimited ? -1 : Math.floor(max_time / duration_data[value].scale)); - } - - input_duration_type.find("option[value='perm']") - .prop("disabled", !unlimited) - .attr("duration-scale", 0) - .attr("duration-max", -1); - }); - }; - initialize_fields(container_add, 0); - initialize_fields(container_edit, 1); - - /* the edit "handler" */ - { - const tag = container_edit; - const input_name = tag.find(".group-name input"); - const input_ip = tag.find(".group-ip input"); - const input_interpret = tag.find(".group-interpret select"); - const input_uid = tag.find(".group-unique-id input"); - const input_hwid = tag.find(".group-hwid input"); - const input_reason = tag.find(".group-reason textarea"); - const input_global = tag.find(".group-global input"); - const input_duration_value = tag.find(".group-duration input"); - const input_duration_type = tag.find(".group-duration select"); - const tooltip_duration_detailed = tag.find(".tooltip-max-time a.detailed"); - - const label_enforcement_count = tag.find(".group-enforcements .value a"); - const button_enforcement_list = tag.find(".button-enforcement-list"); - - const container_creator = tag.find(".group-creator .value"); - - update_edit_window = (switch_to: boolean) => { - category_edit.toggleClass("disabled", !selected_ban); - - const editable = selected_ban && selected_ban.server_id === 0 ? permission_edit[1] : permission_edit[0]; - - input_name.val(selected_ban ? selected_ban.name : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - input_ip.val(selected_ban ? selected_ban.ip : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - input_uid.val(selected_ban ? selected_ban.unique_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - input_hwid.val(selected_ban ? selected_ban.hardware_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - input_reason.val(selected_ban ? selected_ban.reason : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - - input_interpret.find("option").eq(selected_ban && typeof(selected_ban.name_type) === "number" ? selected_ban.name_type : 2).prop("selected", true).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - label_enforcement_count.text((selected_ban ? selected_ban.enforcements : 0) || 0); - button_enforcement_list.prop("disabled", !selected_ban || selected_ban.enforcements == 0); - - input_global.prop("checked", selected_ban && selected_ban.server_id == 0); - - input_duration_type.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - input_duration_value.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); - - if(selected_ban) { - if(selected_ban.timestamp_expire > selected_ban.timestamp_created) { - const duration = Math.ceil((selected_ban.timestamp_expire - selected_ban.timestamp_created) / 1000); - - const periods = Object.keys(duration_data); - let index; - for(index = 0; index < periods.length; index++) { - if(duration_data[periods[index]].scale > duration + 1 || ((duration + 1) % duration_data[periods[index]].scale) > 1.9) - break; - } - if(index > 0) index--; - input_duration_type.find("option[value='" + periods[index] + "']").prop("selected", true); - input_duration_value.val(Math.ceil(duration / duration_data[periods[index]].scale)); - tooltip_duration_detailed.text($.spawn("div").append(...MessageHelper.formatMessage(tr("The ban lasts for exact {}."), MessageHelper.format_time(duration * 1000, "never"))).text()); - } else { - tooltip_duration_detailed.text(tr("The ban is forever.")); - input_duration_value.attr("placeholder", tr("for ever")).val(null).prop('disabled', true); - input_duration_type.find("option[value='perm']").prop("selected", true); - } - } - - container_creator.empty(); - if(selected_ban) { - container_creator.append( - htmltags.generate_client_object({ - client_id: 0, - client_unique_id: selected_ban.invoker_unique_id, - client_name: selected_ban.invoker_name, - add_braces: false - }) - ); - } - - if(switch_to) - category_edit.trigger('click'); - }; - - button_apply.on('click', event => { - if (!button_apply_state[1] || button_apply_state_index != 1) return; - - const data = {banid: selected_ban.banid}; - - if(input_ip.val() != selected_ban.ip) - data["ip"] = input_ip.val(); - - if(input_name.val() != selected_ban.name) - data["name"] = input_name.val(); - - if(input_uid.val() != selected_ban.unique_id) - data["uid"] = input_uid.val(); - - if(input_hwid.val() != selected_ban.hardware_id) - data["hwid"] = input_hwid.val(); - - if(input_reason.val() != selected_ban.reason) - data["banreason"] = input_reason.val(); - - if(input_reason.val() != selected_ban.reason) - data["reason"] = input_reason.val(); - - const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); - if(selected_ban.timestamp_expire > 0 ? (selected_ban.timestamp_expire - selected_ban.timestamp_created != duration) : duration != 0) - data["time"] = Math.floor(duration / 1000); - - controller.edit_ban(data).then(() => { - update_banlist(selected_ban ? selected_ban.banid : undefined); - - selected_ban = undefined; - update_edit_window(false); - - createInfoModal(tr("Ban successfully edited"), tr("Your ban has been successfully edited.")).open(); - }).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to edited ban: %o"), error); - if(error instanceof CommandResult) - error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; - createErrorModal(tr("Failed to edited ban"), MessageHelper.formatMessage(tr("Failed to edited ban. {:br:}Error: {}"), error)).open(); - }); - }); - - button_enforcement_list.on('click', () => { - update_triggerlist(); - show_triggerlist(); - }); - } - - /* the create "handler" */ - { - const tag = container_add; - const input_name = tag.find(".group-name input"); - const input_ip = tag.find(".group-ip input"); - const input_interpret = tag.find(".group-interpret select"); - const input_uid = tag.find(".group-unique-id input"); - const input_hwid = tag.find(".group-hwid input"); - const input_reason = tag.find(".group-reason textarea"); - const input_global = tag.find(".group-global input"); - const input_duration_value = tag.find(".group-duration input"); - const input_duration_type = tag.find(".group-duration select"); - - button_apply.on('click', event => { - if(!button_apply_state[0] || button_apply_state_index != 0) return; - - const data: BanEntry = { - banid: 0, - enforcements: 0, - } as any; - - if(input_global.prop('checked')) - data.server_id = 0; - - if(input_ip.val()) - data.ip = input_ip.val() as any; - - if(input_name.val()) - data.name = input_name.val() as any; - - if(input_uid.val()) - data.unique_id = input_uid.val() as any; - - if(input_hwid.val()) - data.hardware_id = input_hwid.val() as any; - - if(input_reason.val()) - data.reason = input_reason.val() as any; - - data.timestamp_created = Date.now(); - - data.timestamp_expire = input_duration_type.val() === "perm" ? 0 : (data.timestamp_created + 1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); - //TODO: input_interpret (Currently not supported by TeaSpeak) - - controller.add_ban(data).then(() => { - input_name.val(null); - input_ip.val(null); - input_uid.val(null); - input_hwid.val(null); - input_reason.val(null); - input_duration_value.val(1); - update_banlist(); - - createInfoModal(tr("Ban successfully added"), tr("Your ban has been successfully added.")).open(); - }).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to add ban: %o"), error); - if(error instanceof CommandResult) - error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; - createErrorModal(tr("Failed to add ban"), MessageHelper.formatMessage(tr("Failed to add ban. {:br:}Error: {}"), error)).open(); - }); - }); - } - - /* the banlist filter */ - { - const input_filter = container_ban.find(".container-filter input").on('change keyup', () => update_ban_filter()); - const option_show_own = container_ban.find(".option-show-own").on('change keyup', () => update_ban_filter()); - const option_hightlight_own = container_ban.find(".option-highlight-own").on('change keyup', () => update_ban_filter()); - - update_ban_filter = () => { - const text = (input_filter.val() as string || "").toLowerCase(); - const flag_show_own = option_show_own.prop('checked'); - const flag_hightlight_own = option_hightlight_own.prop('checked'); - - let count = 0; - for(const entry of callback_ban_filter) - if(entry(text, flag_show_own, flag_hightlight_own)) - count++; - if(callback_ban_filter.length != 0) { - if(count > 0) - container_ban_entries_empty.hide(); - else - container_ban_entries_empty.show().find("a").text(tr("No bans found")); - } - }; - } - - /* the trigger list filter */ - { - const input_filter = container_trigger.find(".container-filter input").on('change keyup', () => update_trigger_filter()); - const option_hightlight_cause = container_trigger.find(".option-highlight-cause").on('change keyup', () => update_trigger_filter()); - const button_close = container_trigger.find(".container-close"); - - update_trigger_filter = () => { - const text = (input_filter.val() as string || "").toLowerCase(); - - let count = 0; - for(const entry of callback_trigger_filter) - if(entry(text)) - count++; - if(callback_trigger_filter.length != 0) { - if(count > 0) - container_trigger_entries_empty.hide(); - else - container_trigger_entries_empty.show().find("a").text(tr("No trigger events found")); - } - container_trigger.find(".container-list").toggleClass('highlight', option_hightlight_cause.prop('checked')); - }; - - button_close.on('click', () => container_trigger.hide()); - } - - template.find(".button-refresh-banlist").on('click', event => update_banlist(selected_ban ? selected_ban.banid : undefined)); - template.find(".button-refresh-triggerlist").on('click', event => update_triggerlist()); - - /* initialize */ - category_add.trigger('click'); - update_edit_window(false); - update_banlist(); - - tooltip(template); - return template.children(); - } + modal = createModal({ + header: tr("Server Banlist"), + body: () => generate_dom(controller), + footer: null, + + width: '60em' + }); + + client.serverConnection.command_handler_boss().register_single_handler(single_ban_handler); + client.serverConnection.command_handler_boss().register_single_handler(single_trigger_handler); + modal.close_listener.push(() => { + client.serverConnection.command_handler_boss().remove_single_handler(single_ban_handler); + client.serverConnection.command_handler_boss().remove_single_handler(single_trigger_handler); + }); + + //TODO: Test without dividerfy! + modal.htmlTag.dividerfy(); + modal.htmlTag.find(".modal-body").addClass("modal-ban-list"); + modal.open(); } -//container-triggerlist \ No newline at end of file +interface BanEntry { + server_id: number; + banid: number; + + name?: string; + name_type?: number; + + unique_id?: string; + ip?: string; + hardware_id?: string; + + reason: string; + invoker_name: string; + invoker_unique_id?: string; + invoker_database_id?: number; + + timestamp_created: number; + timestamp_expire: number; + + enforcements: number; + + flag_own?: boolean; +} + +interface TriggerEntry { + unique_id?: string; + client_nickname?: string; + hardware_id?: string; + connection_ip: string; + + timestamp: number; +} + +interface BanListController { + request_list(callback_bans: (entries: BanEntry[]) => any) : Promise; + request_trigger_list(ban: {ban_id: number, server_id: number | undefined}, callback_triggers: (entries: TriggerEntry[]) => any) : Promise; + + max_bantime() : Promise; + permission_edit() : Promise; + permission_add() : Promise; + + add_ban(entry: BanEntry) : Promise; + edit_ban(data: any) : Promise; + delete_ban(entry_id: number, server_id: number | undefined) : Promise; +} + +//Note: This object must be sorted (from shortest to longest)! +export const duration_data = { + "sec": { + "text": tr("Seconds"), + "1-text": tr("Second"), + + scale: 1 + }, + "min": { + "text": tr("Minutes"), + "1-text": tr("Minute"), + + scale: 60 + }, + "hours": { + "text": tr("Hours"), + "1-text": tr("Hour"), + + scale: 3600 + }, + "days": { + "text": tr("Days"), + "1-text": tr("Day"), + + scale: 86400 + }, +}; + +declare const moment; +function generate_dom(controller: BanListController) : JQuery { + const template = $("#tmpl_ban_list").renderTag(); + + let callback_ban_filter: ((text: string, flag_own: boolean, highlight_own: boolean) => boolean)[] = []; + let callback_trigger_filter: ((text: string) => boolean)[] = []; + let selected_ban: BanEntry | undefined; + let update_edit_window: (switch_to: boolean) => any; + let update_ban_filter: () => any; + let update_trigger_filter: () => any; + + const container_ban = template.find(".container-banlist"); + const container_ban_entries = container_ban.find(".container-list .body"); + const container_ban_entries_empty = container_ban.find(".container-list .container-empty"); + const container_ban_entries_error = container_ban.find(".container-list .container-error"); + + const container_trigger = template.find(".container-triggerlist").hide(); + const container_trigger_entries = container_trigger.find(".container-list .body"); + const container_trigger_entries_empty = container_trigger.find(".container-list .container-empty"); + const container_trigger_entries_error = container_trigger.find(".container-list .container-error"); + + const button_apply = template.find(".button-apply"); + let button_apply_state = [false, false]; /* first index is add; second index is edit */ + let update_category_inputs: (() => any)[] = [undefined, undefined]; + let button_apply_state_index = 1; + + const category_add = template.find(".left .head .category-add"); + const category_edit = template.find(".left .head .category-edit"); + const container_add = template.find(".left .container-add"); + const container_add_no_permissions = template.find(".left .container-add .container-no-permissions"); + const container_edit = template.find(".left .container-edit"); + + const seperator_top = template.find(".container-seperator .top"); + + /* [local; global] */ + let permission_edit: boolean[] = [false, false], permission_add: boolean[] = [false, false]; + + container_add_no_permissions.hide(); + controller.permission_add().then(result => permission_add = result).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to query ban add permissions: %o"), error); + }).then(() => { + if(permission_add[0] !== permission_add[1]) { + const input_global = container_add.find(".group-global input"); + input_global.prop("checked", permission_add[1]).prop("disabled", true).firstParent(".checkbox").addClass("disabled"); + } else if(!permission_add[0]) + container_add_no_permissions.show(); + }); + + controller.permission_edit().then(result => permission_edit = result).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to query ban edit permissions: %o"), error); + }).then(() => { + if(selected_ban) update_edit_window(false); + }); + + /* category switch */ + { + category_add.on('click', event => { + container_add.removeClass("hidden"); + category_add.addClass("selected"); + + container_edit.addClass("hidden"); + category_edit.removeClass("selected"); + + seperator_top.css({opacity: 1}); + + button_apply_state_index = 0; + button_apply.prop("disabled", !button_apply_state[0]).text(tr("Add ban")); + update_category_inputs[button_apply_state_index](); + }); + + category_edit.on('click', event => { + if(!selected_ban) return; + + container_add.addClass("hidden"); + category_add.removeClass("selected"); + + container_edit.removeClass("hidden"); + category_edit.addClass("selected"); + + seperator_top.css({opacity: 0}); + + button_apply_state_index = 1; + button_apply.prop("disabled", !button_apply_state[1]).text(tr("Save ban")); + update_category_inputs[button_apply_state_index](); + }); + } + + const build_ban_entry = (entry: BanEntry, selected: boolean) => { + let button_delete; + const tag = $.spawn("div").addClass("entry" + (entry.server_id > 0 ? "" : " global") + (selected ? " selected" : "")).append( + $.spawn("div").addClass("column column-key").append( + entry.name ? $.spawn("div").append(entry.name) : undefined, + entry.ip ? $.spawn("div").append(entry.ip) : undefined, + entry.unique_id ? $.spawn("div").append(entry.unique_id) : undefined, + entry.hardware_id ? $.spawn("div").append(entry.hardware_id) : undefined + ), + $.spawn("div").addClass("column column-reason").text(entry.reason), + $.spawn("div").addClass("column column-expires").text(entry.timestamp_expire ? moment(entry.timestamp_expire).format('DD.MM.YYYY hh:mm') : tr("Never")), + $.spawn("div").addClass("column column-delete").append( + button_delete = $.spawn("div").addClass("button-delete").append( + $.spawn("div").addClass("icon_em client-delete") + ) + ) + ); + + tag.on('click', event => { + if(selected_ban === entry || event.isDefaultPrevented()) return; + selected_ban = entry; + + container_ban_entries.find(".entry.selected").removeClass("selected"); + tag.addClass("selected"); + + update_edit_window(true); + }); + + button_delete.on('click', event => { + event.preventDefault(); + + controller.delete_ban(entry.banid, entry.server_id).then(() => { + tag.css({opacity: 1}).animate({opacity: 0}, 250, () => tag.animate({"max-height": 0}, 250, () => tag.remove())); + if(entry === selected_ban) { + selected_ban = undefined; + update_edit_window(false); + } + }).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to delete ban: %o"), error); + if(error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(tr("Failed to delete ban"), formatMessage(tr("Failed to delete ban. {:br:}Error: {}"), error)).open(); + }); + }); + + if(selected) { + selected_ban = entry; + update_edit_window(false); + } + + const lower_mesh = + (entry.reason || "").toLowerCase() + " " + + (entry.unique_id || "").toLowerCase() + " " + + (entry.name || "").toLowerCase() + " " + + (entry.ip || "").toLowerCase() + " " + + (entry.hardware_id || "").toLowerCase(); + callback_ban_filter.push((text, flag_own, highlight_own) => { + if(text && lower_mesh.indexOf(text) == -1) { + tag.hide(); + return false; + } + + if(flag_own && !entry.flag_own) { + tag.hide(); + return false; + } + + tag.show().toggleClass( + "highlight", + highlight_own && + entry.flag_own + ); + return true; + }); + + return tag; + }; + + const update_banlist = (selected_ban?: number) => { + callback_ban_filter = []; + + container_ban_entries.find(".entry").remove(); + container_ban_entries_error.hide(); + container_ban_entries_empty.show().find("a").text(tr("Loading...")); + + let bans = []; + controller.request_list(_bans => bans.push(..._bans)).then(() => { + if(bans.length) { + container_ban_entries.append(...bans.map(e => build_ban_entry(e, e.banid === selected_ban))); + container_ban_entries_empty.hide(); + } else { + container_ban_entries_empty.find("a").text(tr("No bans registered")); + } + update_ban_filter(); + }).catch(error => { + log.info(LogCategory.CLIENT, tr("Failed to update ban list: %o"), error); + if(error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? tr("no permissions") : error.extra_message || error.message; + container_ban_entries_error.show().find("a").text(tr("Failed to receive banlist: ") + error); + container_ban_entries_empty.hide(); + }); + }; + + const build_trigger_entry = (entry: TriggerEntry) => { + const spawn_key_value = (key, value, reason) => { + return $.spawn("div").addClass("property").toggleClass("highlighted", reason).append( + $.spawn("div").addClass("key").text(key + ": "), + $.spawn("div").addClass("value").text(value) + ); + }; + + let cause_name = !!selected_ban.name && !!entry.client_nickname.match(selected_ban.name); + let cause_uid = !cause_name && !!selected_ban.unique_id && selected_ban.unique_id.toLowerCase() === (entry.unique_id || "").toLowerCase(); + let cause_ip = !cause_uid && !!selected_ban.ip && selected_ban.ip.toLowerCase() === (entry.connection_ip || "").toLowerCase(); + let cause_hwid = !cause_ip && !!selected_ban.hardware_id && selected_ban.hardware_id.toLowerCase() === (entry.hardware_id || "").toLowerCase(); + + /* we guess that IP is the cause because we dont see the IP and there is no other reason */ + if(!cause_name && !cause_uid && !cause_ip && !cause_hwid && entry.connection_ip === "hidden") + cause_ip = true; + + const time_str = moment(entry.timestamp).format('DD.MM.YYYY hh:mm'); + + const tag = $.spawn("div").addClass("entry").append( + $.spawn("div").addClass("column column-properties").append( + entry.client_nickname ? spawn_key_value(tr("Nickname"), entry.client_nickname, cause_name) : undefined, + entry.connection_ip ? spawn_key_value(tr("IP"), entry.connection_ip, cause_ip) : undefined, + entry.unique_id ? spawn_key_value(tr("Unique ID"), entry.unique_id, cause_uid) : undefined, + entry.hardware_id ? spawn_key_value(tr("Hardware ID"), entry.hardware_id, cause_hwid) : undefined + ), + $.spawn("div").addClass("column column-timestamp").text(time_str) + ); + + const lower_mesh = + (entry.unique_id || "").toLowerCase() + " " + + (entry.client_nickname || "").toLowerCase() + " " + + (entry.connection_ip || "").toLowerCase() + " " + + (entry.hardware_id || "").toLowerCase() + " " + + time_str + " " + + entry.timestamp; + callback_trigger_filter.push(text => { + if(text && lower_mesh.indexOf(text) == -1) { + tag.hide(); + return false; + } + + tag.show(); + return true; + }); + + return tag; + }; + + const update_triggerlist = () => { + callback_trigger_filter = []; + + container_trigger_entries.find(".entry").remove(); + container_trigger_entries_error.hide(); + container_trigger_entries_empty.show().find("a").text(tr("Loading...")); + + let triggers = []; + controller.request_trigger_list({ + ban_id: selected_ban.banid, + server_id: selected_ban.server_id + }, _triggers => triggers.push(..._triggers)).then(() => { + if(triggers.length) { + container_trigger_entries.append(...triggers.sort((a, b) => b.timestamp - a.timestamp).map(e => build_trigger_entry(e))); + container_trigger_entries_empty.hide(); + } else { + container_trigger_entries_empty.find("a").text(tr("No triggers logged")); + } + + update_trigger_filter(); + }).catch(error => { + log.info(LogCategory.CLIENT, tr("Failed to update trigger list: %o"), error); + if(error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? tr("no permissions") : error.extra_message || error.message; + container_trigger_entries_error.show().find("a").text(tr("Failed to receive trigger list: ") + error); + container_trigger_entries_empty.hide(); + }); + }; + + const show_triggerlist = () => { + container_trigger.show(); + }; + + /* general input field rules */ + const initialize_fields = (tag: JQuery, index: number) => { + const input_name = tag.find(".group-name input").on('change keyup', () => update_category_inputs[index]()); + const input_ip = tag.find(".group-ip input").on('change keyup', () => update_category_inputs[index]()); + const input_uid = tag.find(".group-unique-id input").on('change keyup', () => update_category_inputs[index]()); + const input_hwid = tag.find(".group-hwid input").on('change keyup', () => update_category_inputs[index]()); + const input_reason = tag.find(".group-reason textarea").on('change keyup', () => update_category_inputs[index]()); + //const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input").on('change keyup', () => update_category_inputs[index]()); + const input_duration_type = tag.find(".group-duration select").on('change keyup', () => update_category_inputs[index]()); + const tooltip_duration_max = tag.find(".tooltip-max-time a.max"); + + update_category_inputs[index] = () => { + let _criteria_set = false; + let _input_invalid = false; + + { + //TODO: Check if in regex mode or not + const value = input_name.val() as string || ""; + if(value.length > 255) { + _input_invalid = true; + input_name.firstParent(".input-boxed").addClass("is-invalid"); + } else { + _criteria_set = _criteria_set || !!value; + input_name.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + + { + //TODO: Check if in regex mode or not + const value = input_ip.val() as string || ""; + if(value.length > 255) { + _input_invalid = true; + input_ip.firstParent(".input-boxed").addClass("is-invalid"); + } else { + _criteria_set = _criteria_set || !!value; + input_ip.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + + { + const value = input_uid.val() as string || ""; + try { + if(value && atob(value).length != 20) throw ""; + + _criteria_set = _criteria_set || !!value; + input_uid.firstParent(".input-boxed").removeClass("is-invalid"); + } catch(e) { + _input_invalid = true; + input_uid.firstParent(".input-boxed").addClass("is-invalid"); + } + } + + { + const value = input_hwid.val() as string || ""; + if(value.length > 255) { + _input_invalid = true; + input_hwid.firstParent(".input-boxed").addClass("is-invalid"); + } else { + _criteria_set = _criteria_set || !!value; + input_hwid.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + + { + const value = input_reason.val() as string || ""; + if(value.length > 512) { + _input_invalid = true; + input_reason.firstParent(".input-boxed").addClass("is-invalid"); + } else { + input_reason.firstParent(".input-boxed").removeClass("is-invalid"); + } + } + + { + const type = input_duration_type.val() as string; + const value = parseInt(input_duration_value.val() as string); + const disabled = input_duration_type.prop("disabled"); + + input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled); + if(type !== "perm") { + if(input_duration_value.attr("x-saved-value")) { + input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value"))); + input_duration_value.attr("x-saved-value", null); + } + + const selected_option = input_duration_type.find("option[value='" + type + "']"); + const max = parseInt(selected_option.attr("duration-max")); + + input_duration_value.attr("max", max); + if((value > max && max != -1) || value < 1) { + _input_invalid = true; + input_duration_value.firstParent(".input-boxed").addClass("is-invalid"); + } else { + input_duration_value.firstParent(".input-boxed").removeClass("is-invalid"); + } + + if(max != -1) + tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + ""); + else + tooltip_duration_max.html(tr("You're allowed to ban permanent.")); + } else { + if(value && !Number.isNaN(value)) + input_duration_value.attr("x-saved-value", value); + input_duration_value.attr("placeholder", tr("for ever")).val(null); + tooltip_duration_max.html(tr("You're allowed to ban permanent.")); + } + } + + button_apply.prop("disabled", !(button_apply_state[button_apply_state_index] = _criteria_set && !_input_invalid)); + }; + + /* initialize ban time */ + controller.max_bantime().catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => { + let unlimited = max_time == 0 || max_time == -1; + if(unlimited) max_time = 0; + + for(const value of Object.keys(duration_data)) { + input_duration_type.find("option[value='" + value + "']") + .prop("disabled", !unlimited && max_time >= duration_data[value].scale) + .attr("duration-scale", duration_data[value].scale) + .attr("duration-max", unlimited ? -1 : Math.floor(max_time / duration_data[value].scale)); + } + + input_duration_type.find("option[value='perm']") + .prop("disabled", !unlimited) + .attr("duration-scale", 0) + .attr("duration-max", -1); + }); + }; + initialize_fields(container_add, 0); + initialize_fields(container_edit, 1); + + /* the edit "handler" */ + { + const tag = container_edit; + const input_name = tag.find(".group-name input"); + const input_ip = tag.find(".group-ip input"); + const input_interpret = tag.find(".group-interpret select"); + const input_uid = tag.find(".group-unique-id input"); + const input_hwid = tag.find(".group-hwid input"); + const input_reason = tag.find(".group-reason textarea"); + const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input"); + const input_duration_type = tag.find(".group-duration select"); + const tooltip_duration_detailed = tag.find(".tooltip-max-time a.detailed"); + + const label_enforcement_count = tag.find(".group-enforcements .value a"); + const button_enforcement_list = tag.find(".button-enforcement-list"); + + const container_creator = tag.find(".group-creator .value"); + + update_edit_window = (switch_to: boolean) => { + category_edit.toggleClass("disabled", !selected_ban); + + const editable = selected_ban && selected_ban.server_id === 0 ? permission_edit[1] : permission_edit[0]; + + input_name.val(selected_ban ? selected_ban.name : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_ip.val(selected_ban ? selected_ban.ip : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_uid.val(selected_ban ? selected_ban.unique_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_hwid.val(selected_ban ? selected_ban.hardware_id : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_reason.val(selected_ban ? selected_ban.reason : null).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + + input_interpret.find("option").eq(selected_ban && typeof(selected_ban.name_type) === "number" ? selected_ban.name_type : 2).prop("selected", true).prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + label_enforcement_count.text((selected_ban ? selected_ban.enforcements : 0) || 0); + button_enforcement_list.prop("disabled", !selected_ban || selected_ban.enforcements == 0); + + input_global.prop("checked", selected_ban && selected_ban.server_id == 0); + + input_duration_type.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + input_duration_value.prop("disabled", !editable).firstParent(".input-boxed").toggleClass("disabled", !editable); + + if(selected_ban) { + if(selected_ban.timestamp_expire > selected_ban.timestamp_created) { + const duration = Math.ceil((selected_ban.timestamp_expire - selected_ban.timestamp_created) / 1000); + + const periods = Object.keys(duration_data); + let index; + for(index = 0; index < periods.length; index++) { + if(duration_data[periods[index]].scale > duration + 1 || ((duration + 1) % duration_data[periods[index]].scale) > 1.9) + break; + } + if(index > 0) index--; + input_duration_type.find("option[value='" + periods[index] + "']").prop("selected", true); + input_duration_value.val(Math.ceil(duration / duration_data[periods[index]].scale)); + tooltip_duration_detailed.text($.spawn("div").append(...formatMessage(tr("The ban lasts for exact {}."), format_time(duration * 1000, "never"))).text()); + } else { + tooltip_duration_detailed.text(tr("The ban is forever.")); + input_duration_value.attr("placeholder", tr("for ever")).val(null).prop('disabled', true); + input_duration_type.find("option[value='perm']").prop("selected", true); + } + } + + container_creator.empty(); + if(selected_ban) { + container_creator.append( + htmltags.generate_client_object({ + client_id: 0, + client_unique_id: selected_ban.invoker_unique_id, + client_name: selected_ban.invoker_name, + add_braces: false + }) + ); + } + + if(switch_to) + category_edit.trigger('click'); + }; + + button_apply.on('click', event => { + if (!button_apply_state[1] || button_apply_state_index != 1) return; + + const data = {banid: selected_ban.banid}; + + if(input_ip.val() != selected_ban.ip) + data["ip"] = input_ip.val(); + + if(input_name.val() != selected_ban.name) + data["name"] = input_name.val(); + + if(input_uid.val() != selected_ban.unique_id) + data["uid"] = input_uid.val(); + + if(input_hwid.val() != selected_ban.hardware_id) + data["hwid"] = input_hwid.val(); + + if(input_reason.val() != selected_ban.reason) + data["banreason"] = input_reason.val(); + + if(input_reason.val() != selected_ban.reason) + data["reason"] = input_reason.val(); + + const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); + if(selected_ban.timestamp_expire > 0 ? (selected_ban.timestamp_expire - selected_ban.timestamp_created != duration) : duration != 0) + data["time"] = Math.floor(duration / 1000); + + controller.edit_ban(data).then(() => { + update_banlist(selected_ban ? selected_ban.banid : undefined); + + selected_ban = undefined; + update_edit_window(false); + + createInfoModal(tr("Ban successfully edited"), tr("Your ban has been successfully edited.")).open(); + }).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to edited ban: %o"), error); + if(error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(tr("Failed to edited ban"), formatMessage(tr("Failed to edited ban. {:br:}Error: {}"), error)).open(); + }); + }); + + button_enforcement_list.on('click', () => { + update_triggerlist(); + show_triggerlist(); + }); + } + + /* the create "handler" */ + { + const tag = container_add; + const input_name = tag.find(".group-name input"); + const input_ip = tag.find(".group-ip input"); + const input_interpret = tag.find(".group-interpret select"); + const input_uid = tag.find(".group-unique-id input"); + const input_hwid = tag.find(".group-hwid input"); + const input_reason = tag.find(".group-reason textarea"); + const input_global = tag.find(".group-global input"); + const input_duration_value = tag.find(".group-duration input"); + const input_duration_type = tag.find(".group-duration select"); + + button_apply.on('click', event => { + if(!button_apply_state[0] || button_apply_state_index != 0) return; + + const data: BanEntry = { + banid: 0, + enforcements: 0, + } as any; + + if(input_global.prop('checked')) + data.server_id = 0; + + if(input_ip.val()) + data.ip = input_ip.val() as any; + + if(input_name.val()) + data.name = input_name.val() as any; + + if(input_uid.val()) + data.unique_id = input_uid.val() as any; + + if(input_hwid.val()) + data.hardware_id = input_hwid.val() as any; + + if(input_reason.val()) + data.reason = input_reason.val() as any; + + data.timestamp_created = Date.now(); + + data.timestamp_expire = input_duration_type.val() === "perm" ? 0 : (data.timestamp_created + 1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string)); + //TODO: input_interpret (Currently not supported by TeaSpeak) + + controller.add_ban(data).then(() => { + input_name.val(null); + input_ip.val(null); + input_uid.val(null); + input_hwid.val(null); + input_reason.val(null); + input_duration_value.val(1); + update_banlist(); + + createInfoModal(tr("Ban successfully added"), tr("Your ban has been successfully added.")).open(); + }).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to add ban: %o"), error); + if(error instanceof CommandResult) + error = error.id === ErrorID.PERMISSION_ERROR ? "no permissions" : error.extra_message || error.message; + createErrorModal(tr("Failed to add ban"), formatMessage(tr("Failed to add ban. {:br:}Error: {}"), error)).open(); + }); + }); + } + + /* the banlist filter */ + { + const input_filter = container_ban.find(".container-filter input").on('change keyup', () => update_ban_filter()); + const option_show_own = container_ban.find(".option-show-own").on('change keyup', () => update_ban_filter()); + const option_hightlight_own = container_ban.find(".option-highlight-own").on('change keyup', () => update_ban_filter()); + + update_ban_filter = () => { + const text = (input_filter.val() as string || "").toLowerCase(); + const flag_show_own = option_show_own.prop('checked'); + const flag_hightlight_own = option_hightlight_own.prop('checked'); + + let count = 0; + for(const entry of callback_ban_filter) + if(entry(text, flag_show_own, flag_hightlight_own)) + count++; + if(callback_ban_filter.length != 0) { + if(count > 0) + container_ban_entries_empty.hide(); + else + container_ban_entries_empty.show().find("a").text(tr("No bans found")); + } + }; + } + + /* the trigger list filter */ + { + const input_filter = container_trigger.find(".container-filter input").on('change keyup', () => update_trigger_filter()); + const option_hightlight_cause = container_trigger.find(".option-highlight-cause").on('change keyup', () => update_trigger_filter()); + const button_close = container_trigger.find(".container-close"); + + update_trigger_filter = () => { + const text = (input_filter.val() as string || "").toLowerCase(); + + let count = 0; + for(const entry of callback_trigger_filter) + if(entry(text)) + count++; + if(callback_trigger_filter.length != 0) { + if(count > 0) + container_trigger_entries_empty.hide(); + else + container_trigger_entries_empty.show().find("a").text(tr("No trigger events found")); + } + container_trigger.find(".container-list").toggleClass('highlight', option_hightlight_cause.prop('checked')); + }; + + button_close.on('click', () => container_trigger.hide()); + } + + template.find(".button-refresh-banlist").on('click', event => update_banlist(selected_ban ? selected_ban.banid : undefined)); + template.find(".button-refresh-triggerlist").on('click', event => update_triggerlist()); + + /* initialize */ + category_add.trigger('click'); + update_edit_window(false); + update_banlist(); + + tooltip.initialize(template); + return template.children(); +} \ No newline at end of file diff --git a/shared/js/ui/modal/ModalBookmarks.ts b/shared/js/ui/modal/ModalBookmarks.ts index 950b3658..3f9fe5cd 100644 --- a/shared/js/ui/modal/ModalBookmarks.ts +++ b/shared/js/ui/modal/ModalBookmarks.ts @@ -1,368 +1,383 @@ -/// -/// -/// +import {createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import { + Bookmark, + bookmarks, + BookmarkType, boorkmak_connect, create_bookmark, create_bookmark_directory, + delete_bookmark, + DirectoryBookmark, + save_bookmark +} from "tc-shared/bookmarks"; +import {connection_log, Regex} from "tc-shared/ui/modal/ModalConnect"; +import {IconManager} from "tc-shared/FileManager"; +import {profiles} from "tc-shared/profiles/ConnectionProfile"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {Settings, settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as i18nc from "tc-shared/i18n/country"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import * as top_menu from "../frames/MenuBar"; -namespace Modals { - export function spawnBookmarkModal() { - let modal: Modal; - modal = createModal({ - header: tr("Manage bookmarks"), - body: () => { - let template = $("#tmpl_manage_bookmarks").renderTag({ }); - let selected_bookmark: bookmarks.Bookmark | bookmarks.DirectoryBookmark | undefined; +export function spawnBookmarkModal() { + let modal: Modal; + modal = createModal({ + header: tr("Manage bookmarks"), + body: () => { + let template = $("#tmpl_manage_bookmarks").renderTag({ }); + let selected_bookmark: Bookmark | DirectoryBookmark | undefined; - const button_delete = template.find(".button-delete"); - const button_add_folder = template.find(".button-add-folder"); - const button_add_bookmark = template.find(".button-add-bookmark"); + const button_delete = template.find(".button-delete"); + const button_add_folder = template.find(".button-add-folder"); + const button_add_bookmark = template.find(".button-add-bookmark"); - const button_connect = template.find(".button-connect"); - const button_connect_tab = template.find(".button-connect-tab"); + const button_connect = template.find(".button-connect"); + const button_connect_tab = template.find(".button-connect-tab"); - const label_bookmark_name = template.find(".header .container-name"); - const label_server_address = template.find(".header .container-address"); + const label_bookmark_name = template.find(".header .container-name"); + const label_server_address = template.find(".header .container-address"); - const input_bookmark_name = template.find(".input-bookmark-name"); - const input_connect_profile = template.find(".input-connect-profile"); + const input_bookmark_name = template.find(".input-bookmark-name"); + const input_connect_profile = template.find(".input-connect-profile"); - const input_server_address = template.find(".input-server-address"); - const input_server_password = template.find(".input-server-password"); + const input_server_address = template.find(".input-server-address"); + const input_server_password = template.find(".input-server-password"); - const label_server_name = template.find(".server-name"); - const label_server_region = template.find(".server-region"); - const label_last_ping = template.find(".server-ping"); - const label_client_count = template.find(".server-client-count"); - const label_connection_count = template.find(".server-connection-count"); + const label_server_name = template.find(".server-name"); + const label_server_region = template.find(".server-region"); + const label_last_ping = template.find(".server-ping"); + const label_client_count = template.find(".server-client-count"); + const label_connection_count = template.find(".server-connection-count"); - const update_buttons = () => { - button_delete.prop("disabled", !selected_bookmark); - button_connect.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); - button_connect_tab.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); - }; + const update_buttons = () => { + button_delete.prop("disabled", !selected_bookmark); + button_connect.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY); + button_connect_tab.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY); + }; - const update_connect_info = () => { - if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) { - const entry = selected_bookmark as bookmarks.Bookmark; + const update_connect_info = () => { + if(selected_bookmark && selected_bookmark.type === BookmarkType.ENTRY) { + const entry = selected_bookmark as Bookmark; - const history = connection_log.history().find(e => e.address.hostname === entry.server_properties.server_address && e.address.port === entry.server_properties.server_port); - if(history) { - label_server_name.text(history.name); - label_server_region.empty().append( - $.spawn("div").addClass("country flag-" + history.country.toLowerCase()), - $.spawn("div").text(i18n.country_name(history.country, tr("Global"))) - ); - label_client_count.text(history.clients_online + "/" + history.clients_total); - label_connection_count.empty().append( - ...MessageHelper.formatMessage(tr("You've connected {} times"), $.spawn("div").addClass("connect-count").text(history.total_connection)) - ); - } else { - label_server_name.text(tr("Unknown")); - label_server_region.empty().text(tr("Unknown")); - label_client_count.text(tr("Unknown")); - label_connection_count.empty().append( - ...MessageHelper.formatMessage(tr("You {} connected to that server address"), $.spawn("div").addClass("connect-never").text("never")) - ); - } - label_last_ping.text(tr("Average ping isn't yet supported")); - } else { - label_server_name.text("--"); - label_server_region.text("--"); - label_last_ping.text("--"); - label_client_count.text("--"); - label_connection_count.text("--"); - } - }; - - const update_selected = () => { - input_bookmark_name.prop("disabled", !selected_bookmark); - input_connect_profile.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); - input_server_address.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); - input_server_password.prop("disabled", !selected_bookmark || selected_bookmark.type !== bookmarks.BookmarkType.ENTRY); - - if(selected_bookmark) { - input_bookmark_name.val(selected_bookmark.display_name); - label_bookmark_name.text(selected_bookmark.display_name); - } - - if(selected_bookmark && selected_bookmark.type === bookmarks.BookmarkType.ENTRY) { - const entry = selected_bookmark as bookmarks.Bookmark; - - const address = entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port)); - label_server_address.text(address); - input_server_address.val(address); - - let profile = input_connect_profile.find("option[value='" + entry.connect_profile + "']"); - if(profile.length == 0) - profile = input_connect_profile.find("option[value=default]"); - profile.prop("selected", true); - - input_server_password.val(entry.server_properties.server_password_hash || entry.server_properties.server_password ? "WolverinDEV" : ""); - } else { - input_server_password.val(""); - input_server_address.val(""); - input_connect_profile.find("option[value='no-value']").prop('selected', true); - label_server_address.text(" "); - } - - update_connect_info(); - }; - - const container_bookmarks = template.find(".container-bookmarks"); - const update_bookmark_list = (_current_selected: string) => { - container_bookmarks.empty(); - selected_bookmark = undefined; - update_selected(); - - const hide_links: boolean[] = []; - const build_entry = (entry: bookmarks.Bookmark | bookmarks.DirectoryBookmark, sibling_data: {first: boolean; last: boolean;}, index: number) => { - let container = $.spawn("div") - .addClass(entry.type === bookmarks.BookmarkType.ENTRY ? "bookmark" : "directory") - .addClass(index > 0 ? "linked" : "") - .addClass(sibling_data.first ? "link-start" : ""); - for (let i = 0; i < index; i++) { - container.append( - $.spawn("div") - .addClass("link") - .addClass(i + 1 === index ? " connected" : "") - .addClass(hide_links[i + 1] ? "hidden" : "") - ); - } - - if (entry.type === bookmarks.BookmarkType.ENTRY) { - const bookmark = entry as bookmarks.Bookmark; - container.append( - bookmark.last_icon_id ? - IconManager.generate_tag(IconManager.load_cached_icon(bookmark.last_icon_id || 0), {animate: false}) : - $.spawn("div").addClass("icon-container icon_em") - ); - } else { - container.append( - $.spawn("div").addClass("icon-container icon_em client-folder") - ); - } - - container.append( - $.spawn("div").addClass("name").attr("title", entry.display_name).text(entry.display_name) + const history = connection_log.history().find(e => e.address.hostname === entry.server_properties.server_address && e.address.port === entry.server_properties.server_port); + if(history) { + label_server_name.text(history.name); + label_server_region.empty().append( + $.spawn("div").addClass("country flag-" + history.country.toLowerCase()), + $.spawn("div").text(i18nc.country_name(history.country, tr("Global"))) ); + label_client_count.text(history.clients_online + "/" + history.clients_total); + label_connection_count.empty().append( + ...formatMessage(tr("You've connected {} times"), $.spawn("div").addClass("connect-count").text(history.total_connection)) + ); + } else { + label_server_name.text(tr("Unknown")); + label_server_region.empty().text(tr("Unknown")); + label_client_count.text(tr("Unknown")); + label_connection_count.empty().append( + ...formatMessage(tr("You {} connected to that server address"), $.spawn("div").addClass("connect-never").text("never")) + ); + } + label_last_ping.text(tr("Average ping isn't yet supported")); + } else { + label_server_name.text("--"); + label_server_region.text("--"); + label_last_ping.text("--"); + label_client_count.text("--"); + label_connection_count.text("--"); + } + }; - container.appendTo(container_bookmarks); - container.on('click', event => { - if(selected_bookmark === entry) - return; + const update_selected = () => { + input_bookmark_name.prop("disabled", !selected_bookmark); + input_connect_profile.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY); + input_server_address.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY); + input_server_password.prop("disabled", !selected_bookmark || selected_bookmark.type !== BookmarkType.ENTRY); - selected_bookmark = entry; - container_bookmarks.find(".selected").removeClass("selected"); - container.addClass("selected"); - update_buttons(); - update_selected(); - }); - if(entry.unique_id === _current_selected) - container.trigger('click'); + if(selected_bookmark) { + input_bookmark_name.val(selected_bookmark.display_name); + label_bookmark_name.text(selected_bookmark.display_name); + } - hide_links.push(sibling_data.last); - let cindex = 0; - const children = (entry as bookmarks.DirectoryBookmark).content || []; - for (const child of children) - build_entry(child, {first: cindex++ == 0, last: cindex == children.length}, index + 1); - hide_links.pop(); - }; + if(selected_bookmark && selected_bookmark.type === BookmarkType.ENTRY) { + const entry = selected_bookmark as Bookmark; + const address = entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port)); + label_server_address.text(address); + input_server_address.val(address); + + let profile = input_connect_profile.find("option[value='" + entry.connect_profile + "']"); + if(profile.length == 0) + profile = input_connect_profile.find("option[value=default]"); + profile.prop("selected", true); + + input_server_password.val(entry.server_properties.server_password_hash || entry.server_properties.server_password ? "WolverinDEV" : ""); + } else { + input_server_password.val(""); + input_server_address.val(""); + input_connect_profile.find("option[value='no-value']").prop('selected', true); + label_server_address.text(" "); + } + + update_connect_info(); + }; + + const container_bookmarks = template.find(".container-bookmarks"); + const update_bookmark_list = (_current_selected: string) => { + container_bookmarks.empty(); + selected_bookmark = undefined; + update_selected(); + + const hide_links: boolean[] = []; + const build_entry = (entry: Bookmark | DirectoryBookmark, sibling_data: {first: boolean; last: boolean;}, index: number) => { + let container = $.spawn("div") + .addClass(entry.type === BookmarkType.ENTRY ? "bookmark" : "directory") + .addClass(index > 0 ? "linked" : "") + .addClass(sibling_data.first ? "link-start" : ""); + for (let i = 0; i < index; i++) { + container.append( + $.spawn("div") + .addClass("link") + .addClass(i + 1 === index ? " connected" : "") + .addClass(hide_links[i + 1] ? "hidden" : "") + ); + } + + if (entry.type === BookmarkType.ENTRY) { + const bookmark = entry as Bookmark; + container.append( + bookmark.last_icon_id ? + IconManager.generate_tag(IconManager.load_cached_icon(bookmark.last_icon_id || 0), {animate: false}) : + $.spawn("div").addClass("icon-container icon_em") + ); + } else { + container.append( + $.spawn("div").addClass("icon-container icon_em client-folder") + ); + } + + container.append( + $.spawn("div").addClass("name").attr("title", entry.display_name).text(entry.display_name) + ); + + container.appendTo(container_bookmarks); + container.on('click', event => { + if(selected_bookmark === entry) + return; + + selected_bookmark = entry; + container_bookmarks.find(".selected").removeClass("selected"); + container.addClass("selected"); + update_buttons(); + update_selected(); + }); + if(entry.unique_id === _current_selected) + container.trigger('click'); + + hide_links.push(sibling_data.last); let cindex = 0; - const children = bookmarks.bookmarks().content; - for (const bookmark of children) - build_entry(bookmark, {first: cindex++ == 0, last: cindex == children.length}, 0); + const children = (entry as DirectoryBookmark).content || []; + for (const child of children) + build_entry(child, {first: cindex++ == 0, last: cindex == children.length}, index + 1); + hide_links.pop(); }; - /* generate profile list */ - { + let cindex = 0; + const children = bookmarks().content; + for (const bookmark of children) + build_entry(bookmark, {first: cindex++ == 0, last: cindex == children.length}, 0); + }; + + /* generate profile list */ + { + input_connect_profile.append( + $.spawn("option") + .attr("value", "no-value") + .text("") + .css("display", "none") + ); + for(const profile of profiles()) { input_connect_profile.append( $.spawn("option") - .attr("value", "no-value") - .text("") - .css("display", "none") + .attr("value", profile.id) + .text(profile.profile_name) ); - for(const profile of profiles.profiles()) { - input_connect_profile.append( - $.spawn("option") - .attr("value", profile.id) - .text(profile.profile_name) - ); + } + } + + /* buttons */ + { + button_delete.on('click', event => { + if(!selected_bookmark) return; + + if(selected_bookmark.type === BookmarkType.DIRECTORY && (selected_bookmark as DirectoryBookmark).content.length > 0) { + spawnYesNo(tr("Are you sure"), tr("Do you really want to delete this non empty directory?"), answer => { + if(answer) { + delete_bookmark(selected_bookmark); + save_bookmark(selected_bookmark); + update_bookmark_list(undefined); + } + }); + } else { + delete_bookmark(selected_bookmark); + save_bookmark(selected_bookmark); + update_bookmark_list(undefined); } - } - - /* buttons */ - { - button_delete.on('click', event => { - if(!selected_bookmark) return; - - if(selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY && (selected_bookmark as bookmarks.DirectoryBookmark).content.length > 0) { - Modals.spawnYesNo(tr("Are you sure"), tr("Do you really want to delete this non empty directory?"), answer => { - if(answer) { - bookmarks.delete_bookmark(selected_bookmark); - bookmarks.save_bookmark(selected_bookmark); - update_bookmark_list(undefined); - } - }); - } else { - bookmarks.delete_bookmark(selected_bookmark); - bookmarks.save_bookmark(selected_bookmark); - update_bookmark_list(undefined); - } - }); - - button_add_folder.on('click', event => { - createInputModal(tr("Enter a folder name"), tr("Enter the folder name"), text => { - return true; - }, result => { - if(result) { - const mark = bookmarks.create_bookmark_directory( - selected_bookmark ? - selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ? - selected_bookmark as bookmarks.DirectoryBookmark : - selected_bookmark.parent : - bookmarks.bookmarks(), - result as string - ); - bookmarks.save_bookmark(mark); - update_bookmark_list(mark.unique_id); - } - }).open(); - }); - - button_add_bookmark.on('click', event => { - createInputModal(tr("Enter a bookmark name"), tr("Enter the bookmark name"), text => { - return true; - }, result => { - if(result) { - const mark = bookmarks.create_bookmark(result as string, - selected_bookmark ? - selected_bookmark.type === bookmarks.BookmarkType.DIRECTORY ? - selected_bookmark as bookmarks.DirectoryBookmark : - selected_bookmark.parent : - bookmarks.bookmarks(), { - server_password: "", - server_port: 9987, - server_address: "", - server_password_hash: "" - }, ""); - bookmarks.save_bookmark(mark); - update_bookmark_list(mark.unique_id); - } - }).open(); - }); - - button_connect_tab.on('click', event => { - bookmarks.boorkmak_connect(selected_bookmark as bookmarks.Bookmark, true); - modal.close(); - }).toggle(!settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION)); - - button_connect.on('click', event => { - bookmarks.boorkmak_connect(selected_bookmark as bookmarks.Bookmark, false); - modal.close(); - }); - } - - /* inputs */ - { - input_bookmark_name.on('change keydown', event => { - const name = input_bookmark_name.val() as string; - const valid = name.length > 3; - input_bookmark_name.firstParent(".input-boxed").toggleClass("is-invalid", !valid); - - if(event.type === "change" && valid) { - selected_bookmark.display_name = name; - label_bookmark_name.text(name); - } - }); - - input_server_address.on('change keydown', event => { - const address = input_server_address.val() as string; - const valid = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN); - input_server_address.firstParent(".input-boxed").toggleClass("is-invalid", !valid); - - if(valid) { - const entry = selected_bookmark as bookmarks.Bookmark; - let _v6_end = address.indexOf(']'); - let idx = address.lastIndexOf(':'); - if(idx != -1 && idx > _v6_end) { - entry.server_properties.server_port = parseInt(address.substr(idx + 1)); - entry.server_properties.server_address = address.substr(0, idx); - } else { - entry.server_properties.server_address = address; - entry.server_properties.server_port = 9987; - } - - label_server_address.text(entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port))); - update_connect_info(); - } - }); - - input_connect_profile.on('change', event => { - const id = input_connect_profile.val() as string; - const profile = profiles.profiles().find(e => e.id === id); - if(profile) { - (selected_bookmark as bookmarks.Bookmark).connect_profile = id; - } else { - log.warn(LogCategory.GENERAL, tr("Failed to change connect profile for profile %s to %s"), selected_bookmark.unique_id, id); - } - }) - } - - /* Arrow key navigation for the bookmark list */ - { - let _focused = false; - let _focus_listener; - let _key_listener; - - _focus_listener = event => { - _focused = false; - let element = event.target as HTMLElement; - while(element) { - if(element === container_bookmarks[0]) { - _focused = true; - break; - } - element = element.parentNode as HTMLElement; - } - }; - - _key_listener = event => { - if(!_focused) return; - - if(event.key.toLowerCase() === "arrowdown") { - container_bookmarks.find(".selected").next().trigger('click'); - } else if(event.key.toLowerCase() === "arrowup") { - container_bookmarks.find(".selected").prev().trigger('click'); - } - }; - - document.addEventListener('click', _focus_listener); - document.addEventListener('keydown', _key_listener); - modal.close_listener.push(() => { - document.removeEventListener('click', _focus_listener); - document.removeEventListener('keydown', _key_listener); - }) - } - - - update_bookmark_list(undefined); - update_buttons(); - - template.find(".container-bookmarks").on('keydown', event => { - console.error(event.key); }); - template.find(".button-close").on('click', event => modal.close()); - return template.children(); - }, - footer: undefined, - width: "40em" - }); - modal.htmlTag.dividerfy().find(".modal-body").addClass("modal-bookmarks"); - modal.close_listener.push(() => { - control_bar.update_bookmarks(); - top_menu.rebuild_bookmarks(); - }); + button_add_folder.on('click', event => { + createInputModal(tr("Enter a folder name"), tr("Enter the folder name"), text => { + return true; + }, result => { + if(result) { + const mark = create_bookmark_directory( + selected_bookmark ? + selected_bookmark.type === BookmarkType.DIRECTORY ? + selected_bookmark as DirectoryBookmark : + selected_bookmark.parent : + bookmarks(), + result as string + ); + save_bookmark(mark); + update_bookmark_list(mark.unique_id); + } + }).open(); + }); - modal.open(); - } + button_add_bookmark.on('click', event => { + createInputModal(tr("Enter a bookmark name"), tr("Enter the bookmark name"), text => { + return true; + }, result => { + if(result) { + const mark = create_bookmark(result as string, + selected_bookmark ? + selected_bookmark.type === BookmarkType.DIRECTORY ? + selected_bookmark as DirectoryBookmark : + selected_bookmark.parent : + bookmarks(), { + server_password: "", + server_port: 9987, + server_address: "", + server_password_hash: "" + }, ""); + save_bookmark(mark); + update_bookmark_list(mark.unique_id); + } + }).open(); + }); + + button_connect_tab.on('click', event => { + boorkmak_connect(selected_bookmark as Bookmark, true); + modal.close(); + }).toggle(!settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION)); + + button_connect.on('click', event => { + boorkmak_connect(selected_bookmark as Bookmark, false); + modal.close(); + }); + } + + /* inputs */ + { + input_bookmark_name.on('change keydown', event => { + const name = input_bookmark_name.val() as string; + const valid = name.length > 3; + input_bookmark_name.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + + if(event.type === "change" && valid) { + selected_bookmark.display_name = name; + label_bookmark_name.text(name); + } + }); + + input_server_address.on('change keydown', event => { + const address = input_server_address.val() as string; + const valid = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN); + input_server_address.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + + if(valid) { + const entry = selected_bookmark as Bookmark; + let _v6_end = address.indexOf(']'); + let idx = address.lastIndexOf(':'); + if(idx != -1 && idx > _v6_end) { + entry.server_properties.server_port = parseInt(address.substr(idx + 1)); + entry.server_properties.server_address = address.substr(0, idx); + } else { + entry.server_properties.server_address = address; + entry.server_properties.server_port = 9987; + } + + label_server_address.text(entry.server_properties.server_address + (entry.server_properties.server_port == 9987 ? "" : (" " + entry.server_properties.server_port))); + update_connect_info(); + } + }); + + input_connect_profile.on('change', event => { + const id = input_connect_profile.val() as string; + const profile = profiles().find(e => e.id === id); + if(profile) { + (selected_bookmark as Bookmark).connect_profile = id; + } else { + log.warn(LogCategory.GENERAL, tr("Failed to change connect profile for profile %s to %s"), selected_bookmark.unique_id, id); + } + }) + } + + /* Arrow key navigation for the bookmark list */ + { + let _focused = false; + let _focus_listener; + let _key_listener; + + _focus_listener = event => { + _focused = false; + let element = event.target as HTMLElement; + while(element) { + if(element === container_bookmarks[0]) { + _focused = true; + break; + } + element = element.parentNode as HTMLElement; + } + }; + + _key_listener = event => { + if(!_focused) return; + + if(event.key.toLowerCase() === "arrowdown") { + container_bookmarks.find(".selected").next().trigger('click'); + } else if(event.key.toLowerCase() === "arrowup") { + container_bookmarks.find(".selected").prev().trigger('click'); + } + }; + + document.addEventListener('click', _focus_listener); + document.addEventListener('keydown', _key_listener); + modal.close_listener.push(() => { + document.removeEventListener('click', _focus_listener); + document.removeEventListener('keydown', _key_listener); + }) + } + + + update_bookmark_list(undefined); + update_buttons(); + + template.find(".container-bookmarks").on('keydown', event => { + console.error(event.key); + }); + template.find(".button-close").on('click', event => modal.close()); + return template.children(); + }, + footer: undefined, + width: "40em" + }); + + modal.htmlTag.dividerfy().find(".modal-body").addClass("modal-bookmarks"); + modal.close_listener.push(() => { + control_bar.update_bookmarks(); + top_menu.rebuild_bookmarks(); + }); + + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalChangeLatency.ts b/shared/js/ui/modal/ModalChangeLatency.ts index 3b8dca95..d279e276 100644 --- a/shared/js/ui/modal/ModalChangeLatency.ts +++ b/shared/js/ui/modal/ModalChangeLatency.ts @@ -1,114 +1,115 @@ -/// -/// -/// +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {ClientEntry} from "tc-shared/ui/client"; +import {voice} from "tc-shared/connection/ConnectionBase"; +import LatencySettings = voice.LatencySettings; +import {Slider, sliderfy} from "tc-shared/ui/elements/Slider"; +import * as htmltags from "tc-shared/ui/htmltags"; -namespace Modals { - let modal: Modal; - export function spawnChangeLatency(client: ClientEntry, current: connection.voice.LatencySettings, reset: () => connection.voice.LatencySettings, apply: (settings: connection.voice.LatencySettings) => any, callback_flush?: () => any) { - if(modal) modal.close(); +let modal: Modal; +export function spawnChangeLatency(client: ClientEntry, current: LatencySettings, reset: () => LatencySettings, apply: (settings: LatencySettings) => any, callback_flush?: () => any) { + if(modal) modal.close(); - const begin = Object.assign({}, current); - current = Object.assign({}, current); + const begin = Object.assign({}, current); + current = Object.assign({}, current); - modal = createModal({ - header: tr("Change playback latency"), - body: function () { - let tag = $("#tmpl_change_latency").renderTag({ - client: htmltags.generate_client_object({ - add_braces: false, - client_name: client.clientNickName(), - client_unique_id: client.properties.client_unique_identifier, - client_id: client.clientId() - }), + modal = createModal({ + header: tr("Change playback latency"), + body: function () { + let tag = $("#tmpl_change_latency").renderTag({ + client: htmltags.generate_client_object({ + add_braces: false, + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier, + client_id: client.clientId() + }), - have_flush: (typeof(callback_flush) === "function") + have_flush: (typeof(callback_flush) === "function") + }); + + const update_value = () => { + const valid = current.min_buffer < current.max_buffer; + + modal.htmlTag.find(".modal-body").toggleClass("modal-red", !valid); + modal.htmlTag.find(".modal-body").toggleClass("modal-green", valid); + + if(!valid) + return; + + apply(current); + }; + + let slider_min: Slider, slider_max: Slider; + { + const container = tag.find(".container-min"); + const tag_value = container.find(".value"); + + const slider_tag = container.find(".container-slider"); + slider_min = sliderfy(slider_tag, { + initial_value: current.min_buffer, + step: 20, + max_value: 1000, + min_value: 0, + + unit: 'ms' }); - - const update_value = () => { - const valid = current.min_buffer < current.max_buffer; - - modal.htmlTag.find(".modal-body").toggleClass("modal-red", !valid); - modal.htmlTag.find(".modal-body").toggleClass("modal-green", valid); - - if(!valid) - return; - - apply(current); - }; - - let slider_min: Slider, slider_max: Slider; - { - const container = tag.find(".container-min"); - const tag_value = container.find(".value"); - - const slider_tag = container.find(".container-slider"); - slider_min = sliderfy(slider_tag, { - initial_value: current.min_buffer, - step: 20, - max_value: 1000, - min_value: 0, - - unit: 'ms' - }); - slider_tag.on('change', event => { - current.min_buffer = parseInt(slider_tag.attr("value")); - tag_value.text(current.min_buffer + "ms"); - update_value(); - }); - + slider_tag.on('change', event => { + current.min_buffer = parseInt(slider_tag.attr("value")); tag_value.text(current.min_buffer + "ms"); - } + update_value(); + }); - { - const container = tag.find(".container-max"); - const tag_value = container.find(".value"); + tag_value.text(current.min_buffer + "ms"); + } - const slider_tag = container.find(".container-slider"); - slider_max = sliderfy(slider_tag, { - initial_value: current.max_buffer, - step: 20, - max_value: 1020, - min_value: 20, + { + const container = tag.find(".container-max"); + const tag_value = container.find(".value"); - unit: 'ms' - }); + const slider_tag = container.find(".container-slider"); + slider_max = sliderfy(slider_tag, { + initial_value: current.max_buffer, + step: 20, + max_value: 1020, + min_value: 20, - slider_tag.on('change', event => { - current.max_buffer = parseInt(slider_tag.attr("value")); - tag_value.text(current.max_buffer + "ms"); - update_value(); - }); + unit: 'ms' + }); + slider_tag.on('change', event => { + current.max_buffer = parseInt(slider_tag.attr("value")); tag_value.text(current.max_buffer + "ms"); - } - setTimeout(update_value, 0); - - tag.find(".button-close").on('click', event => { - modal.close(); + update_value(); }); - tag.find(".button-cancel").on('click', event => { - apply(begin); - modal.close(); - }); + tag_value.text(current.max_buffer + "ms"); + } + setTimeout(update_value, 0); - tag.find(".button-reset").on('click', event => { - current = Object.assign({}, reset()); - slider_max.value(current.max_buffer); - slider_min.value(current.min_buffer); - }); + tag.find(".button-close").on('click', event => { + modal.close(); + }); - tag.find(".button-flush").on('click', event => callback_flush()); + tag.find(".button-cancel").on('click', event => { + apply(begin); + modal.close(); + }); - return tag.children(); - }, - footer: null, + tag.find(".button-reset").on('click', event => { + current = Object.assign({}, reset()); + slider_max.value(current.max_buffer); + slider_min.value(current.min_buffer); + }); - width: 600 - }); + tag.find(".button-flush").on('click', event => callback_flush()); - modal.close_listener.push(() => modal = undefined); - modal.open(); - modal.htmlTag.find(".modal-body").addClass("modal-latency"); - } + return tag.children(); + }, + footer: null, + + width: 600 + }); + + modal.close_listener.push(() => modal = undefined); + modal.open(); + modal.htmlTag.find(".modal-body").addClass("modal-latency"); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalChangeVolume.ts b/shared/js/ui/modal/ModalChangeVolume.ts index b9b0778a..8271705e 100644 --- a/shared/js/ui/modal/ModalChangeVolume.ts +++ b/shared/js/ui/modal/ModalChangeVolume.ts @@ -1,77 +1,76 @@ -/// -/// -/// +//TODO: Use the max limit! -namespace Modals { - //TODO: Use the max limit! +import {sliderfy} from "tc-shared/ui/elements/Slider"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {ClientEntry} from "tc-shared/ui/client"; +import * as htmltags from "tc-shared/ui/htmltags"; - let modal: Modal; - export function spawnChangeVolume(client: ClientEntry, local: boolean, current: number, max: number | undefined, callback: (number) => void) { - if(modal) modal.close(); +let modal: Modal; +export function spawnChangeVolume(client: ClientEntry, local: boolean, current: number, max: number | undefined, callback: (number) => void) { + if(modal) modal.close(); - let new_value: number; - modal = createModal({ - header: local ? tr("Change local volume") : tr("Change remote volume"), - body: function () { - let tag = $("#tmpl_change_volume").renderTag({ - client: htmltags.generate_client_object({ - add_braces: false, - client_name: client.clientNickName(), - client_unique_id: client.properties.client_unique_identifier, - client_id: client.clientId() - }), - local: local - }); + let new_value: number; + modal = createModal({ + header: local ? tr("Change local volume") : tr("Change remote volume"), + body: function () { + let tag = $("#tmpl_change_volume").renderTag({ + client: htmltags.generate_client_object({ + add_braces: false, + client_name: client.clientNickName(), + client_unique_id: client.properties.client_unique_identifier, + client_id: client.clientId() + }), + local: local + }); - const container_value = tag.find(".info .value"); - const set_value = value => { - const number = value > 100 ? value - 100 : 100 - value; - container_value.html((value == 100 ? "±" : value > 100 ? "+" : "-") + number + "%"); + const container_value = tag.find(".info .value"); + const set_value = value => { + const number = value > 100 ? value - 100 : 100 - value; + container_value.html((value == 100 ? "±" : value > 100 ? "+" : "-") + number + "%"); - new_value = value / 100; - if(local) callback(new_value); - }; - set_value(current * 100); + new_value = value / 100; + if(local) callback(new_value); + }; + set_value(current * 100); - const slider_tag = tag.find(".container-slider"); - const slider = sliderfy(slider_tag, { - initial_value: current * 100, - step: 1, - max_value: 200, - min_value: 0, + const slider_tag = tag.find(".container-slider"); + const slider = sliderfy(slider_tag, { + initial_value: current * 100, + step: 1, + max_value: 200, + min_value: 0, - unit: '%' - }); - slider_tag.on('change', event => set_value(parseInt(slider_tag.attr("value")))); + unit: '%' + }); + slider_tag.on('change', event => set_value(parseInt(slider_tag.attr("value")))); - tag.find(".button-save").on('click', event => { - if(typeof(new_value) !== "undefined") callback(new_value); - modal.close(); - }); + tag.find(".button-save").on('click', event => { + if(typeof(new_value) !== "undefined") callback(new_value); + modal.close(); + }); - tag.find(".button-cancel").on('click', event => { - callback(current); - modal.close(); - }); + tag.find(".button-cancel").on('click', event => { + callback(current); + modal.close(); + }); - tag.find(".button-reset").on('click', event => { - slider.value(100); - }); + tag.find(".button-reset").on('click', event => { + slider.value(100); + }); - tag.find(".button-apply").on('click', event => { - callback(new_value); - new_value = undefined; - }); + tag.find(".button-apply").on('click', event => { + callback(new_value); + new_value = undefined; + }); - return tag.children(); - }, - footer: null, + return tag.children(); + }, + footer: null, - width: 600 - }); + width: 600 + }); - modal.close_listener.push(() => modal = undefined); - modal.open(); - modal.htmlTag.find(".modal-body").addClass("modal-volume"); - } + modal.close_listener.push(() => modal = undefined); + modal.open(); + modal.htmlTag.find(".modal-body").addClass("modal-volume"); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalChannelInfo.ts b/shared/js/ui/modal/ModalChannelInfo.ts index db91989f..7433eea0 100644 --- a/shared/js/ui/modal/ModalChannelInfo.ts +++ b/shared/js/ui/modal/ModalChannelInfo.ts @@ -1,152 +1,157 @@ -namespace Modals { - export function openChannelInfo(channel: ChannelEntry) { - let modal: Modal; +import {createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {formatMessage} from "tc-shared/ui/frames/chat"; - modal = createModal({ - header: tr("Channel information: ") + channel.channelName(), - body: () => { - const template = $("#tmpl_channel_info").renderTag(); +export function openChannelInfo(channel: ChannelEntry) { + let modal: Modal; - const update_values = (container) => { + modal = createModal({ + header: tr("Channel information: ") + channel.channelName(), + body: () => { + const template = $("#tmpl_channel_info").renderTag(); - apply_channel_description(container.find(".container-description"), channel); - apply_general(container, channel); - }; + const update_values = (container) => { - template.find(".button-copy").on('click', event => { - copy_to_clipboard(channel.properties.channel_description); - createInfoModal(tr("Description copied"), tr("The channel description has been copied to your clipboard!")).open(); - }); + apply_channel_description(container.find(".container-description"), channel); + apply_general(container, channel); + }; - const button_update = template.find(".button-update"); - button_update.on('click', event => update_values(modal.htmlTag)); + template.find(".button-copy").on('click', event => { + copy_to_clipboard(channel.properties.channel_description); + createInfoModal(tr("Description copied"), tr("The channel description has been copied to your clipboard!")).open(); + }); - update_values(template); - tooltip(template); - return template.children(); - }, - footer: null, - width: "65em" - }); - modal.htmlTag.find(".button-close").on('click', event => modal.close()); - modal.htmlTag.find(".modal-body").addClass("modal-channel-info"); - modal.open(); + const button_update = template.find(".button-update"); + button_update.on('click', event => update_values(modal.htmlTag)); + + update_values(template); + tooltip.initialize(template); + return template.children(); + }, + footer: null, + width: "65em" + }); + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".modal-body").addClass("modal-channel-info"); + modal.open(); +} + +declare const xbbcode; +function apply_channel_description(container: JQuery, channel: ChannelEntry) { + const container_value = container.find(".value"); + const container_no_value = container.find(".no-value"); + + channel.getChannelDescription().then(description => { + if(description) { + const result = xbbcode.parse(description, {}); + container_value[0].innerHTML = result.build_html(); + container_no_value.hide(); + container_value.show(); + } else { + container_no_value.text(tr("Channel has no description")); + } + }); + + container_value.hide(); + container_no_value.text(tr("loading...")).show(); +} + +const codec_names = [ + tr("Speex Narrowband"), + tr("Speex Wideband"), + tr("Speex Ultra-Wideband"), + tr("CELT Mono"), + tr("Opus Voice"), + tr("Opus Music") +]; + +function apply_general(container: JQuery, channel: ChannelEntry) { + /* channel type */ + { + const tag = container.find(".channel-type .value").empty(); + if(channel.properties.channel_flag_permanent) + tag.text(tr("Permanent")); + else if(channel.properties.channel_flag_semi_permanent) + tag.text(tr("Semi permanent")); + else + //TODO: Channel delete delay! + tag.text(tr("Temporary")); } - function apply_channel_description(container: JQuery, channel: ChannelEntry) { - const container_value = container.find(".value"); - const container_no_value = container.find(".no-value"); - - channel.getChannelDescription().then(description => { - if(description) { - const result = xbbcode.parse(description, {}); - container_value[0].innerHTML = result.build_html(); - container_no_value.hide(); - container_value.show(); - } else { - container_no_value.text(tr("Channel has no description")); - } - }); - - container_value.hide(); - container_no_value.text(tr("loading...")).show(); + /* chat mode */ + { + const tag = container.find(".chat-mode .value").empty(); + if(channel.properties.channel_flag_conversation_private || channel.properties.channel_flag_password) { + tag.text(tr("Private")); + } else { + if(channel.properties.channel_conversation_history_length == -1) + tag.text(tr("Public; Semi permanent message saving")); + else if(channel.properties.channel_conversation_history_length == 0) + tag.text(tr("Public; Permanent message saving")); + else + tag.append(formatMessage(tr("Public; Saving last {} messages"), channel.properties.channel_conversation_history_length)); + } } - const codec_names = [ - tr("Speex Narrowband"), - tr("Speex Wideband"), - tr("Speex Ultra-Wideband"), - tr("CELT Mono"), - tr("Opus Voice"), - tr("Opus Music") - ]; + /* current clients */ + { + const tag = container.find(".current-clients .value").empty(); - function apply_general(container: JQuery, channel: ChannelEntry) { - /* channel type */ - { - const tag = container.find(".channel-type .value").empty(); - if(channel.properties.channel_flag_permanent) - tag.text(tr("Permanent")); - else if(channel.properties.channel_flag_semi_permanent) - tag.text(tr("Semi permanent")); - else - //TODO: Channel delete delay! - tag.text(tr("Temporary")); - } - - /* chat mode */ - { - const tag = container.find(".chat-mode .value").empty(); - if(channel.properties.channel_flag_conversation_private || channel.properties.channel_flag_password) { - tag.text(tr("Private")); - } else { - if(channel.properties.channel_conversation_history_length == -1) - tag.text(tr("Public; Semi permanent message saving")); - else if(channel.properties.channel_conversation_history_length == 0) - tag.text(tr("Public; Permanent message saving")); - else - tag.append(MessageHelper.formatMessage(tr("Public; Saving last {} messages"), channel.properties.channel_conversation_history_length)); + if(channel.flag_subscribed) { + const current = channel.clients().length; + let channel_limit = tr("Unlimited"); + if(!channel.properties.channel_flag_maxclients_unlimited) + channel_limit = "" + channel.properties.channel_maxclients; + else if(!channel.properties.channel_flag_maxfamilyclients_unlimited) { + if(channel.properties.channel_maxfamilyclients >= 0) + channel_limit = "" + channel.properties.channel_maxfamilyclients; } + + tag.text(current + " / " + channel_limit); + } else { + tag.text(tr("Channel not subscribed")); } + } - /* current clients */ - { - const tag = container.find(".current-clients .value").empty(); + /* audio codec */ + { + const tag = container.find(".audio-codec .value").empty(); + tag.text((codec_names[channel.properties.channel_codec] || tr("Unknown")) + " (" + channel.properties.channel_codec_quality + ")") + } - if(channel.flag_subscribed) { - const current = channel.clients().length; - let channel_limit = tr("Unlimited"); - if(!channel.properties.channel_flag_maxclients_unlimited) - channel_limit = "" + channel.properties.channel_maxclients; - else if(!channel.properties.channel_flag_maxfamilyclients_unlimited) { - if(channel.properties.channel_maxfamilyclients >= 0) - channel_limit = "" + channel.properties.channel_maxfamilyclients; - } + /* audio encrypted */ + { + const tag = container.find(".audio-encrypted .value").empty(); + const mode = channel.channelTree.server.properties.virtualserver_codec_encryption_mode; + let appendix; + if(mode == 1) + appendix = tr("Overridden by the server with Unencrypted!"); + else if(mode == 2) + appendix = tr("Overridden by the server with Encrypted!"); - tag.text(current + " / " + channel_limit); - } else { - tag.text(tr("Channel not subscribed")); - } - } + tag.html((channel.properties.channel_codec_is_unencrypted ? tr("Unencrypted") : tr("Encrypted")) + (appendix ? "
" + appendix : "")) + } - /* audio codec */ - { - const tag = container.find(".audio-codec .value").empty(); - tag.text((codec_names[channel.properties.channel_codec] || tr("Unknown")) + " (" + channel.properties.channel_codec_quality + ")") - } + /* flag password */ + { + const tag = container.find(".flag-password .value").empty(); + if(channel.properties.channel_flag_password) + tag.text(tr("Yes")); + else + tag.text(tr("No")); + } - /* audio encrypted */ - { - const tag = container.find(".audio-encrypted .value").empty(); - const mode = channel.channelTree.server.properties.virtualserver_codec_encryption_mode; - let appendix; - if(mode == 1) - appendix = tr("Overridden by the server with Unencrypted!"); - else if(mode == 2) - appendix = tr("Overridden by the server with Encrypted!"); - - tag.html((channel.properties.channel_codec_is_unencrypted ? tr("Unencrypted") : tr("Encrypted")) + (appendix ? "
" + appendix : "")) - } - - /* flag password */ - { - const tag = container.find(".flag-password .value").empty(); - if(channel.properties.channel_flag_password) - tag.text(tr("Yes")); - else - tag.text(tr("No")); - } - - /* topic */ - { - const container_tag = container.find(".topic"); - const tag = container_tag.find(".value").empty(); - if(channel.properties.channel_topic) { - container_tag.show(); - tag.text(channel.properties.channel_topic); - } else { - container_tag.hide(); - } + /* topic */ + { + const container_tag = container.find(".topic"); + const tag = container_tag.find(".value").empty(); + if(channel.properties.channel_topic) { + container_tag.show(); + tag.text(channel.properties.channel_topic); + } else { + container_tag.hide(); } } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalClientInfo.ts b/shared/js/ui/modal/ModalClientInfo.ts index 482a42d5..b2249283 100644 --- a/shared/js/ui/modal/ModalClientInfo.ts +++ b/shared/js/ui/modal/ModalClientInfo.ts @@ -1,512 +1,519 @@ -namespace Modals { - type InfoUpdateCallback = (info: ClientConnectionInfo) => any; - export function openClientInfo(client: ClientEntry) { - let modal: Modal; - let update_callbacks: InfoUpdateCallback[] = []; +import {ClientConnectionInfo, ClientEntry} from "tc-shared/ui/client"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import * as i18nc from "tc-shared/i18n/country"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {format_number, network} from "tc-shared/ui/frames/chat"; - modal = createModal({ - header: tr("Profile Information: ") + client.clientNickName(), - body: () => { - const template = $("#tmpl_client_info").renderTag(); +type InfoUpdateCallback = (info: ClientConnectionInfo) => any; +export function openClientInfo(client: ClientEntry) { + let modal: Modal; + let update_callbacks: InfoUpdateCallback[] = []; - /* the tab functionality */ - { - const container_tabs = template.find(".container-categories"); - container_tabs.find(".categories .entry").on('click', event => { - const entry = $(event.target); + modal = createModal({ + header: tr("Profile Information: ") + client.clientNickName(), + body: () => { + const template = $("#tmpl_client_info").renderTag(); - container_tabs.find(".bodies > .body").addClass("hidden"); - container_tabs.find(".categories > .selected").removeClass("selected"); + /* the tab functionality */ + { + const container_tabs = template.find(".container-categories"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); - entry.addClass("selected"); - container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); - }); + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); - container_tabs.find(".entry").first().trigger('click'); - } + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); - apply_static_info(client, template, modal, update_callbacks); - apply_client_status(client, template, modal, update_callbacks); - apply_basic_info(client, template.find(".container-basic"), modal, update_callbacks); - apply_groups(client, template.find(".container-groups"), modal, update_callbacks); - apply_packets(client, template.find(".container-packets"), modal, update_callbacks); + container_tabs.find(".entry").first().trigger('click'); + } - tooltip(template); - return template.children(); - }, - footer: null, + apply_static_info(client, template, modal, update_callbacks); + apply_client_status(client, template, modal, update_callbacks); + apply_basic_info(client, template.find(".container-basic"), modal, update_callbacks); + apply_groups(client, template.find(".container-groups"), modal, update_callbacks); + apply_packets(client, template.find(".container-packets"), modal, update_callbacks); - width: '60em' + tooltip.initialize(template); + return template.children(); + }, + footer: null, + + width: '60em' + }); + + const updater = setInterval(() => { + client.request_connection_info().then(info => update_callbacks.forEach(e => e(info))); + }, 1000); + + modal.htmlTag.find(".modal-body").addClass("modal-client-info"); + modal.open(); + modal.close_listener.push(() => clearInterval(updater)); +} + +const TIME_SECOND = 1000; +const TIME_MINUTE = 60 * TIME_SECOND; +const TIME_HOUR = 60 * TIME_MINUTE; +const TIME_DAY = 24 * TIME_HOUR; +const TIME_WEEK = 7 * TIME_DAY; + +function format_time(time: number, default_value: string) { + let result = ""; + if(time > TIME_WEEK) { + const amount = Math.floor(time / TIME_WEEK); + result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); + time -= amount * TIME_WEEK; + } + + if(time > TIME_DAY) { + const amount = Math.floor(time / TIME_DAY); + result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); + time -= amount * TIME_DAY; + } + + if(time > TIME_HOUR) { + const amount = Math.floor(time / TIME_HOUR); + result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); + time -= amount * TIME_HOUR; + } + + if(time > TIME_MINUTE) { + const amount = Math.floor(time / TIME_MINUTE); + result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); + time -= amount * TIME_MINUTE; + } + + if(time > TIME_SECOND) { + const amount = Math.floor(time / TIME_SECOND); + result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); + time -= amount * TIME_SECOND; + } + + return result.length > 0 ? result.substring(1) : default_value; +} + +function apply_static_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { + tag.find(".container-avatar").append( + client.channelTree.client.fileManager.avatars.generate_chat_tag({database_id: client.properties.client_database_id, id: client.clientId()}, client.properties.client_unique_identifier) + ); + + tag.find(".container-name").append( + client.createChatTag() + ); + + tag.find(".client-description").text( + client.properties.client_description + ); +} + +function apply_client_status(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { + tag.find(".status-output-disabled").toggle(!client.properties.client_output_hardware); + tag.find(".status-input-disabled").toggle(!client.properties.client_input_hardware); + + tag.find(".status-output-muted").toggle(client.properties.client_output_muted); + tag.find(".status-input-muted").toggle(client.properties.client_input_muted); + + + tag.find(".status-away").toggle(client.properties.client_away); + if(client.properties.client_away_message) { + tag.find(".container-away-message").show().find("a").text(client.properties.client_away_message); + } else { + tag.find(".container-away-message").hide(); + } +} + +declare const moment; +function apply_basic_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { + /* Unique ID */ + { + const container = tag.find(".property-unique-id"); + + container.find(".value a").text(client.clientUid()); + container.find(".value-dbid").text(client.properties.client_database_id); + + container.find(".button-copy").on('click', event => { + copy_to_clipboard(client.clientUid()); + createInfoModal(tr("Unique ID copied"), tr("The unique id has been copied to your clipboard!")).open(); + }); + } + + /* TeaForo */ + { + const container = tag.find(".property-teaforo .value").empty(); + + if(client.properties.client_teaforo_id) { + container.children().remove(); + + let text = client.properties.client_teaforo_name; + if((client.properties.client_teaforo_flags & 0x01) > 0) + text += " (" + tr("Banned") + ")"; + if((client.properties.client_teaforo_flags & 0x02) > 0) + text += " (" + tr("Stuff") + ")"; + if((client.properties.client_teaforo_flags & 0x04) > 0) + text += " (" + tr("Premium") + ")"; + + $.spawn("a") + .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) + .attr("target", "_blank") + .text(text) + .appendTo(container); + } else { + container.append($.spawn("a").text(tr("Not connected"))); + } + } + + /* Version */ + { + const container = tag.find(".property-version"); + + let version_full = client.properties.client_version; + let version = version_full.substring(0, version_full.indexOf(" ")); + + container.find(".value").empty().append( + $.spawn("a").attr("title", version_full).text(version), + $.spawn("a").addClass("a-on").text("on"), + $.spawn("a").text(client.properties.client_platform) + ); + + const container_timestamp = container.find(".container-tooltip"); + + let timestamp = -1; + version_full.replace(/\[build: ?([0-9]+)]/gmi, (group, ts) => { + timestamp = parseInt(ts); + return ""; + }); + if(timestamp > 0) { + container_timestamp.find(".value-timestamp").text(moment(timestamp * 1000).format('MMMM Do YYYY, h:mm:ss a')); + container_timestamp.show(); + } else { + container_timestamp.hide(); + } + } + + /* Country */ + { + const container = tag.find(".property-country"); + container.find(".value").empty().append( + $.spawn("div").addClass("country flag-" + client.properties.client_country.toLowerCase()), + $.spawn("a").text(i18nc.country_name(client.properties.client_country, tr("Unknown"))) + ); + } + + /* IP Address */ + { + const container = tag.find(".property-ip"); + const value = container.find(".value a"); + value.text(tr("loading...")); + + container.find(".button-copy").on('click', event => { + copy_to_clipboard(value.text()); + createInfoModal(tr("Client IP copied"), tr("The client IP has been copied to your clipboard!")).open(); }); - const updater = setInterval(() => { - client.request_connection_info().then(info => update_callbacks.forEach(e => e(info))); - }, 1000); - - modal.htmlTag.find(".modal-body").addClass("modal-client-info"); - modal.open(); - modal.close_listener.push(() => clearInterval(updater)); + callbacks.push(info => { + value.text(info.connection_client_ip ? (info.connection_client_ip + ":" + info.connection_client_port) : tr("Hidden")); + }); } - const TIME_SECOND = 1000; - const TIME_MINUTE = 60 * TIME_SECOND; - const TIME_HOUR = 60 * TIME_MINUTE; - const TIME_DAY = 24 * TIME_HOUR; - const TIME_WEEK = 7 * TIME_DAY; + /* first connected */ + { + const container = tag.find(".property-first-connected"); - function format_time(time: number, default_value: string) { - let result = ""; - if(time > TIME_WEEK) { - const amount = Math.floor(time / TIME_WEEK); - result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); - time -= amount * TIME_WEEK; - } - - if(time > TIME_DAY) { - const amount = Math.floor(time / TIME_DAY); - result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); - time -= amount * TIME_DAY; - } - - if(time > TIME_HOUR) { - const amount = Math.floor(time / TIME_HOUR); - result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); - time -= amount * TIME_HOUR; - } - - if(time > TIME_MINUTE) { - const amount = Math.floor(time / TIME_MINUTE); - result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); - time -= amount * TIME_MINUTE; - } - - if(time > TIME_SECOND) { - const amount = Math.floor(time / TIME_SECOND); - result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); - time -= amount * TIME_SECOND; - } - - return result.length > 0 ? result.substring(1) : default_value; + container.find(".value a").text(tr("loading...")); + client.updateClientVariables().then(() => { + container.find(".value a").text(moment(client.properties.client_created * 1000).format('MMMM Do YYYY, h:mm:ss a')); + }).catch(error => { + container.find(".value a").text(tr("error")); + }); } - function apply_static_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { - tag.find(".container-avatar").append( - client.channelTree.client.fileManager.avatars.generate_chat_tag({database_id: client.properties.client_database_id, id: client.clientId()}, client.properties.client_unique_identifier) - ); + /* connect count */ + { + const container = tag.find(".property-connect-count"); - tag.find(".container-name").append( - client.createChatTag() - ); - - tag.find(".client-description").text( - client.properties.client_description - ); + container.find(".value a").text(tr("loading...")); + client.updateClientVariables().then(() => { + container.find(".value a").text(client.properties.client_totalconnections); + }).catch(error => { + container.find(".value a").text(tr("error")); + }); } - function apply_client_status(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { - tag.find(".status-output-disabled").toggle(!client.properties.client_output_hardware); - tag.find(".status-input-disabled").toggle(!client.properties.client_input_hardware); + /* Online since */ + { + const container = tag.find(".property-online-since"); - tag.find(".status-output-muted").toggle(client.properties.client_output_muted); - tag.find(".status-input-muted").toggle(client.properties.client_input_muted); - - - tag.find(".status-away").toggle(client.properties.client_away); - if(client.properties.client_away_message) { - tag.find(".container-away-message").show().find("a").text(client.properties.client_away_message); - } else { - tag.find(".container-away-message").hide(); - } - } - - function apply_basic_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { - /* Unique ID */ - { - const container = tag.find(".property-unique-id"); - - container.find(".value a").text(client.clientUid()); - container.find(".value-dbid").text(client.properties.client_database_id); - - container.find(".button-copy").on('click', event => { - copy_to_clipboard(client.clientUid()); - createInfoModal(tr("Unique ID copied"), tr("The unique id has been copied to your clipboard!")).open(); - }); - } - - /* TeaForo */ - { - const container = tag.find(".property-teaforo .value").empty(); - - if(client.properties.client_teaforo_id) { - container.children().remove(); - - let text = client.properties.client_teaforo_name; - if((client.properties.client_teaforo_flags & 0x01) > 0) - text += " (" + tr("Banned") + ")"; - if((client.properties.client_teaforo_flags & 0x02) > 0) - text += " (" + tr("Stuff") + ")"; - if((client.properties.client_teaforo_flags & 0x04) > 0) - text += " (" + tr("Premium") + ")"; - - $.spawn("a") - .attr("href", "https://forum.teaspeak.de/index.php?members/" + client.properties.client_teaforo_id) - .attr("target", "_blank") - .text(text) - .appendTo(container); - } else { - container.append($.spawn("a").text(tr("Not connected"))); - } - } - - /* Version */ - { - const container = tag.find(".property-version"); - - let version_full = client.properties.client_version; - let version = version_full.substring(0, version_full.indexOf(" ")); - - container.find(".value").empty().append( - $.spawn("a").attr("title", version_full).text(version), - $.spawn("a").addClass("a-on").text("on"), - $.spawn("a").text(client.properties.client_platform) - ); - - const container_timestamp = container.find(".container-tooltip"); - - let timestamp = -1; - version_full.replace(/\[build: ?([0-9]+)]/gmi, (group, ts) => { - timestamp = parseInt(ts); - return ""; - }); - if(timestamp > 0) { - container_timestamp.find(".value-timestamp").text(moment(timestamp * 1000).format('MMMM Do YYYY, h:mm:ss a')); - container_timestamp.show(); - } else { - container_timestamp.hide(); - } - } - - /* Country */ - { - const container = tag.find(".property-country"); - container.find(".value").empty().append( - $.spawn("div").addClass("country flag-" + client.properties.client_country.toLowerCase()), - $.spawn("a").text(i18n.country_name(client.properties.client_country, tr("Unknown"))) - ); - } - - /* IP Address */ - { - const container = tag.find(".property-ip"); - const value = container.find(".value a"); - value.text(tr("loading...")); - - container.find(".button-copy").on('click', event => { - copy_to_clipboard(value.text()); - createInfoModal(tr("Client IP copied"), tr("The client IP has been copied to your clipboard!")).open(); - }); - - callbacks.push(info => { - value.text(info.connection_client_ip ? (info.connection_client_ip + ":" + info.connection_client_port) : tr("Hidden")); - }); - } - - /* first connected */ - { - const container = tag.find(".property-first-connected"); - - container.find(".value a").text(tr("loading...")); - client.updateClientVariables().then(() => { - container.find(".value a").text(moment(client.properties.client_created * 1000).format('MMMM Do YYYY, h:mm:ss a')); - }).catch(error => { - container.find(".value a").text(tr("error")); - }); - } - - /* connect count */ - { - const container = tag.find(".property-connect-count"); - - container.find(".value a").text(tr("loading...")); - client.updateClientVariables().then(() => { - container.find(".value a").text(client.properties.client_totalconnections); - }).catch(error => { - container.find(".value a").text(tr("error")); - }); - } - - /* Online since */ - { - const container = tag.find(".property-online-since"); - - const node = container.find(".value a")[0]; - if(node) { - const update = () => { - node.innerText = format_time(client.calculateOnlineTime() * 1000, tr("0 Seconds")); - }; - - callbacks.push(update); /* keep it in sync with all other updates. Else it looks wired */ - update(); - } - } - - /* Idle time */ - { - const container = tag.find(".property-idle-time"); - const node = container.find(".value a")[0]; - if(node) { - callbacks.push(info => { - node.innerText = format_time(info.connection_idle_time, tr("Currently active")); - }); - node.innerText = tr("loading..."); - } - } - - /* ping */ - { - const container = tag.find(".property-ping"); - const node = container.find(".value a")[0]; - - if(node) { - callbacks.push(info => { - if(info.connection_ping >= 0) - node.innerText = info.connection_ping.toFixed(0) + "ms ± " + info.connection_ping_deviation.toFixed(2) + "ms"; - else if(info.connection_ping == -1 && info.connection_ping_deviation == -1) - node.innerText = tr("Not calculated"); - else - node.innerText = tr("loading..."); - }); - node.innerText = tr("loading..."); - } - } - } - - function apply_groups(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { - /* server groups */ - { - const container_entries = tag.find(".entries"); - const container_empty = tag.find(".container-default-groups"); - - const update_groups = () => { - container_entries.empty(); - container_empty.show(); - - for(const group_id of client.assignedServerGroupIds()) { - if(group_id == client.channelTree.server.properties.virtualserver_default_server_group) - continue; - - const group = client.channelTree.client.groups.serverGroup(group_id); - if(!group) continue; //This shall never happen! - - container_empty.hide(); - container_entries.append($.spawn("div").addClass("entry").append( - client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid), - $.spawn("a").addClass("name").text(group.name + " (" + group.id + ")"), - $.spawn("div").addClass("button-delete").append( - $.spawn("div").addClass("icon_em client-delete").attr("title", tr("Delete group")).on('click', event => { - client.channelTree.client.serverConnection.send_command("servergroupdelclient", { - sgid: group.id, - cldbid: client.properties.client_database_id - }).then(result => update_groups()); - }) - ).toggleClass("visible", - client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberRemovePower) || - client.clientId() == client.channelTree.client.getClientId() && client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_SELF_REMOVE_POWER).granted(group.requiredMemberRemovePower) - ) - )) - } + const node = container.find(".value a")[0]; + if(node) { + const update = () => { + node.innerText = format_time(client.calculateOnlineTime() * 1000, tr("0 Seconds")); }; - tag.find(".button-group-add").on('click', () => client.open_assignment_modal()); - - update_groups(); + callbacks.push(update); /* keep it in sync with all other updates. Else it looks wired */ + update(); } } - function apply_packets(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { - - /* Packet Loss */ - { - const container = tag.find(".statistic-packet-loss"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; - - if(node_downstream) { - callbacks.push(info => { - node_downstream.innerText = info.connection_server2client_packetloss_control < 0 ? tr("Not calculated") : (info.connection_server2client_packetloss_control || 0).toFixed(); - }); - node_downstream.innerText = tr("loading..."); - } - - if(node_upstream) { - callbacks.push(info => { - node_upstream.innerText = info.connection_client2server_packetloss_total < 0 ? tr("Not calculated") : (info.connection_client2server_packetloss_total || 0).toFixed(); - }); - node_upstream.innerText = tr("loading..."); - } - } - - /* Packets transmitted */ - { - const container = tag.find(".statistic-transmitted-packets"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; - - if(node_downstream) { - callbacks.push(info => { - let packets = 0; - packets += info.connection_packets_received_speech > 0 ? info.connection_packets_received_speech : 0; - packets += info.connection_packets_received_control > 0 ? info.connection_packets_received_control : 0; - packets += info.connection_packets_received_keepalive > 0 ? info.connection_packets_received_keepalive : 0; - if(packets == 0 && info.connection_packets_received_keepalive == -1) - node_downstream.innerText = tr("Not calculated"); - else - node_downstream.innerText = MessageHelper.format_number(packets, {unit: "Packets"}); - }); - node_downstream.innerText = tr("loading..."); - } - - if(node_upstream) { - callbacks.push(info => { - let packets = 0; - packets += info.connection_packets_sent_speech > 0 ? info.connection_packets_sent_speech : 0; - packets += info.connection_packets_sent_control > 0 ? info.connection_packets_sent_control : 0; - packets += info.connection_packets_sent_keepalive > 0 ? info.connection_packets_sent_keepalive : 0; - if(packets == 0 && info.connection_packets_sent_keepalive == -1) - node_upstream.innerText = tr("Not calculated"); - else - node_upstream.innerText = MessageHelper.format_number(packets, {unit: "Packets"}); - }); - node_upstream.innerText = tr("loading..."); - } - } - - /* Bytes transmitted */ - { - const container = tag.find(".statistic-transmitted-bytes"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; - - if(node_downstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bytes_received_speech > 0 ? info.connection_bytes_received_speech : 0; - bytes += info.connection_bytes_received_control > 0 ? info.connection_bytes_received_control : 0; - bytes += info.connection_bytes_received_keepalive > 0 ? info.connection_bytes_received_keepalive : 0; - if(bytes == 0 && info.connection_bytes_received_keepalive == -1) - node_downstream.innerText = tr("Not calculated"); - else - node_downstream.innerText = MessageHelper.network.format_bytes(bytes); - }); - node_downstream.innerText = tr("loading..."); - } - - if(node_upstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bytes_sent_speech > 0 ? info.connection_bytes_sent_speech : 0; - bytes += info.connection_bytes_sent_control > 0 ? info.connection_bytes_sent_control : 0; - bytes += info.connection_bytes_sent_keepalive > 0 ? info.connection_bytes_sent_keepalive : 0; - if(bytes == 0 && info.connection_bytes_sent_keepalive == -1) - node_upstream.innerText = tr("Not calculated"); - else - node_upstream.innerText = MessageHelper.network.format_bytes(bytes); - }); - node_upstream.innerText = tr("loading..."); - } - } - - /* Bandwidth second */ - { - const container = tag.find(".statistic-bandwidth-second"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; - - if(node_downstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bandwidth_received_last_second_speech > 0 ? info.connection_bandwidth_received_last_second_speech : 0; - bytes += info.connection_bandwidth_received_last_second_control > 0 ? info.connection_bandwidth_received_last_second_control : 0; - bytes += info.connection_bandwidth_received_last_second_keepalive > 0 ? info.connection_bandwidth_received_last_second_keepalive : 0; - if(bytes == 0 && info.connection_bandwidth_received_last_second_keepalive == -1) - node_downstream.innerText = tr("Not calculated"); - else - node_downstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"}); + /* Idle time */ + { + const container = tag.find(".property-idle-time"); + const node = container.find(".value a")[0]; + if(node) { + callbacks.push(info => { + node.innerText = format_time(info.connection_idle_time, tr("Currently active")); }); - node_downstream.innerText = tr("loading..."); - } + node.innerText = tr("loading..."); + } + } - if(node_upstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bandwidth_sent_last_second_speech > 0 ? info.connection_bandwidth_sent_last_second_speech : 0; - bytes += info.connection_bandwidth_sent_last_second_control > 0 ? info.connection_bandwidth_sent_last_second_control : 0; - bytes += info.connection_bandwidth_sent_last_second_keepalive > 0 ? info.connection_bandwidth_sent_last_second_keepalive : 0; - if(bytes == 0 && info.connection_bandwidth_sent_last_second_keepalive == -1) - node_upstream.innerText = tr("Not calculated"); - else - node_upstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"}); - }); - node_upstream.innerText = tr("loading..."); + /* ping */ + { + const container = tag.find(".property-ping"); + const node = container.find(".value a")[0]; + + if(node) { + callbacks.push(info => { + if(info.connection_ping >= 0) + node.innerText = info.connection_ping.toFixed(0) + "ms ± " + info.connection_ping_deviation.toFixed(2) + "ms"; + else if(info.connection_ping == -1 && info.connection_ping_deviation == -1) + node.innerText = tr("Not calculated"); + else + node.innerText = tr("loading..."); + }); + node.innerText = tr("loading..."); + } + } +} + +function apply_groups(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { + /* server groups */ + { + const container_entries = tag.find(".entries"); + const container_empty = tag.find(".container-default-groups"); + + const update_groups = () => { + container_entries.empty(); + container_empty.show(); + + for(const group_id of client.assignedServerGroupIds()) { + if(group_id == client.channelTree.server.properties.virtualserver_default_server_group) + continue; + + const group = client.channelTree.client.groups.serverGroup(group_id); + if(!group) continue; //This shall never happen! + + container_empty.hide(); + container_entries.append($.spawn("div").addClass("entry").append( + client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid), + $.spawn("a").addClass("name").text(group.name + " (" + group.id + ")"), + $.spawn("div").addClass("button-delete").append( + $.spawn("div").addClass("icon_em client-delete").attr("title", tr("Delete group")).on('click', event => { + client.channelTree.client.serverConnection.send_command("servergroupdelclient", { + sgid: group.id, + cldbid: client.properties.client_database_id + }).then(result => update_groups()); + }) + ).toggleClass("visible", + client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberRemovePower) || + client.clientId() == client.channelTree.client.getClientId() && client.channelTree.client.permissions.neededPermission(PermissionType.I_SERVER_GROUP_SELF_REMOVE_POWER).granted(group.requiredMemberRemovePower) + ) + )) } + }; + + tag.find(".button-group-add").on('click', () => client.open_assignment_modal()); + + update_groups(); + } +} + +function apply_packets(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { + + /* Packet Loss */ + { + const container = tag.find(".statistic-packet-loss"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + + if(node_downstream) { + callbacks.push(info => { + node_downstream.innerText = info.connection_server2client_packetloss_control < 0 ? tr("Not calculated") : (info.connection_server2client_packetloss_control || 0).toFixed(); + }); + node_downstream.innerText = tr("loading..."); } - /* Bandwidth minute */ - { - const container = tag.find(".statistic-bandwidth-minute"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; + if(node_upstream) { + callbacks.push(info => { + node_upstream.innerText = info.connection_client2server_packetloss_total < 0 ? tr("Not calculated") : (info.connection_client2server_packetloss_total || 0).toFixed(); + }); + node_upstream.innerText = tr("loading..."); + } + } - if(node_downstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bandwidth_received_last_minute_speech > 0 ? info.connection_bandwidth_received_last_minute_speech : 0; - bytes += info.connection_bandwidth_received_last_minute_control > 0 ? info.connection_bandwidth_received_last_minute_control : 0; - bytes += info.connection_bandwidth_received_last_minute_keepalive > 0 ? info.connection_bandwidth_received_last_minute_keepalive : 0; - if(bytes == 0 && info.connection_bandwidth_received_last_minute_keepalive == -1) - node_downstream.innerText = tr("Not calculated"); - else - node_downstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"}); - }); - node_downstream.innerText = tr("loading..."); - } + /* Packets transmitted */ + { + const container = tag.find(".statistic-transmitted-packets"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; - if(node_upstream) { - callbacks.push(info => { - let bytes = 0; - bytes += info.connection_bandwidth_sent_last_minute_speech > 0 ? info.connection_bandwidth_sent_last_minute_speech : 0; - bytes += info.connection_bandwidth_sent_last_minute_control > 0 ? info.connection_bandwidth_sent_last_minute_control : 0; - bytes += info.connection_bandwidth_sent_last_minute_keepalive > 0 ? info.connection_bandwidth_sent_last_minute_keepalive : 0; - if(bytes == 0 && info.connection_bandwidth_sent_last_minute_keepalive == -1) - node_upstream.innerText = tr("Not calculated"); - else - node_upstream.innerText = MessageHelper.network.format_bytes(bytes, {time: "s"}); - }); - node_upstream.innerText = tr("loading..."); - } + if(node_downstream) { + callbacks.push(info => { + let packets = 0; + packets += info.connection_packets_received_speech > 0 ? info.connection_packets_received_speech : 0; + packets += info.connection_packets_received_control > 0 ? info.connection_packets_received_control : 0; + packets += info.connection_packets_received_keepalive > 0 ? info.connection_packets_received_keepalive : 0; + if(packets == 0 && info.connection_packets_received_keepalive == -1) + node_downstream.innerText = tr("Not calculated"); + else + node_downstream.innerText = format_number(packets, {unit: "Packets"}); + }); + node_downstream.innerText = tr("loading..."); } - /* quota */ - { - const container = tag.find(".statistic-quota"); - const node_downstream = container.find(".downstream .value")[0]; - const node_upstream = container.find(".upstream .value")[0]; + if(node_upstream) { + callbacks.push(info => { + let packets = 0; + packets += info.connection_packets_sent_speech > 0 ? info.connection_packets_sent_speech : 0; + packets += info.connection_packets_sent_control > 0 ? info.connection_packets_sent_control : 0; + packets += info.connection_packets_sent_keepalive > 0 ? info.connection_packets_sent_keepalive : 0; + if(packets == 0 && info.connection_packets_sent_keepalive == -1) + node_upstream.innerText = tr("Not calculated"); + else + node_upstream.innerText = format_number(packets, {unit: "Packets"}); + }); + node_upstream.innerText = tr("loading..."); + } + } - if(node_downstream) { - client.updateClientVariables().then(info => { - //TODO: Test for own client info and if so then show the max quota (needed permission) - node_downstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_downloaded, {exact: false}); - }); - node_downstream.innerText = tr("loading..."); - } + /* Bytes transmitted */ + { + const container = tag.find(".statistic-transmitted-bytes"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; - if(node_upstream) { - client.updateClientVariables().then(info => { - //TODO: Test for own client info and if so then show the max quota (needed permission) - node_upstream.innerText = MessageHelper.network.format_bytes(client.properties.client_month_bytes_uploaded, {exact: false}); - }); - node_upstream.innerText = tr("loading..."); - } + if(node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bytes_received_speech > 0 ? info.connection_bytes_received_speech : 0; + bytes += info.connection_bytes_received_control > 0 ? info.connection_bytes_received_control : 0; + bytes += info.connection_bytes_received_keepalive > 0 ? info.connection_bytes_received_keepalive : 0; + if(bytes == 0 && info.connection_bytes_received_keepalive == -1) + node_downstream.innerText = tr("Not calculated"); + else + node_downstream.innerText = network.format_bytes(bytes); + }); + node_downstream.innerText = tr("loading..."); + } + + if(node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bytes_sent_speech > 0 ? info.connection_bytes_sent_speech : 0; + bytes += info.connection_bytes_sent_control > 0 ? info.connection_bytes_sent_control : 0; + bytes += info.connection_bytes_sent_keepalive > 0 ? info.connection_bytes_sent_keepalive : 0; + if(bytes == 0 && info.connection_bytes_sent_keepalive == -1) + node_upstream.innerText = tr("Not calculated"); + else + node_upstream.innerText = network.format_bytes(bytes); + }); + node_upstream.innerText = tr("loading..."); + } + } + + /* Bandwidth second */ + { + const container = tag.find(".statistic-bandwidth-second"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + + if(node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_received_last_second_speech > 0 ? info.connection_bandwidth_received_last_second_speech : 0; + bytes += info.connection_bandwidth_received_last_second_control > 0 ? info.connection_bandwidth_received_last_second_control : 0; + bytes += info.connection_bandwidth_received_last_second_keepalive > 0 ? info.connection_bandwidth_received_last_second_keepalive : 0; + if(bytes == 0 && info.connection_bandwidth_received_last_second_keepalive == -1) + node_downstream.innerText = tr("Not calculated"); + else + node_downstream.innerText = network.format_bytes(bytes, {time: "s"}); + }); + node_downstream.innerText = tr("loading..."); + } + + if(node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_sent_last_second_speech > 0 ? info.connection_bandwidth_sent_last_second_speech : 0; + bytes += info.connection_bandwidth_sent_last_second_control > 0 ? info.connection_bandwidth_sent_last_second_control : 0; + bytes += info.connection_bandwidth_sent_last_second_keepalive > 0 ? info.connection_bandwidth_sent_last_second_keepalive : 0; + if(bytes == 0 && info.connection_bandwidth_sent_last_second_keepalive == -1) + node_upstream.innerText = tr("Not calculated"); + else + node_upstream.innerText = network.format_bytes(bytes, {time: "s"}); + }); + node_upstream.innerText = tr("loading..."); + } + } + + /* Bandwidth minute */ + { + const container = tag.find(".statistic-bandwidth-minute"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + + if(node_downstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_received_last_minute_speech > 0 ? info.connection_bandwidth_received_last_minute_speech : 0; + bytes += info.connection_bandwidth_received_last_minute_control > 0 ? info.connection_bandwidth_received_last_minute_control : 0; + bytes += info.connection_bandwidth_received_last_minute_keepalive > 0 ? info.connection_bandwidth_received_last_minute_keepalive : 0; + if(bytes == 0 && info.connection_bandwidth_received_last_minute_keepalive == -1) + node_downstream.innerText = tr("Not calculated"); + else + node_downstream.innerText = network.format_bytes(bytes, {time: "s"}); + }); + node_downstream.innerText = tr("loading..."); + } + + if(node_upstream) { + callbacks.push(info => { + let bytes = 0; + bytes += info.connection_bandwidth_sent_last_minute_speech > 0 ? info.connection_bandwidth_sent_last_minute_speech : 0; + bytes += info.connection_bandwidth_sent_last_minute_control > 0 ? info.connection_bandwidth_sent_last_minute_control : 0; + bytes += info.connection_bandwidth_sent_last_minute_keepalive > 0 ? info.connection_bandwidth_sent_last_minute_keepalive : 0; + if(bytes == 0 && info.connection_bandwidth_sent_last_minute_keepalive == -1) + node_upstream.innerText = tr("Not calculated"); + else + node_upstream.innerText = network.format_bytes(bytes, {time: "s"}); + }); + node_upstream.innerText = tr("loading..."); + } + } + + /* quota */ + { + const container = tag.find(".statistic-quota"); + const node_downstream = container.find(".downstream .value")[0]; + const node_upstream = container.find(".upstream .value")[0]; + + if(node_downstream) { + client.updateClientVariables().then(info => { + //TODO: Test for own client info and if so then show the max quota (needed permission) + node_downstream.innerText = network.format_bytes(client.properties.client_month_bytes_downloaded, {exact: false}); + }); + node_downstream.innerText = tr("loading..."); + } + + if(node_upstream) { + client.updateClientVariables().then(info => { + //TODO: Test for own client info and if so then show the max quota (needed permission) + node_upstream.innerText = network.format_bytes(client.properties.client_month_bytes_uploaded, {exact: false}); + }); + node_upstream.innerText = tr("loading..."); } } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalConnect.ts b/shared/js/ui/modal/ModalConnect.ts index cefdf403..ba5f9ff1 100644 --- a/shared/js/ui/modal/ModalConnect.ts +++ b/shared/js/ui/modal/ModalConnect.ts @@ -1,7 +1,17 @@ -/// +import {Settings, settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as loader from "tc-loader"; +import {createModal} from "tc-shared/ui/elements/Modal"; +import {ConnectionProfile, default_profile, find_profile, profiles} from "tc-shared/profiles/ConnectionProfile"; +import {KeyCode} from "tc-shared/PPTListener"; +import {IconManager} from "tc-shared/FileManager"; +import * as i18nc from "tc-shared/i18n/country"; +import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; //FIXME: Move this shit out of this file! -namespace connection_log { +export namespace connection_log { //TODO: Save password data export type ConnectionData = { name: string; @@ -91,168 +101,150 @@ namespace connection_log { }); } -namespace Modals { - export function spawnConnectModal(options: { - default_connect_new_tab?: boolean /* default false */ - }, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: profiles.ConnectionProfile, enforce: boolean}) { - let selected_profile: profiles.ConnectionProfile; +declare const native_client; +export function spawnConnectModal(options: { + default_connect_new_tab?: boolean /* default false */ +}, defaultHost: { url: string, enforce: boolean} = { url: "ts.TeaSpeak.de", enforce: false}, connect_profile?: { profile: ConnectionProfile, enforce: boolean}) { + let selected_profile: ConnectionProfile; - const random_id = (() => { - const array = new Uint32Array(10); - window.crypto.getRandomValues(array); - return array.join(""); - })(); + const random_id = (() => { + const array = new Uint32Array(10); + window.crypto.getRandomValues(array); + return array.join(""); + })(); - const modal = createModal({ - header: tr("Connect to a server"), - body: $("#tmpl_connect").renderTag({ - client: native_client, - forum_path: settings.static("forum_path"), - password_id: random_id, - multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), - default_connect_new_tab: typeof(options.default_connect_new_tab) === "boolean" && options.default_connect_new_tab - }), - footer: () => undefined, - min_width: "28em" + const modal = createModal({ + header: tr("Connect to a server"), + body: $("#tmpl_connect").renderTag({ + client: native_client, + forum_path: settings.static("forum_path"), + password_id: random_id, + multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), + default_connect_new_tab: typeof(options.default_connect_new_tab) === "boolean" && options.default_connect_new_tab + }), + footer: () => undefined, + min_width: "28em" + }); + + modal.htmlTag.find(".modal-body").addClass("modal-connect"); + + /* server list toggle */ + { + const container_last_servers = modal.htmlTag.find(".container-last-servers"); + const button = modal.htmlTag.find(".button-toggle-last-servers"); + const set_show = shown => { + container_last_servers.toggleClass('shown', shown); + button.find(".arrow").toggleClass('down', shown).toggleClass('up', !shown); + settings.changeGlobal("connect_show_last_servers", shown); + }; + button.on('click', event => { + set_show(!container_last_servers.hasClass("shown")); }); + set_show(settings.static_global("connect_show_last_servers", false)); + } - modal.htmlTag.find(".modal-body").addClass("modal-connect"); + const apply = (header, body, footer) => { + const container_last_server_body = modal.htmlTag.find(".container-last-servers .table .body"); + const container_empty = container_last_server_body.find(".body-empty"); + let current_connect_data: connection_log.ConnectionEntry; - /* server list toggle */ - { - const container_last_servers = modal.htmlTag.find(".container-last-servers"); - const button = modal.htmlTag.find(".button-toggle-last-servers"); - const set_show = shown => { - container_last_servers.toggleClass('shown', shown); - button.find(".arrow").toggleClass('down', shown).toggleClass('up', !shown); - settings.changeGlobal("connect_show_last_servers", shown); - }; - button.on('click', event => { - set_show(!container_last_servers.hasClass("shown")); - }); - set_show(settings.static_global("connect_show_last_servers", false)); - } + const button_connect = footer.find(".button-connect"); + const button_connect_tab = footer.find(".button-connect-new-tab"); + const button_manage = body.find(".button-manage-profiles"); - const apply = (header, body, footer) => { - const container_last_server_body = modal.htmlTag.find(".container-last-servers .table .body"); - const container_empty = container_last_server_body.find(".body-empty"); - let current_connect_data: connection_log.ConnectionEntry; + const input_profile = body.find(".container-select-profile select"); + const input_address = body.find(".container-address input"); + const input_nickname = body.find(".container-nickname input"); + const input_password = body.find(".container-password input"); - const button_connect = footer.find(".button-connect"); - const button_connect_tab = footer.find(".button-connect-new-tab"); - const button_manage = body.find(".button-manage-profiles"); - - const input_profile = body.find(".container-select-profile select"); - const input_address = body.find(".container-address input"); - const input_nickname = body.find(".container-nickname input"); - const input_password = body.find(".container-password input"); - - let updateFields = (reset_current_data: boolean) => { - if(reset_current_data) { - current_connect_data = undefined; - container_last_server_body.find(".selected").removeClass("selected"); - } - - let address = input_address.val().toString(); - settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address); - let flag_address = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN); - - let nickname = input_nickname.val().toString(); - if(nickname) - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname); - else - nickname = input_nickname.attr("placeholder") || ""; - let flag_nickname = nickname.length >= 3 && nickname.length <= 32; - - input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address); - input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname); - - const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid(); - button_connect.prop("disabled", flag_disabled); - button_connect_tab.prop("disabled", flag_disabled); - }; - - input_address.val(defaultHost.enforce ? defaultHost.url : settings.static_global(Settings.KEY_CONNECT_ADDRESS, defaultHost.url)); - input_address - .on("keyup", () => updateFields(true)) - .on('keydown', event => { - if(event.keyCode == KeyCode.KEY_ENTER && !event.shiftKey) - button_connect.trigger('click'); - }); - button_manage.on('click', event => { - const modal = Modals.spawnSettingsModal("identity-profiles"); - modal.close_listener.push(() => { - input_profile.trigger('change'); - }); - return true; - }); - - /* Connect Profiles */ - { - for(const profile of profiles.profiles()) { - input_profile.append( - $.spawn("option").text(profile.profile_name).val(profile.id) - ); - } - - input_profile.on('change', event => { - selected_profile = profiles.find_profile(input_profile.val() as string) || profiles.default_profile(); - { - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined); - input_nickname - .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user") - .val(""); - } - - settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, selected_profile.id); - input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid()); - updateFields(true); - }); - input_profile.val(connect_profile && connect_profile.profile ? - connect_profile.profile.id : - settings.static_global(Settings.KEY_CONNECT_PROFILE, "default") - ).trigger('change'); + let updateFields = (reset_current_data: boolean) => { + if(reset_current_data) { + current_connect_data = undefined; + container_last_server_body.find(".selected").removeClass("selected"); } - const last_nickname = settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined); - if(last_nickname) /* restore */ - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, last_nickname); + let address = input_address.val().toString(); + settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address); + let flag_address = !!address.match(Regex.IP_V4) || !!address.match(Regex.IP_V6) || !!address.match(Regex.DOMAIN); - input_nickname.val(last_nickname); - input_nickname.on("keyup", () => updateFields(true)); - setTimeout(() => updateFields(false), 100); + let nickname = input_nickname.val().toString(); + if(nickname) + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname); + else + nickname = input_nickname.attr("placeholder") || ""; + let flag_nickname = nickname.length >= 3 && nickname.length <= 32; - const server_address = () => { - let address = input_address.val().toString(); - if(address.match(Regex.IP_V6) && !address.startsWith("[")) - return "[" + address + "]"; - return address; - }; - button_connect.on('click', event => { - modal.close(); + input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address); + input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname); - const connection = server_connections.active_connection_handler(); - if(connection) { - connection.startConnection( - current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), - selected_profile, - true, - { - nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), - password: (current_connect_data && current_connect_data.password_hash) ? {password: current_connect_data.password_hash, hashed: true} : {password: input_password.val().toString(), hashed: false} - } - ); - } else { - button_connect_tab.trigger('click'); - } + const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid(); + button_connect.prop("disabled", flag_disabled); + button_connect_tab.prop("disabled", flag_disabled); + }; + + input_address.val(defaultHost.enforce ? defaultHost.url : settings.static_global(Settings.KEY_CONNECT_ADDRESS, defaultHost.url)); + input_address + .on("keyup", () => updateFields(true)) + .on('keydown', event => { + if(event.keyCode == KeyCode.KEY_ENTER && !event.shiftKey) + button_connect.trigger('click'); }); - button_connect_tab.on('click', event => { - modal.close(); + button_manage.on('click', event => { + const modal = spawnSettingsModal("identity-profiles"); + modal.close_listener.push(() => { + input_profile.trigger('change'); + }); + return true; + }); - const connection = server_connections.spawn_server_connection_handler(); - server_connections.set_active_connection_handler(connection); + /* Connect Profiles */ + { + for(const profile of profiles()) { + input_profile.append( + $.spawn("option").text(profile.profile_name).val(profile.id) + ); + } + + input_profile.on('change', event => { + selected_profile = find_profile(input_profile.val() as string) || default_profile(); + { + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined); + input_nickname + .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user") + .val(""); + } + + settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, selected_profile.id); + input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid()); + updateFields(true); + }); + input_profile.val(connect_profile && connect_profile.profile ? + connect_profile.profile.id : + settings.static_global(Settings.KEY_CONNECT_PROFILE, "default") + ).trigger('change'); + } + + const last_nickname = settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined); + if(last_nickname) /* restore */ + settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, last_nickname); + + input_nickname.val(last_nickname); + input_nickname.on("keyup", () => updateFields(true)); + setTimeout(() => updateFields(false), 100); + + const server_address = () => { + let address = input_address.val().toString(); + if(address.match(Regex.IP_V6) && !address.startsWith("[")) + return "[" + address + "]"; + return address; + }; + button_connect.on('click', event => { + modal.close(); + + const connection = server_connections.active_connection_handler(); + if(connection) { connection.startConnection( - current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), + current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { @@ -260,72 +252,89 @@ namespace Modals { password: (current_connect_data && current_connect_data.password_hash) ? {password: current_connect_data.password_hash, hashed: true} : {password: input_password.val().toString(), hashed: false} } ); - }); - - - /* connect history show */ - { - for(const entry of connection_log.history().slice(0, 10)) { - $.spawn("div").addClass("row").append( - $.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { - event.preventDefault(); - - const row = $(event.target).parents('.row'); - row.hide(250, () => { - row.detach(); - }); - connection_log.delete_entry(entry.address); - container_empty.toggle(container_last_server_body.children().length > 1); - }) - ).append( - $.spawn("div").addClass("column name").append([ - IconManager.generate_tag(IconManager.load_cached_icon(entry.icon_id)), - $.spawn("a").text(entry.name) - ]) - ).append( - $.spawn("div").addClass("column address").text(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")) - ).append( - $.spawn("div").addClass("column password").text(entry.flag_password ? tr("Yes") : tr("No")) - ).append( - $.spawn("div").addClass("column country-name").append([ - $.spawn("div").addClass("country flag-" + entry.country.toLowerCase()), - $.spawn("a").text(i18n.country_name(entry.country, tr("Global"))) - ]) - ).append( - $.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total) - ).append( - $.spawn("div").addClass("column connections").text(entry.total_connection + "") - ).on('click', event => { - if(event.isDefaultPrevented()) - return; - - event.preventDefault(); - current_connect_data = entry; - container_last_server_body.find(".selected").removeClass("selected"); - $(event.target).parent('.row').addClass('selected'); - - input_address.val(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")); - input_password.val(entry.flag_password && entry.password_hash ? "WolverinDEV Yeahr!" : "").trigger('change'); - }).on('dblclick', event => { - current_connect_data = entry; - button_connect.trigger('click'); - }).appendTo(container_last_server_body); - container_empty.toggle(false); - } + } else { + button_connect_tab.trigger('click'); } - }; - apply(modal.htmlTag, modal.htmlTag, modal.htmlTag); + }); + button_connect_tab.on('click', event => { + modal.close(); - modal.open(); - return; - } + const connection = server_connections.spawn_server_connection_handler(); + server_connections.set_active_connection_handler(connection); + connection.startConnection( + current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), + selected_profile, + true, + { + nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), + password: (current_connect_data && current_connect_data.password_hash) ? {password: current_connect_data.password_hash, hashed: true} : {password: input_password.val().toString(), hashed: false} + } + ); + }); - export const Regex = { - //DOMAIN<:port> - DOMAIN: /^(localhost|((([a-zA-Z0-9_-]{0,63}\.){0,253})?[a-zA-Z0-9_-]{0,63}\.[a-zA-Z]{2,64}))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,46}))$/, - //IP<:port> - IP_V4: /(^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4}))$/, - IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, - IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, + + /* connect history show */ + { + for(const entry of connection_log.history().slice(0, 10)) { + $.spawn("div").addClass("row").append( + $.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { + event.preventDefault(); + + const row = $(event.target).parents('.row'); + row.hide(250, () => { + row.detach(); + }); + connection_log.delete_entry(entry.address); + container_empty.toggle(container_last_server_body.children().length > 1); + }) + ).append( + $.spawn("div").addClass("column name").append([ + IconManager.generate_tag(IconManager.load_cached_icon(entry.icon_id)), + $.spawn("a").text(entry.name) + ]) + ).append( + $.spawn("div").addClass("column address").text(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")) + ).append( + $.spawn("div").addClass("column password").text(entry.flag_password ? tr("Yes") : tr("No")) + ).append( + $.spawn("div").addClass("column country-name").append([ + $.spawn("div").addClass("country flag-" + entry.country.toLowerCase()), + $.spawn("a").text(i18nc.country_name(entry.country, tr("Global"))) + ]) + ).append( + $.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total) + ).append( + $.spawn("div").addClass("column connections").text(entry.total_connection + "") + ).on('click', event => { + if(event.isDefaultPrevented()) + return; + + event.preventDefault(); + current_connect_data = entry; + container_last_server_body.find(".selected").removeClass("selected"); + $(event.target).parent('.row').addClass('selected'); + + input_address.val(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")); + input_password.val(entry.flag_password && entry.password_hash ? "WolverinDEV Yeahr!" : "").trigger('change'); + }).on('dblclick', event => { + current_connect_data = entry; + button_connect.trigger('click'); + }).appendTo(container_last_server_body); + container_empty.toggle(false); + } + } }; -} \ No newline at end of file + apply(modal.htmlTag, modal.htmlTag, modal.htmlTag); + + modal.open(); + return; +} + +export const Regex = { + //DOMAIN<:port> + DOMAIN: /^(localhost|((([a-zA-Z0-9_-]{0,63}\.){0,253})?[a-zA-Z0-9_-]{0,63}\.[a-zA-Z]{2,64}))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,46}))$/, + //IP<:port> + IP_V4: /(^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4}))$/, + IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, + IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, +}; \ No newline at end of file diff --git a/shared/js/ui/modal/ModalCreateChannel.ts b/shared/js/ui/modal/ModalCreateChannel.ts index 5f7be3bc..a5be35d3 100644 --- a/shared/js/ui/modal/ModalCreateChannel.ts +++ b/shared/js/ui/modal/ModalCreateChannel.ts @@ -1,711 +1,720 @@ -/// +import PermissionType from "tc-shared/permission/PermissionType"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {ChannelEntry, ChannelProperties} from "tc-shared/ui/channel"; +import {PermissionManager, PermissionValue} from "tc-shared/permission/PermissionManager"; +import {LogCategory} from "tc-shared/log"; +import {createModal} from "tc-shared/ui/elements/Modal"; +import * as log from "tc-shared/log"; +import {Settings, settings} from "tc-shared/settings"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect"; +import {hashPassword} from "tc-shared/utils/helpers"; +import {sliderfy} from "tc-shared/ui/elements/Slider"; -namespace Modals { - export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) { - let properties: ChannelProperties = { } as ChannelProperties; //The changes properties - const modal = createModal({ - header: channel ? tr("Edit channel") : tr("Create channel"), - body: () => { - const render_properties = {}; - Object.assign(render_properties, channel ? channel.properties : { - channel_flag_maxfamilyclients_unlimited: true, - channel_flag_maxclients_unlimited: true, - }); - render_properties["channel_icon_tab"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); - render_properties["channel_icon_general"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); - render_properties["create"] = !channel; - - let template = $("#tmpl_channel_edit").renderTag(render_properties); - - /* the tab functionality */ - { - const container_tabs = template.find(".container-advanced"); - container_tabs.find(".categories .entry").on('click', event => { - const entry = $(event.target); - - container_tabs.find(".bodies > .body").addClass("hidden"); - container_tabs.find(".categories > .selected").removeClass("selected"); - - entry.addClass("selected"); - container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); - }); - - container_tabs.find(".entry").first().trigger('click'); - } - - /* Advanced/normal switch */ - { - const input = template.find(".input-advanced-mode"); - const container_mode = template.find(".mode-container"); - const container_advanced = container_mode.find(".container-advanced"); - const container_simple = container_mode.find(".container-simple"); - input.on('change', event => { - const advanced = input.prop("checked"); - settings.changeGlobal(Settings.KEY_CHANNEL_EDIT_ADVANCED, advanced); - - container_mode.css("overflow", "hidden"); - container_advanced.show().toggleClass("hidden", !advanced); - container_simple.show().toggleClass("hidden", advanced); - - setTimeout(() => { - container_advanced.toggle(advanced); - container_simple.toggle(!advanced); - container_mode.css("overflow", "visible"); - }, 300); - }).prop("checked", settings.static_global(Settings.KEY_CHANNEL_EDIT_ADVANCED)).trigger('change'); - } - - return template.tabify().children(); /* the "render" div */ - }, - footer: null, - width: 500 - }); - modal.htmlTag.find(".modal-body").addClass("modal-channel modal-blue"); - - - applyGeneralListener(connection, properties, modal.htmlTag.find(".container-general"), modal.htmlTag.find(".button_ok"), channel); - applyStandardListener(connection, properties, modal.htmlTag.find(".container-standard"), modal.htmlTag.find(".container-simple"), parent, channel); - applyPermissionListener(connection, properties, modal.htmlTag.find(".container-permissions"), modal.htmlTag.find(".button_ok"), permissions, channel); - applyAudioListener(connection, properties, modal.htmlTag.find(".container-audio"), modal.htmlTag.find(".container-simple"), channel); - applyAdvancedListener(connection, properties, modal.htmlTag.find(".container-misc"), modal.htmlTag.find(".button_ok"), channel); - - let updated: PermissionValue[] = []; - modal.htmlTag.find(".button_ok").click(() => { - modal.htmlTag.find(".container-permissions").find("input[permission]").each((index, _element) => { - let element = $(_element); - if(element.val() == element.attr("original-value")) return; - let permission = permissions.resolveInfo(element.attr("permission")); - if(!permission) { - log.error(LogCategory.PERMISSIONS, tr("Failed to resolve channel permission for name %o"), element.attr("permission")); - element.prop("disabled", true); - return; - } - - updated.push(new PermissionValue(permission, element.val())); +export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) { + let properties: ChannelProperties = { } as ChannelProperties; //The changes properties + const modal = createModal({ + header: channel ? tr("Edit channel") : tr("Create channel"), + body: () => { + const render_properties = {}; + Object.assign(render_properties, channel ? channel.properties : { + channel_flag_maxfamilyclients_unlimited: true, + channel_flag_maxclients_unlimited: true, }); - console.log(tr("Updated permissions %o"), updated); - }).click(() => { - modal.close(); - for(const key of Object.keys(channel ? channel.properties : {})) - if(channel.properties[key] == properties[key]) - delete properties[key]; - callback(properties, updated); //First may create the channel - }); + render_properties["channel_icon_tab"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); + render_properties["channel_icon_general"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0); + render_properties["create"] = !channel; - tooltip(modal.htmlTag); - modal.htmlTag.find(".button_cancel").click(() => { - modal.close(); - callback(); - }); + let template = $("#tmpl_channel_edit").renderTag(render_properties); - modal.open(); - if(!channel) - modal.htmlTag.find(".channel_name").focus(); + /* the tab functionality */ + { + const container_tabs = template.find(".container-advanced"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); + + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); + + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); + + container_tabs.find(".entry").first().trigger('click'); + } + + /* Advanced/normal switch */ + { + const input = template.find(".input-advanced-mode"); + const container_mode = template.find(".mode-container"); + const container_advanced = container_mode.find(".container-advanced"); + const container_simple = container_mode.find(".container-simple"); + input.on('change', event => { + const advanced = input.prop("checked"); + settings.changeGlobal(Settings.KEY_CHANNEL_EDIT_ADVANCED, advanced); + + container_mode.css("overflow", "hidden"); + container_advanced.show().toggleClass("hidden", !advanced); + container_simple.show().toggleClass("hidden", advanced); + + setTimeout(() => { + container_advanced.toggle(advanced); + container_simple.toggle(!advanced); + container_mode.css("overflow", "visible"); + }, 300); + }).prop("checked", settings.static_global(Settings.KEY_CHANNEL_EDIT_ADVANCED)).trigger('change'); + } + + return template.tabify().children(); /* the "render" div */ + }, + footer: null, + width: 500 + }); + modal.htmlTag.find(".modal-body").addClass("modal-channel modal-blue"); + + + applyGeneralListener(connection, properties, modal.htmlTag.find(".container-general"), modal.htmlTag.find(".button_ok"), channel); + applyStandardListener(connection, properties, modal.htmlTag.find(".container-standard"), modal.htmlTag.find(".container-simple"), parent, channel); + applyPermissionListener(connection, properties, modal.htmlTag.find(".container-permissions"), modal.htmlTag.find(".button_ok"), permissions, channel); + applyAudioListener(connection, properties, modal.htmlTag.find(".container-audio"), modal.htmlTag.find(".container-simple"), channel); + applyAdvancedListener(connection, properties, modal.htmlTag.find(".container-misc"), modal.htmlTag.find(".button_ok"), channel); + + let updated: PermissionValue[] = []; + modal.htmlTag.find(".button_ok").click(() => { + modal.htmlTag.find(".container-permissions").find("input[permission]").each((index, _element) => { + let element = $(_element); + if(element.val() == element.attr("original-value")) return; + let permission = permissions.resolveInfo(element.attr("permission")); + if(!permission) { + log.error(LogCategory.PERMISSIONS, tr("Failed to resolve channel permission for name %o"), element.attr("permission")); + element.prop("disabled", true); + return; + } + + updated.push(new PermissionValue(permission, element.val())); + }); + console.log(tr("Updated permissions %o"), updated); + }).click(() => { + modal.close(); + for(const key of Object.keys(channel ? channel.properties : {})) + if(channel.properties[key] == properties[key]) + delete properties[key]; + callback(properties, updated); //First may create the channel + }); + + tooltip.initialize(modal.htmlTag); + modal.htmlTag.find(".button_cancel").click(() => { + modal.close(); + callback(); + }); + + modal.open(); + if(!channel) + modal.htmlTag.find(".channel_name").focus(); +} + +function applyGeneralListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel: ChannelEntry | undefined) { + let updateButton = () => { + const status = tag.find(".input_error").length != 0; + console.log("Disabled: %o", status); + button.prop("disabled", status); + }; + + { + const channel_name = tag.find(".channel_name"); + tag.find(".channel_name").on('change keyup', function (this: HTMLInputElement) { + properties.channel_name = this.value; + + channel_name.toggleClass("input_error", this.value.length < 1 || this.value.length > 40); + updateButton(); + }).prop("disabled", channel && !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1)); } - function applyGeneralListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel: ChannelEntry | undefined) { - let updateButton = () => { - const status = tag.find(".input_error").length != 0; - console.log("Disabled: %o", status); - button.prop("disabled", status); - }; - - { - const channel_name = tag.find(".channel_name"); - tag.find(".channel_name").on('change keyup', function (this: HTMLInputElement) { - properties.channel_name = this.value; - - channel_name.toggleClass("input_error", this.value.length < 1 || this.value.length > 40); - updateButton(); - }).prop("disabled", channel && !connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1)); - } - - tag.find(".button-select-icon").on('click', event => { - Modals.spawnIconSelect(connection, id => { - const icon_node = tag.find(".icon-preview"); - icon_node.children().remove(); - icon_node.append(connection.fileManager.icons.generateTag(id)); - - console.log("Selected icon ID: %d", id); - properties.channel_icon_id = id; - }, channel ? channel.properties.channel_icon_id : 0); - }); - - tag.find(".button-icon-remove").on('click', event => { + tag.find(".button-select-icon").on('click', event => { + spawnIconSelect(connection, id => { const icon_node = tag.find(".icon-preview"); icon_node.children().remove(); - icon_node.append(connection.fileManager.icons.generateTag(0)); + icon_node.append(connection.fileManager.icons.generateTag(id)); - console.log("Remove channel icon"); - properties.channel_icon_id = 0; - }); + console.log("Selected icon ID: %d", id); + properties.channel_icon_id = id; + }, channel ? channel.properties.channel_icon_id : 0); + }); - { - const channel_password = tag.find(".channel_password"); - tag.find(".channel_password").change(function (this: HTMLInputElement) { - properties.channel_flag_password = this.value.length != 0; - if(properties.channel_flag_password) - helpers.hashPassword(this.value).then(pass => properties.channel_password = pass); + tag.find(".button-icon-remove").on('click', event => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(connection.fileManager.icons.generateTag(0)); - channel_password.removeClass("input_error"); - if(!properties.channel_flag_password) - if(connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1)) - channel_password.addClass("input_error"); - updateButton(); - }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD : PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1)); - } + console.log("Remove channel icon"); + properties.channel_icon_id = 0; + }); - tag.find(".channel_topic").change(function (this: HTMLInputElement) { - properties.channel_topic = this.value; - }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_TOPIC : PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1)); + { + const channel_password = tag.find(".channel_password"); + tag.find(".channel_password").change(function (this: HTMLInputElement) { + properties.channel_flag_password = this.value.length != 0; + if(properties.channel_flag_password) + hashPassword(this.value).then(pass => properties.channel_password = pass); - { - const container = tag.find(".container-description"); - const input = container.find("textarea"); - - const insert_tag = (open: string, close: string) => { - if(input.prop("disabled")) - return; - - const node = input[0] as HTMLTextAreaElement; - if (node.selectionStart || node.selectionStart == 0) { - const startPos = node.selectionStart; - const endPos = node.selectionEnd; - node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); - node.selectionEnd = endPos + open.length; - node.selectionStart = node.selectionEnd; - } else { - node.value += open + close; - node.selectionEnd = node.value.length - close.length; - node.selectionStart = node.selectionEnd; - } - - input.focus().trigger('change'); - }; - - input.on('change', event => { - console.log(tr("Channel description edited: %o"), input.val()); - properties.channel_description = input.val() as string; - }); - - container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); - container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); - container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); - container.find(".button-color input").on('change', event => { - insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') - }) - } - tag.find(".channel_description").change(function (this: HTMLInputElement) { - properties.channel_description = this.value; - }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION : PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1)); - - if(!channel) { - setTimeout(() => { - tag.find(".channel_name").trigger("change"); - tag.find(".channel_password").trigger('change'); - }, 0); - } + channel_password.removeClass("input_error"); + if(!properties.channel_flag_password) + if(connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1)) + channel_password.addClass("input_error"); + updateButton(); + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD : PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1)); } - function applyStandardListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, simple: JQuery, parent: ChannelEntry, channel: ChannelEntry) { - /* Channel type */ - { - const input_advanced_type = tag.find("input[name='channel_type']"); + tag.find(".channel_topic").change(function (this: HTMLInputElement) { + properties.channel_topic = this.value; + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_TOPIC : PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1)); - let _in_update = false; - const update_simple_type = () => { - if(_in_update) - return; + { + const container = tag.find(".container-description"); + const input = container.find("textarea"); - let type; - if(properties.channel_flag_default || (typeof(properties.channel_flag_default) === "undefined" && channel && channel.properties.channel_flag_default)) - type = "def"; - else if(properties.channel_flag_permanent || (typeof(properties.channel_flag_permanent) === "undefined" && channel && channel.properties.channel_flag_permanent)) - type = "perm"; - else if(properties.channel_flag_semi_permanent || (typeof(properties.channel_flag_semi_permanent) === "undefined" && channel && channel.properties.channel_flag_semi_permanent)) - type = "semi"; - else - type = "temp"; + const insert_tag = (open: string, close: string) => { + if(input.prop("disabled")) + return; - simple.find("option[name='channel-type'][value='" + type + "']").prop("selected", true); - }; - - input_advanced_type.on('change', event => { - const value = [...input_advanced_type as JQuery].find(e => e.checked).value; - switch(value) { - case "semi": - properties.channel_flag_permanent = false; - properties.channel_flag_semi_permanent = true; - break; - case "perm": - properties.channel_flag_permanent = true; - properties.channel_flag_semi_permanent = false; - break; - default: - properties.channel_flag_permanent = false; - properties.channel_flag_semi_permanent = false; - break; - } - update_simple_type(); - }); - - const permission_temp = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_TEMPORARY : PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1); - const permission_semi = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1); - const permission_perm = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1); - const permission_default = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) && - connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT : PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1); - - /* advanced type listeners */ - const container_types = tag.find(".container-channel-type"); - const tag_type_temp = container_types.find(".type-temp"); - const tag_type_semi = container_types.find(".type-semi"); - const tag_type_perm = container_types.find(".type-perm"); - const select_default = tag.find(".input-flag-default"); - - { - select_default.on('change', event => { - const node = select_default[0] as HTMLInputElement; - properties.channel_flag_default = node.checked; - - if(node.checked) - tag_type_perm.find("input").prop("checked", true); - - tag_type_temp - .toggleClass("disabled", node.checked || !permission_temp) - .find("input").prop("disabled", node.checked || !permission_temp); - - tag_type_semi - .toggleClass("disabled", node.checked || !permission_semi) - .find("input").prop("disabled", node.checked || !permission_semi); - - tag_type_perm - .toggleClass("disabled", node.checked || !permission_perm) - .find("input").prop("disabled", node.checked || !permission_perm); - - update_simple_type(); - }).prop("disabled", !permission_default).trigger('change').parent().toggleClass("disabled", !permission_default); - } - - /* simple */ - { - simple.find("option[name='channel-type'][value='def']").prop("disabled", !permission_default); - simple.find("option[name='channel-type'][value='perm']").prop("disabled", !permission_perm); - simple.find("option[name='channel-type'][value='semi']").prop("disabled", !permission_semi); - simple.find("option[name='channel-type'][value='temp']").prop("disabled", !permission_temp); - - simple.find("select[name='channel-type']").on('change', event => { - try { - _in_update = true; - switch ((event.target as HTMLSelectElement).value) { - case "temp": - properties.channel_flag_permanent = false; - properties.channel_flag_semi_permanent = false; - properties.channel_flag_default = false; - select_default.prop("checked", false).trigger('change'); - tag_type_temp.trigger('click'); - break; - case "semi": - properties.channel_flag_permanent = false; - properties.channel_flag_semi_permanent = true; - properties.channel_flag_default = false; - select_default.prop("checked", false).trigger('change'); - tag_type_semi.trigger('click'); - break; - case "perm": - properties.channel_flag_permanent = true; - properties.channel_flag_semi_permanent = false; - properties.channel_flag_default = false; - select_default.prop("checked", false).trigger('change'); - tag_type_perm.trigger('click'); - break; - case "def": - properties.channel_flag_permanent = true; - properties.channel_flag_semi_permanent = false; - properties.channel_flag_default = true; - select_default.prop("checked", true).trigger('change'); - break; - } - } finally { - _in_update = false; - /* We dont need to update the simple type because we changed the advanced part to the just changed simple part */ - //update_simple_type(); - } - }); - } - - /* init */ - setTimeout(() => { - if(!channel) { - if(permission_perm) - tag_type_perm.find("input").trigger('click'); - else if(permission_semi) - tag_type_semi.find("input").trigger('click'); - else - tag_type_temp.find("input").trigger('click'); - } else { - if(channel.properties.channel_flag_permanent) - tag_type_perm.find("input").trigger('click'); - else if(channel.properties.channel_flag_semi_permanent) - tag_type_semi.find("input").trigger('click'); - else - tag_type_temp.find("input").trigger('click'); - } - }, 0); - } - - /* Talk power */ - { - const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER : PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1); - const input_advanced = tag.find("input[name='talk_power']").prop("disabled", !permission); - const input_simple = simple.find("input[name='talk_power']").prop("disabled", !permission); - - input_advanced.on('change', event => { - properties.channel_needed_talk_power = parseInt(input_advanced.val() as string); - input_simple.val(input_advanced.val()); - }); - - input_simple.on('change', event => { - properties.channel_needed_talk_power = parseInt(input_simple.val() as string); - input_advanced.val(input_simple.val()); - }); - } - - /* Channel order */ - { - const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER : PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1); - - const advanced_order_id = tag.find(".order_id").prop("disabled", !permission) as JQuery; - const simple_order_id = simple.find(".order_id").prop("disabled", !permission) as JQuery; - - for(let previous_channel of (parent ? parent.children() : connection.channelTree.rootChannel())) { - let selected = channel && channel.properties.channel_order == previous_channel.channelId; - $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(advanced_order_id); - $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(simple_order_id); - } - - advanced_order_id.on('change', event => { - simple_order_id[0].selectedIndex = advanced_order_id[0].selectedIndex; - const selected = $(advanced_order_id[0].options.item(advanced_order_id[0].selectedIndex)); - properties.channel_order = parseInt(selected.attr("channelId")); - }); - - simple_order_id.on('change', event => { - advanced_order_id[0].selectedIndex = simple_order_id[0].selectedIndex; - const selected = $(simple_order_id[0].options.item(simple_order_id[0].selectedIndex)); - properties.channel_order = parseInt(selected.attr("channelId")); - }); - } - - - /* Advanced only */ - { - const container_max_users = tag.find(".container-max-users"); - - const container_unlimited = container_max_users.find(".container-unlimited"); - const container_limited = container_max_users.find(".container-limited"); - - const input_unlimited = container_unlimited.find("input[value='unlimited']"); - const input_limited = container_limited.find("input[value='limited']"); - const input_limit = container_limited.find(".channel_maxclients"); - - const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); - - if(!permission) { - input_unlimited.prop("disabled", true); - input_limited.prop("disabled", true); - input_limit.prop("disabled", true); - - container_limited.addClass("disabled"); - container_unlimited.addClass("disabled"); + const node = input[0] as HTMLTextAreaElement; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; } else { - container_max_users.find("input[name='max_users']").on('change', event => { - const node = event.target as HTMLInputElement; - console.log(tr("Channel max user mode: %o"), node.value); - - const flag = node.value === "unlimited"; - input_limit - .prop("disabled", flag) - .parent().toggleClass("disabled", flag); - properties.channel_flag_maxclients_unlimited = flag; - }); - - input_limit.on('change', event => { - properties.channel_maxclients = parseInt(input_limit.val() as string); - console.log(tr("Changed max user limit to %o"), properties.channel_maxclients); - }); - - setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; } - } - { - const container_max_users = tag.find(".container-max-family-users"); - - const container_unlimited = container_max_users.find(".container-unlimited"); - const container_inherited = container_max_users.find(".container-inherited"); - const container_limited = container_max_users.find(".container-limited"); - - const input_unlimited = container_unlimited.find("input[value='unlimited']"); - const input_inherited = container_inherited.find("input[value='inherited']"); - const input_limited = container_limited.find("input[value='limited']"); - const input_limit = container_limited.find(".channel_maxfamilyclients"); - - const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); - - if(!permission) { - input_unlimited.prop("disabled", true); - input_inherited.prop("disabled", true); - input_limited.prop("disabled", true); - input_limit.prop("disabled", true); - - container_limited.addClass("disabled"); - container_unlimited.addClass("disabled"); - container_inherited.addClass("disabled"); - } else { - container_max_users.find("input[name='max_family_users']").on('change', event => { - const node = event.target as HTMLInputElement; - console.log(tr("Channel max family user mode: %o"), node.value); - - const flag_unlimited = node.value === "unlimited"; - const flag_inherited = node.value === "inherited"; - input_limit - .prop("disabled", flag_unlimited || flag_inherited) - .parent().toggleClass("disabled", flag_unlimited || flag_inherited); - properties.channel_flag_maxfamilyclients_unlimited = flag_unlimited; - properties.channel_flag_maxfamilyclients_inherited = flag_inherited; - }); - - input_limit.on('change', event => { - properties.channel_maxfamilyclients = parseInt(input_limit.val() as string); - console.log(tr("Changed max family user limit to %o"), properties.channel_maxfamilyclients); - }); - - setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); - } - } - } - - function applyPermissionListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, permissions: PermissionManager, channel?: ChannelEntry) { - let apply_permissions = (channel_permissions: PermissionValue[]) => { - log.trace(LogCategory.CHANNEL, tr("Received channel permissions: %o"), channel_permissions); - - let required_power = -2; - for(let cperm of channel_permissions) - if(cperm.type.name == PermissionType.I_CHANNEL_NEEDED_MODIFY_POWER) { - required_power = cperm.value; - break; - } - - tag.find("input[permission]").each((index, _element) => { - let element = $(_element); - element.attr("original-value", 0); - element.val(0); - - let permission = permissions.resolveInfo(element.attr("permission")); - if(!permission) { - log.error(LogCategory.PERMISSIONS, tr("Failed to resolve channel permission for name %o"), element.attr("permission")); - element.prop("disabled", true); - return; - } - - for(let cperm of channel_permissions) - if(cperm.type == permission) { - element.val(cperm.value); - element.attr("original-value", cperm.value); - return; - } - }); - - const permission = permissions.neededPermission(PermissionType.I_CHANNEL_PERMISSION_MODIFY_POWER).granted(required_power, false); - tag.find("input[permission]").prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); //No permissions + input.focus().trigger('change'); }; - if(channel) { - permissions.requestChannelPermissions(channel.getChannelId()).then(apply_permissions).catch((error) => { - tag.find("input[permission]").prop("disabled", true); - console.log("Failed to receive channel permissions (%o)", error); - }); - } else apply_permissions([]); + input.on('change', event => { + console.log(tr("Channel description edited: %o"), input.val()); + properties.channel_description = input.val() as string; + }); + + container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container.find(".button-color input").on('change', event => { + insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') + }) } + tag.find(".channel_description").change(function (this: HTMLInputElement) { + properties.channel_description = this.value; + }).prop("disabled", !connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION : PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1)); - function applyAudioListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, simple: JQuery, channel?: ChannelEntry) { - const bandwidth_mapping = [ - /* SPEEX narrow */ [2.49, 2.69, 2.93, 3.17, 3.17, 3.56, 3.56, 4.05, 4.05, 4.44, 5.22], - /* SPEEX wide */ [2.69, 2.93, 3.17, 3.42, 3.76, 4.25, 4.74, 5.13, 5.62, 6.40, 7.37], - /* SPEEX ultra */ [2.73, 3.12, 3.37, 3.61, 4.00, 4.49, 4.93, 5.32, 5.81, 6.59, 7.57], - /* CELT */ [6.10, 6.10, 7.08, 7.08, 7.08, 8.06, 8.06, 8.06, 8.06, 10.01, 13.92], + if(!channel) { + setTimeout(() => { + tag.find(".channel_name").trigger("change"); + tag.find(".channel_password").trigger('change'); + }, 0); + } +} - /* Opus Voice */ [2.73, 3.22, 3.71, 4.20, 4.74, 5.22, 5.71, 6.20, 6.74, 7.23, 7.71], - /* Opus Music */ [3.08, 3.96, 4.83, 5.71, 6.59, 7.47, 8.35, 9.23, 10.11, 10.99, 11.87] - ]; +function applyStandardListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, simple: JQuery, parent: ChannelEntry, channel: ChannelEntry) { + /* Channel type */ + { + const input_advanced_type = tag.find("input[name='channel_type']"); - let update_template = () => { - let codec = properties.channel_codec; - if(!codec && channel) - codec = channel.properties.channel_codec; - if(!codec) return; + let _in_update = false; + const update_simple_type = () => { + if(_in_update) + return; - let quality = properties.channel_codec_quality; - if(!quality && channel) - quality = channel.properties.channel_codec_quality; - if(!quality) return; - - let template_name = "custom"; - - { - if(codec == 4 && quality == 4) - template_name = "voice_mobile"; - else if(codec == 4 && quality == 6) - template_name = "voice_desktop"; - else if(codec == 5 && quality == 6) - template_name = "music"; - } - tag.find("input[name='voice_template'][value='" + template_name + "']").prop("checked", true); - simple.find("option[name='voice_template'][value='" + template_name + "']").prop("selected", true); - - let bandwidth; - if(codec < 0 || codec > bandwidth_mapping.length) - bandwidth = 0; + let type; + if(properties.channel_flag_default || (typeof(properties.channel_flag_default) === "undefined" && channel && channel.properties.channel_flag_default)) + type = "def"; + else if(properties.channel_flag_permanent || (typeof(properties.channel_flag_permanent) === "undefined" && channel && channel.properties.channel_flag_permanent)) + type = "perm"; + else if(properties.channel_flag_semi_permanent || (typeof(properties.channel_flag_semi_permanent) === "undefined" && channel && channel.properties.channel_flag_semi_permanent)) + type = "semi"; else - bandwidth = bandwidth_mapping[codec][quality] || 0; /* OOB access results in undefined, but is allowed */ - tag.find(".container-needed-bandwidth").text(bandwidth.toFixed(2) + " KiB/s"); + type = "temp"; + + simple.find("option[name='channel-type'][value='" + type + "']").prop("selected", true); }; - let change_codec = codec => { - if(properties.channel_codec == codec) return; - - tag.find(".voice_codec option").prop("selected", false).eq(codec).prop("selected", true); - properties.channel_codec = codec; - update_template(); - }; - - const container_quality = tag.find(".container-quality"); - const slider_quality = sliderfy(container_quality.find(".container-slider"), { - initial_value: properties.channel_codec_quality || 6, - unit: "", - min_value: 1, - max_value: 10, - step: 1, - value_field: container_quality.find(".container-value") - }); - - let change_quality = (quality: number) => { - if(properties.channel_codec_quality == quality) return; - - properties.channel_codec_quality = quality; - slider_quality.value(quality); - update_template(); - }; - - container_quality.find(".container-slider").on('change', event => { - properties.channel_codec_quality = slider_quality.value(); - update_template(); - }); - - tag.find("input[name='voice_template']").change(function (this: HTMLInputElement) { - switch(this.value) { - case "custom": + input_advanced_type.on('change', event => { + const value = [...input_advanced_type as JQuery].find(e => e.checked).value; + switch(value) { + case "semi": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = true; break; - case "music": - change_codec(5); - change_quality(6); + case "perm": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; break; - case "voice_desktop": - change_codec(4); - change_quality(6); - break; - case "voice_mobile": - change_codec(4); - change_quality(4); + default: + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = false; break; } + update_simple_type(); }); - simple.find("select[name='voice_template']").change(function (this: HTMLInputElement) { - switch(this.value) { - case "custom": - break; - case "music": - change_codec(5); - change_quality(6); - break; - case "voice_desktop": - change_codec(4); - change_quality(6); - break; - case "voice_mobile": - change_codec(4); - change_quality(4); - break; - } - }); + const permission_temp = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_TEMPORARY : PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1); + const permission_semi = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1); + const permission_perm = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1); + const permission_default = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_PERMANENT : PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) && + connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT : PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1); - /* disable not granted templates */ - { - tag.find("input[name='voice_template'][value='voice_mobile']") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); - simple.find("option[name='voice_template'][value='voice_mobile']") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); - - tag.find("input[name='voice_template'][value=\"voice_desktop\"]") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); - simple.find("option[name='voice_template'][value=\"voice_desktop\"]") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); - - tag.find("input[name='voice_template'][value=\"music\"]") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); - simple.find("option[name='voice_template'][value=\"music\"]") - .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); - } - - let codecs = tag.find(".voice_codec option"); - codecs.eq(0).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8).granted(1)); - codecs.eq(1).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16).granted(1)); - codecs.eq(2).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32).granted(1)); - codecs.eq(3).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48).granted(1)); - codecs.eq(4).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); - codecs.eq(5).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); - tag.find(".voice_codec").change(function (this: HTMLSelectElement) { - if($(this.item(this.selectedIndex)).prop("disabled")) return false; - - change_codec(this.selectedIndex); - }); - - if(!channel) { - change_codec(4); - change_quality(6); - } else { - change_codec(channel.properties.channel_codec); - change_quality(channel.properties.channel_codec_quality); - } - update_template(); - } - - function applyAdvancedListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) { - tag.find(".channel_name_phonetic").change(function (this: HTMLInputElement) { - properties.channel_topic = this.value; - }); + /* advanced type listeners */ + const container_types = tag.find(".container-channel-type"); + const tag_type_temp = container_types.find(".type-temp"); + const tag_type_semi = container_types.find(".type-semi"); + const tag_type_perm = container_types.find(".type-perm"); + const select_default = tag.find(".input-flag-default"); { - const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1); - tag.find(".channel_delete_delay").change(function (this: HTMLInputElement) { - properties.channel_delete_delay = parseInt(this.value); - }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); + select_default.on('change', event => { + const node = select_default[0] as HTMLInputElement; + properties.channel_flag_default = node.checked; + + if(node.checked) + tag_type_perm.find("input").prop("checked", true); + + tag_type_temp + .toggleClass("disabled", node.checked || !permission_temp) + .find("input").prop("disabled", node.checked || !permission_temp); + + tag_type_semi + .toggleClass("disabled", node.checked || !permission_semi) + .find("input").prop("disabled", node.checked || !permission_semi); + + tag_type_perm + .toggleClass("disabled", node.checked || !permission_perm) + .find("input").prop("disabled", node.checked || !permission_perm); + + update_simple_type(); + }).prop("disabled", !permission_default).trigger('change').parent().toggleClass("disabled", !permission_default); } + /* simple */ { - tag.find(".button-delete-max").on('click', event => { - const power = connection.permissions.neededPermission(PermissionType.I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY).value; - let value = power == -2 ? 0 : power == -1 ? (7 * 24 * 60 * 60) : power; - tag.find(".channel_delete_delay").val(value).trigger('change'); + simple.find("option[name='channel-type'][value='def']").prop("disabled", !permission_default); + simple.find("option[name='channel-type'][value='perm']").prop("disabled", !permission_perm); + simple.find("option[name='channel-type'][value='semi']").prop("disabled", !permission_semi); + simple.find("option[name='channel-type'][value='temp']").prop("disabled", !permission_temp); + + simple.find("select[name='channel-type']").on('change', event => { + try { + _in_update = true; + switch ((event.target as HTMLSelectElement).value) { + case "temp": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_temp.trigger('click'); + break; + case "semi": + properties.channel_flag_permanent = false; + properties.channel_flag_semi_permanent = true; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_semi.trigger('click'); + break; + case "perm": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = false; + select_default.prop("checked", false).trigger('change'); + tag_type_perm.trigger('click'); + break; + case "def": + properties.channel_flag_permanent = true; + properties.channel_flag_semi_permanent = false; + properties.channel_flag_default = true; + select_default.prop("checked", true).trigger('change'); + break; + } + } finally { + _in_update = false; + /* We dont need to update the simple type because we changed the advanced part to the just changed simple part */ + //update_simple_type(); + } }); } - { - const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1); - tag.find(".channel_codec_is_unencrypted").change(function (this: HTMLInputElement) { - properties.channel_codec_is_unencrypted = parseInt(this.value) == 0; - }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); + /* init */ + setTimeout(() => { + if(!channel) { + if(permission_perm) + tag_type_perm.find("input").trigger('click'); + else if(permission_semi) + tag_type_semi.find("input").trigger('click'); + else + tag_type_temp.find("input").trigger('click'); + } else { + if(channel.properties.channel_flag_permanent) + tag_type_perm.find("input").trigger('click'); + else if(channel.properties.channel_flag_semi_permanent) + tag_type_semi.find("input").trigger('click'); + else + tag_type_temp.find("input").trigger('click'); + } + }, 0); + } + + /* Talk power */ + { + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER : PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1); + const input_advanced = tag.find("input[name='talk_power']").prop("disabled", !permission); + const input_simple = simple.find("input[name='talk_power']").prop("disabled", !permission); + + input_advanced.on('change', event => { + properties.channel_needed_talk_power = parseInt(input_advanced.val() as string); + input_simple.val(input_advanced.val()); + }); + + input_simple.on('change', event => { + properties.channel_needed_talk_power = parseInt(input_simple.val() as string); + input_advanced.val(input_simple.val()); + }); + } + + /* Channel order */ + { + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER : PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1); + + const advanced_order_id = tag.find(".order_id").prop("disabled", !permission) as JQuery; + const simple_order_id = simple.find(".order_id").prop("disabled", !permission) as JQuery; + + for(let previous_channel of (parent ? parent.children() : connection.channelTree.rootChannel())) { + let selected = channel && channel.properties.channel_order == previous_channel.channelId; + $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(advanced_order_id); + $.spawn("option").attr("channelId", previous_channel.channelId.toString()).prop("selected", selected).text(previous_channel.channelName()).appendTo(simple_order_id); } + + advanced_order_id.on('change', event => { + simple_order_id[0].selectedIndex = advanced_order_id[0].selectedIndex; + const selected = $(advanced_order_id[0].options.item(advanced_order_id[0].selectedIndex)); + properties.channel_order = parseInt(selected.attr("channelId")); + }); + + simple_order_id.on('change', event => { + advanced_order_id[0].selectedIndex = simple_order_id[0].selectedIndex; + const selected = $(simple_order_id[0].options.item(simple_order_id[0].selectedIndex)); + properties.channel_order = parseInt(selected.attr("channelId")); + }); + } + + + /* Advanced only */ + { + const container_max_users = tag.find(".container-max-users"); + + const container_unlimited = container_max_users.find(".container-unlimited"); + const container_limited = container_max_users.find(".container-limited"); + + const input_unlimited = container_unlimited.find("input[value='unlimited']"); + const input_limited = container_limited.find("input[value='limited']"); + const input_limit = container_limited.find(".channel_maxclients"); + + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); + + if(!permission) { + input_unlimited.prop("disabled", true); + input_limited.prop("disabled", true); + input_limit.prop("disabled", true); + + container_limited.addClass("disabled"); + container_unlimited.addClass("disabled"); + } else { + container_max_users.find("input[name='max_users']").on('change', event => { + const node = event.target as HTMLInputElement; + console.log(tr("Channel max user mode: %o"), node.value); + + const flag = node.value === "unlimited"; + input_limit + .prop("disabled", flag) + .parent().toggleClass("disabled", flag); + properties.channel_flag_maxclients_unlimited = flag; + }); + + input_limit.on('change', event => { + properties.channel_maxclients = parseInt(input_limit.val() as string); + console.log(tr("Changed max user limit to %o"), properties.channel_maxclients); + }); + + setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); + } + } + + { + const container_max_users = tag.find(".container-max-family-users"); + + const container_unlimited = container_max_users.find(".container-unlimited"); + const container_inherited = container_max_users.find(".container-inherited"); + const container_limited = container_max_users.find(".container-limited"); + + const input_unlimited = container_unlimited.find("input[value='unlimited']"); + const input_inherited = container_inherited.find("input[value='inherited']"); + const input_limited = container_limited.find("input[value='limited']"); + const input_limit = container_limited.find(".channel_maxfamilyclients"); + + const permission = connection.permissions.neededPermission(!channel ? PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS : PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1); + + if(!permission) { + input_unlimited.prop("disabled", true); + input_inherited.prop("disabled", true); + input_limited.prop("disabled", true); + input_limit.prop("disabled", true); + + container_limited.addClass("disabled"); + container_unlimited.addClass("disabled"); + container_inherited.addClass("disabled"); + } else { + container_max_users.find("input[name='max_family_users']").on('change', event => { + const node = event.target as HTMLInputElement; + console.log(tr("Channel max family user mode: %o"), node.value); + + const flag_unlimited = node.value === "unlimited"; + const flag_inherited = node.value === "inherited"; + input_limit + .prop("disabled", flag_unlimited || flag_inherited) + .parent().toggleClass("disabled", flag_unlimited || flag_inherited); + properties.channel_flag_maxfamilyclients_unlimited = flag_unlimited; + properties.channel_flag_maxfamilyclients_inherited = flag_inherited; + }); + + input_limit.on('change', event => { + properties.channel_maxfamilyclients = parseInt(input_limit.val() as string); + console.log(tr("Changed max family user limit to %o"), properties.channel_maxfamilyclients); + }); + + setTimeout(() => container_max_users.find("input:checked").trigger('change'), 100); + } + } +} + +function applyPermissionListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, permissions: PermissionManager, channel?: ChannelEntry) { + let apply_permissions = (channel_permissions: PermissionValue[]) => { + log.trace(LogCategory.CHANNEL, tr("Received channel permissions: %o"), channel_permissions); + + let required_power = -2; + for(let cperm of channel_permissions) + if(cperm.type.name == PermissionType.I_CHANNEL_NEEDED_MODIFY_POWER) { + required_power = cperm.value; + break; + } + + tag.find("input[permission]").each((index, _element) => { + let element = $(_element); + element.attr("original-value", 0); + element.val(0); + + let permission = permissions.resolveInfo(element.attr("permission")); + if(!permission) { + log.error(LogCategory.PERMISSIONS, tr("Failed to resolve channel permission for name %o"), element.attr("permission")); + element.prop("disabled", true); + return; + } + + for(let cperm of channel_permissions) + if(cperm.type == permission) { + element.val(cperm.value); + element.attr("original-value", cperm.value); + return; + } + }); + + const permission = permissions.neededPermission(PermissionType.I_CHANNEL_PERMISSION_MODIFY_POWER).granted(required_power, false); + tag.find("input[permission]").prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); //No permissions + }; + + if(channel) { + permissions.requestChannelPermissions(channel.getChannelId()).then(apply_permissions).catch((error) => { + tag.find("input[permission]").prop("disabled", true); + console.log("Failed to receive channel permissions (%o)", error); + }); + } else apply_permissions([]); +} + +function applyAudioListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, simple: JQuery, channel?: ChannelEntry) { + const bandwidth_mapping = [ + /* SPEEX narrow */ [2.49, 2.69, 2.93, 3.17, 3.17, 3.56, 3.56, 4.05, 4.05, 4.44, 5.22], + /* SPEEX wide */ [2.69, 2.93, 3.17, 3.42, 3.76, 4.25, 4.74, 5.13, 5.62, 6.40, 7.37], + /* SPEEX ultra */ [2.73, 3.12, 3.37, 3.61, 4.00, 4.49, 4.93, 5.32, 5.81, 6.59, 7.57], + /* CELT */ [6.10, 6.10, 7.08, 7.08, 7.08, 8.06, 8.06, 8.06, 8.06, 10.01, 13.92], + + /* Opus Voice */ [2.73, 3.22, 3.71, 4.20, 4.74, 5.22, 5.71, 6.20, 6.74, 7.23, 7.71], + /* Opus Music */ [3.08, 3.96, 4.83, 5.71, 6.59, 7.47, 8.35, 9.23, 10.11, 10.99, 11.87] + ]; + + let update_template = () => { + let codec = properties.channel_codec; + if(!codec && channel) + codec = channel.properties.channel_codec; + if(!codec) return; + + let quality = properties.channel_codec_quality; + if(!quality && channel) + quality = channel.properties.channel_codec_quality; + if(!quality) return; + + let template_name = "custom"; + + { + if(codec == 4 && quality == 4) + template_name = "voice_mobile"; + else if(codec == 4 && quality == 6) + template_name = "voice_desktop"; + else if(codec == 5 && quality == 6) + template_name = "music"; + } + tag.find("input[name='voice_template'][value='" + template_name + "']").prop("checked", true); + simple.find("option[name='voice_template'][value='" + template_name + "']").prop("selected", true); + + let bandwidth; + if(codec < 0 || codec > bandwidth_mapping.length) + bandwidth = 0; + else + bandwidth = bandwidth_mapping[codec][quality] || 0; /* OOB access results in undefined, but is allowed */ + tag.find(".container-needed-bandwidth").text(bandwidth.toFixed(2) + " KiB/s"); + }; + + let change_codec = codec => { + if(properties.channel_codec == codec) return; + + tag.find(".voice_codec option").prop("selected", false).eq(codec).prop("selected", true); + properties.channel_codec = codec; + update_template(); + }; + + const container_quality = tag.find(".container-quality"); + const slider_quality = sliderfy(container_quality.find(".container-slider"), { + initial_value: properties.channel_codec_quality || 6, + unit: "", + min_value: 1, + max_value: 10, + step: 1, + value_field: container_quality.find(".container-value") + }); + + let change_quality = (quality: number) => { + if(properties.channel_codec_quality == quality) return; + + properties.channel_codec_quality = quality; + slider_quality.value(quality); + update_template(); + }; + + container_quality.find(".container-slider").on('change', event => { + properties.channel_codec_quality = slider_quality.value(); + update_template(); + }); + + tag.find("input[name='voice_template']").change(function (this: HTMLInputElement) { + switch(this.value) { + case "custom": + break; + case "music": + change_codec(5); + change_quality(6); + break; + case "voice_desktop": + change_codec(4); + change_quality(6); + break; + case "voice_mobile": + change_codec(4); + change_quality(4); + break; + } + }); + + simple.find("select[name='voice_template']").change(function (this: HTMLInputElement) { + switch(this.value) { + case "custom": + break; + case "music": + change_codec(5); + change_quality(6); + break; + case "voice_desktop": + change_codec(4); + change_quality(6); + break; + case "voice_mobile": + change_codec(4); + change_quality(4); + break; + } + }); + + /* disable not granted templates */ + { + tag.find("input[name='voice_template'][value='voice_mobile']") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + simple.find("option[name='voice_template'][value='voice_mobile']") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + + tag.find("input[name='voice_template'][value=\"voice_desktop\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + simple.find("option[name='voice_template'][value=\"voice_desktop\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + + tag.find("input[name='voice_template'][value=\"music\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + simple.find("option[name='voice_template'][value=\"music\"]") + .prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + } + + let codecs = tag.find(".voice_codec option"); + codecs.eq(0).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX8).granted(1)); + codecs.eq(1).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX16).granted(1)); + codecs.eq(2).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_SPEEX32).granted(1)); + codecs.eq(3).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_CELTMONO48).granted(1)); + codecs.eq(4).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1)); + codecs.eq(5).prop("disabled", !connection.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1)); + tag.find(".voice_codec").change(function (this: HTMLSelectElement) { + if($(this.item(this.selectedIndex)).prop("disabled")) return false; + + change_codec(this.selectedIndex); + }); + + if(!channel) { + change_codec(4); + change_quality(6); + } else { + change_codec(channel.properties.channel_codec); + change_quality(channel.properties.channel_codec_quality); + } + update_template(); +} + +function applyAdvancedListener(connection: ConnectionHandler, properties: ChannelProperties, tag: JQuery, button: JQuery, channel?: ChannelEntry) { + tag.find(".channel_name_phonetic").change(function (this: HTMLInputElement) { + properties.channel_topic = this.value; + }); + + { + const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1); + tag.find(".channel_delete_delay").change(function (this: HTMLInputElement) { + properties.channel_delete_delay = parseInt(this.value); + }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); + } + + { + tag.find(".button-delete-max").on('click', event => { + const power = connection.permissions.neededPermission(PermissionType.I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY).value; + let value = power == -2 ? 0 : power == -1 ? (7 * 24 * 60 * 60) : power; + tag.find(".channel_delete_delay").val(value).trigger('change'); + }); + } + + { + const permission = connection.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1); + tag.find(".channel_codec_is_unencrypted").change(function (this: HTMLInputElement) { + properties.channel_codec_is_unencrypted = parseInt(this.value) == 0; + }).prop("disabled", !permission).parent(".input-boxed").toggleClass("disabled", !permission); } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalGroupAssignment.ts b/shared/js/ui/modal/ModalGroupAssignment.ts index 52deb24d..4efff2c9 100644 --- a/shared/js/ui/modal/ModalGroupAssignment.ts +++ b/shared/js/ui/modal/ModalGroupAssignment.ts @@ -1,81 +1,85 @@ -namespace Modals { - let current_modal: Modal; - export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise) { - if(current_modal) - current_modal.close(); +import {LogCategory} from "tc-shared/log"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import * as log from "tc-shared/log"; +import {ClientEntry} from "tc-shared/ui/client"; +import {GroupManager, GroupType} from "tc-shared/permission/GroupManager"; +import PermissionType from "tc-shared/permission/PermissionType"; - current_modal = createModal({ - header: tr("Server Groups"), - body: () => { - let tag: any = {}; - let groups = tag["groups"] = []; +let current_modal: Modal; +export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise) { + if(current_modal) + current_modal.close(); - tag["client"] = client.createChatTag(); + current_modal = createModal({ + header: tr("Server Groups"), + body: () => { + let tag: any = {}; + let groups = tag["groups"] = []; - const _groups = client.channelTree.client.groups.serverGroups.sort(GroupManager.sorter()); + tag["client"] = client.createChatTag(); + + const _groups = client.channelTree.client.groups.serverGroups.sort(GroupManager.sorter()); + for(let group of _groups) { + if(group.type != GroupType.NORMAL) continue; + + let entry = {} as any; + entry["id"] = group.id; + entry["name"] = group.name; + entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); + entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id; + tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid); + groups.push(entry); + } + + let template = $("#tmpl_server_group_assignment").renderTag(tag); + + const update_groups = () => { for(let group of _groups) { - if(group.type != GroupType.NORMAL) continue; - - let entry = {} as any; - entry["id"] = group.id; - entry["name"] = group.name; - entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); - entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id; - tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid); - groups.push(entry); + template.find("input[group-id='" + group.id + "']").prop("checked", client.groupAssigned(group)); } + }; - let template = $("#tmpl_server_group_assignment").renderTag(tag); + template.find(".group-entry input").each((_idx, _entry) => { + let entry = $(_entry); - const update_groups = () => { - for(let group of _groups) { - template.find("input[group-id='" + group.id + "']").prop("checked", client.groupAssigned(group)); + entry.on('change', event => { + let group_id = parseInt(entry.attr("group-id")); + let group = client.channelTree.client.groups.serverGroup(group_id); + if(!group) { + console.warn(tr("Could not resolve target group!")); + return false; } - }; + + let target = entry.prop("checked"); + callback([group.id], target).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to change group assignment: %o"), e)}).then(update_groups); + }); + }); + + template.find(".button-close").on('click', () => current_modal.close()); + template.find(".button-remove-all").on('click', () => { + const group_ids = []; template.find(".group-entry input").each((_idx, _entry) => { let entry = $(_entry); + if(entry.attr("default") !== undefined || !entry.prop("checked")) + return; - entry.on('change', event => { - let group_id = parseInt(entry.attr("group-id")); - let group = client.channelTree.client.groups.serverGroup(group_id); - if(!group) { - console.warn(tr("Could not resolve target group!")); - return false; - } - - let target = entry.prop("checked"); - callback([group.id], target).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to change group assignment: %o"), e)}).then(update_groups); - }); + group_ids.push(parseInt(entry.attr("group-id"))); }); - template.find(".button-close").on('click', () => current_modal.close()); - template.find(".button-remove-all").on('click', () => { - const group_ids = []; + callback(group_ids, false).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to remove all group assignments: %o"), e)}).then(update_groups); - template.find(".group-entry input").each((_idx, _entry) => { - let entry = $(_entry); - if(entry.attr("default") !== undefined || !entry.prop("checked")) - return; + }); - group_ids.push(parseInt(entry.attr("group-id"))); - }); + update_groups(); + return template; + }, + footer: null, + min_width: "10em" - callback(group_ids, false).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to remove all group assignments: %o"), e)}).then(update_groups); - - }); - - update_groups(); - return template; - }, - footer: null, - min_width: "10em" - - }); - - current_modal.htmlTag.find(".modal-body").addClass("modal-server-group-assignments"); - current_modal.close_listener.push(() => current_modal = undefined); - current_modal.open(); - } + }); + current_modal.htmlTag.find(".modal-body").addClass("modal-server-group-assignments"); + current_modal.close_listener.push(() => current_modal = undefined); + current_modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalIconSelect.ts b/shared/js/ui/modal/ModalIconSelect.ts index 42911e61..78cee825 100644 --- a/shared/js/ui/modal/ModalIconSelect.ts +++ b/shared/js/ui/modal/ModalIconSelect.ts @@ -1,539 +1,518 @@ -/// -/// -/// +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal"; +import {FileEntry, spawn_upload_transfer, UploadKey} from "tc-shared/FileManager"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {tra} from "tc-shared/i18n/localize"; +import {arrayBufferBase64} from "tc-shared/utils/buffers"; +import {Settings, settings} from "tc-shared/settings"; +import * as crc32 from "tc-shared/crypto/crc32"; -namespace Modals { - export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) { - selected_icon = selected_icon || 0; - let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1); +export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) { + selected_icon = selected_icon || 0; + let allow_manage = client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1); - const modal = createModal({ - header: tr("Icons"), - footer: undefined, - body: () => { - return $("#tmpl_icon_select").renderTag({ - enable_select: !!callback_icon, + const modal = createModal({ + header: tr("Icons"), + footer: undefined, + body: () => { + return $("#tmpl_icon_select").renderTag({ + enable_select: !!callback_icon, - enable_upload: allow_manage, - enable_delete: allow_manage + enable_upload: allow_manage, + enable_delete: allow_manage + }); + }, + + min_width: "20em" + }); + + modal.htmlTag.find(".modal-body").addClass("modal-icon-select"); + + const button_select = modal.htmlTag.find(".button-select"); + const button_delete = modal.htmlTag.find(".button-delete").prop("disabled", true); + const button_upload = modal.htmlTag.find(".button-upload").prop("disabled", !allow_manage); + + const container_loading = modal.htmlTag.find(".container-loading").hide(); + const container_no_permissions = modal.htmlTag.find(".container-no-permissions").hide(); + const container_error = modal.htmlTag.find(".container-error").hide(); + + const selected_container = modal.htmlTag.find(".selected-item-container"); + + const container_icons = modal.htmlTag.find(".container-icons"); + const container_icons_remote = container_icons.find(".container-icons-remote"); + const container_icons_local = container_icons.find(".container-icons-local"); + + const update_local_icons = (icons: number[]) => { + container_icons_local.empty(); + + for(const icon_id of icons) { + const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id); + if(callback_icon) { + tag.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + tag.addClass("selected"); + + selected_container.empty().append(tag.clone()); + selected_icon = icon_id; + button_select.prop("disabled", false); }); - }, - - min_width: "20em" - }); - - modal.htmlTag.find(".modal-body").addClass("modal-icon-select"); - - const button_select = modal.htmlTag.find(".button-select"); - const button_delete = modal.htmlTag.find(".button-delete").prop("disabled", true); - const button_upload = modal.htmlTag.find(".button-upload").prop("disabled", !allow_manage); - - const container_loading = modal.htmlTag.find(".container-loading").hide(); - const container_no_permissions = modal.htmlTag.find(".container-no-permissions").hide(); - const container_error = modal.htmlTag.find(".container-error").hide(); - - const selected_container = modal.htmlTag.find(".selected-item-container"); - - const container_icons = modal.htmlTag.find(".container-icons"); - const container_icons_remote = container_icons.find(".container-icons-remote"); - const container_icons_local = container_icons.find(".container-icons-local"); - - const update_local_icons = (icons: number[]) => { - container_icons_local.empty(); - - for(const icon_id of icons) { - const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id); - if(callback_icon) { - tag.on('click', event => { - container_icons.find(".selected").removeClass("selected"); - tag.addClass("selected"); - - selected_container.empty().append(tag.clone()); - selected_icon = icon_id; - button_select.prop("disabled", false); - }); - tag.on('dblclick', event => { - callback_icon(icon_id); - modal.close(); - }); - if(icon_id == selected_icon) - tag.trigger('click'); - } - tag.appendTo(container_icons_local); + tag.on('dblclick', event => { + callback_icon(icon_id); + modal.close(); + }); + if(icon_id == selected_icon) + tag.trigger('click'); } - }; - - const update_remote_icons = () => { - container_no_permissions.hide(); - container_error.hide(); - container_loading.show(); - const display_remote_error = (error?: string) => { - if(typeof(error) === "string") { - container_error.find(".error-message").text(error); - container_error.show(); - } else { - container_error.hide(); - } - }; - - client.fileManager.requestFileList("/icons").then(icons => { - const container_icons_remote_parent = container_icons_remote.parent(); - container_icons_remote.detach().empty(); - - const chunk_size = 50; - const icon_chunks: FileEntry[][] = []; - let index = 0; - while(icons.length > index) { - icon_chunks.push(icons.slice(index, index + chunk_size)); - index += chunk_size; - } - - const process_next_chunk = () => { - const chunk = icon_chunks.pop_front(); - if(!chunk) return; - - for(const icon of chunk) { - const icon_id = parseInt(icon.name.substr("icon_".length)); - if(Number.isNaN(icon_id)) { - log.warn(LogCategory.GENERAL, tr("Received an unparsable icon within icon list (%o)"), icon); - continue; - } - const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id); - if(callback_icon || allow_manage) { - tag.on('click', event => { - container_icons.find(".selected").removeClass("selected"); - tag.addClass("selected"); - - selected_container.empty().append(tag.clone()); - selected_icon = icon_id; - button_select.prop("disabled", false); - button_delete.prop("disabled", !allow_manage); - }); - tag.on('dblclick', event => { - if(!callback_icon) - return; - - callback_icon(icon_id); - modal.close(); - }); - if(icon_id == selected_icon) - tag.trigger('click'); - } - tag.appendTo(container_icons_remote); - } - setTimeout(process_next_chunk, 100); - }; - process_next_chunk(); - - container_icons_remote_parent.append(container_icons_remote); - container_error.hide(); - container_loading.hide(); - container_no_permissions.hide(); - }).catch(error => { - if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { - container_no_permissions.show(); - } else { - log.error(LogCategory.GENERAL, tr("Failed to fetch icon list. Error: %o"), error); - display_remote_error(tr("Failed to fetch icon list")); - } - container_loading.hide(); - }); - }; - - button_delete.on('click', event => { - if(!selected_icon) - return; - - const selected = modal.htmlTag.find(".selected"); - if(selected.length != 1) - console.warn(tr("UI selected icon length does not equal with 1! (%o)"), selected.length); - - if(selected_icon < 1000) return; /* we cant delete local icons */ - - client.fileManager.icons.delete_icon(selected_icon).then(() => { - selected.detach(); - }).catch(error => { - if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) - return; - console.warn(tr("Failed to delete icon %d: %o"), selected_icon, error); - - error = error instanceof CommandResult ? error.extra_message || error.message : error; - - createErrorModal(tr("Failed to delete icon"), tra("Failed to delete icon.
Error: ", error)).open(); - }); - }); - - button_upload.on('click', event => spawnIconUpload(client)); - - update_local_icons([100, 200, 300, 500, 600]); - update_remote_icons(); - modal.htmlTag.find('.button-reload').on('click', () => update_remote_icons()); - button_select.prop("disabled", true).on('click', () => { - if(callback_icon) callback_icon(selected_icon); - modal.close(); - }); - modal.htmlTag.find(".button-select-no-icon").on('click', () => { - if(callback_icon) callback_icon(0); - modal.close(); - }); - modal.open(); - } - - interface UploadingIcon { - file: File; - state: "loading" | "valid" | "error"; - upload_state: "unset" | "uploading" | "uploaded" | "error"; - - html_tag?: JQuery; - image_element?: () => HTMLImageElement; - - loader: Promise; - - upload_icon: () => () => Promise; - upload_html_tag?: JQuery; - - icon_id: string; - } - - function handle_icon_upload(file: File, client: ConnectionHandler) : UploadingIcon { - const icon = {} as UploadingIcon; - icon.file = file; - icon.upload_state = "unset"; - - const file_too_big = () => { - console.error(tr("Failed to load file %s: File is too big!"), file.name); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
The given file is too big!", file.name)).open(); - icon.state = "error"; - }; - if(file.size > 1024 * 1024 * 512) { - file_too_big(); - } else if((file.size | 0) <= 0) { - console.error(tr("Failed to load file %s: Your browser does not support file sizes!"), file.name); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Your browser does not support file sizes!", file.name)).open(); - icon.state = "error"; - return; - } else { - icon.state = "loading"; - icon.loader = (async () => { - const reader = new FileReader(); - - try { - await new Promise((resolve, reject) => { - reader.onload = resolve; - reader.onerror = reject; - reader.readAsDataURL(file); - }); - } catch(error) { - console.log("Image failed to load (%o)", error); - console.error(tr("Failed to load file %s: Image failed to load"), file.name); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Failed to load image", file.name)).open(); - icon.state = "error"; - return; - } - - const result = reader.result as string; - if(typeof(result) !== "string") { - console.error(tr("Failed to load file %s: Result is not an media string (%o)"), file.name, result); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Result is not an media string", file.name)).open(); - icon.state = "error"; - return; - } - - - /* get the CRC32 sum */ - { - if(!result.startsWith("data:image/")) { - console.error(tr("Failed to load file %s: Invalid data media type (%o)"), file.name, result); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
File is not an image", file.name)).open(); - icon.state = "error"; - return; - } - const semi = result.indexOf(';'); - const type = result.substring(11, semi); - console.log(tr("Given image has type %s"), type); - if(!result.substr(semi + 1).startsWith("base64,")) { - console.error(tr("Failed to load file %s: Mimetype isnt base64 encoded (%o)"), file.name, result.substr(semi + 1)); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Decoder returned unknown result", file.name)).open(); - icon.state = "error"; - return; - } - - const crc = new Crc32(); - crc.update(arrayBufferBase64(result.substr(semi + 8))); - icon.icon_id = crc.digest(10); - } - - - const image = document.createElement("img"); - try { - await new Promise((resolve, reject) => { - image.onload = resolve; - image.onerror = reject; - image.src = result; - }); - } catch(error) { - console.log("Image failed to load (%o)", error); - console.error(tr("Failed to load file %s: Image failed to load"), file.name); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.{:br:}Failed to load image", file.name)).open(); - icon.state = "error"; - } - - const width_error = message => { - console.error(tr("Failed to load file %s: Invalid bounds: %s"), file.name, message); - createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.{:br:}Image is too large ({})", file.name, message)).open(); - icon.state = "error"; - }; - - if(!result.startsWith("data:image/svg+xml")) { - if(image.naturalWidth > 32 && image.naturalHeight > 32) { - width_error("width and height (max 32px). Given: " + image.naturalWidth + "x" + image.naturalHeight); - return; - } - if(image.naturalWidth > 32) { - width_error("width (max 32px)"); - return; - } - if(image.naturalHeight > 32) { - width_error("height (max 32px)"); - return; - } - } - console.log("Image loaded (%dx%d) %s (%s)", image.naturalWidth, image.naturalHeight, image.name, icon.icon_id); - icon.image_element = () => { - const image = document.createElement("img"); - image.src = result; - return image; - }; - icon.state = "valid"; - })(); - - icon.upload_icon = () => { - const create_progress_bar = () => { - const html = $.spawn("div").addClass("progress"); - const indicator = $.spawn("div").addClass("progress-bar bg-success progress-bar-striped progress-bar-animated"); - const message = $.spawn("div").addClass("progress-message"); - const set_value = value => { - indicator.stop(true, false).animate({width: value + "%"}, 250); - if(value === 100) - setTimeout(() => indicator.removeClass("progress-bar-striped progress-bar-animated"), 900) - }; - - return { - html_tag: html.append(indicator).append(message), - set_value: set_value, - set_message: msg => message.text(msg), - set_error: (msg: string) => { - let index = msg.lastIndexOf(':'); - message.text(index == -1 ? msg : msg.substring(index + 1)); - message.attr('title', msg); - set_value(100); - indicator.removeClass("bg-success").addClass("bg-danger"); - } - } - }; - - const container_image = $.spawn("div").addClass("container-icon"); - const bar = create_progress_bar(); - - const set_error = message => { - bar.set_value(100); - bar.set_message(tr("error: ") + message); - }; - - const html_tag = $.spawn("div") - .addClass("upload-entry") - .append(container_image) - .append(bar.html_tag); - - icon.upload_html_tag = html_tag; - - let icon_added = false; - if(icon.image_element) { - container_image.append(icon.image_element()); - icon_added = true; - } - - - bar.set_value(0); - bar.set_value(tr("waiting")); - - return async () => { - const time_begin = Date.now(); - - if(icon.state === "loading") { - bar.set_message(tr("Awaiting local processing")); - await icon.loader; - // @ts-ignore Could happen because the loader function updates the state - if(icon.state !== "valid") { - set_error(tr("local processing failed")); - icon.upload_state = "error"; - return; - } - } else if(icon.state === "error") { - set_error(tr("local processing error")); - icon.upload_state = "error"; - return; - } - if(!icon_added) - container_image.append(icon.image_element()); - - bar.set_value(25); - bar.set_message(tr("initializing")); - - let upload_key: transfer.UploadKey; - try { - upload_key = await client.fileManager.upload_file({ - channel: undefined, - channel_password: undefined, - name: '/icon_' + icon.icon_id, - overwrite: false, - path: '', - size: icon.file.size - }) - } catch(error) { - if(error instanceof CommandResult && error.id == ErrorID.FILE_ALREADY_EXISTS) { - if(!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) - await new Promise(resolve => setTimeout(resolve, 500 + Math.floor(Math.random() * 500))); - bar.set_message(tr("icon already exists")); - bar.set_value(100); - icon.upload_state = "uploaded"; - return; - } - console.error(tr("Failed to initialize upload: %o"), error); - bar.set_error(tr("failed to initialize upload")); - icon.upload_state = "error"; - return; - } - bar.set_value(50); - bar.set_message(tr("uploading")); - - const connection = transfer.spawn_upload_transfer(upload_key); - try { - await connection.put_data(icon.file) - } catch(error) { - console.error(tr("Icon upload failed for icon %s: %o"), icon.file.name, error); - if(typeof(error) === "string") - bar.set_error(tr("upload failed: ") + error); - else if(typeof(error.message) === "string") - bar.set_error(tr("upload failed: ") + error.message); - else - bar.set_error(tr("upload failed")); - icon.upload_state = "error"; - return; - } - - const time_end = Date.now(); - if(!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) - await new Promise(resolve => setTimeout(resolve, Math.max(0, 1000 - (time_end - time_begin)))); - bar.set_value(100); - bar.set_message(tr("upload completed")); - icon.upload_state = "uploaded"; - }; - }; + tag.appendTo(container_icons_local); } + }; - return icon; - } - - export function spawnIconUpload(client: ConnectionHandler) { - const modal = createModal({ - header: tr("Upload Icons"), - footer: undefined, - body: () => $("#tmpl_icon_upload").renderTag(), - closeable: false, - - min_width: "20em" - }); - modal.htmlTag.find(".modal-body").addClass("modal-icon-upload"); - - const button_upload = modal.htmlTag.find(".button-upload"); - const button_delete = modal.htmlTag.find(".button-remove").prop("disabled", true); - const button_add = modal.htmlTag.find(".button-add"); - const button_upload_abort = modal.htmlTag.find(".button-upload-abort"); - const input_file = modal.htmlTag.find(".input-file-upload") as JQuery; - const container_icons = modal.htmlTag.find(".container-icons"); - - let selected_icon: UploadingIcon; - let icons: UploadingIcon[] = []; - - const update_upload_button = () => { - const icon_count = icons.filter(e => e.state === "valid").length; - button_upload.empty(); - tra("Upload icons ({})", icon_count).forEach(e => e.appendTo(button_upload)); - button_upload.prop("disabled", icon_count == 0); + const update_remote_icons = () => { + container_no_permissions.hide(); + container_error.hide(); + container_loading.show(); + const display_remote_error = (error?: string) => { + if(typeof(error) === "string") { + container_error.find(".error-message").text(error); + container_error.show(); + } else { + container_error.hide(); + } }; - update_upload_button(); - const add_icon = (icon: UploadingIcon) => { - icons.push(icon); - icon.loader.then(e => { - if(icon.state === "valid") { - const image = icon.image_element(); - const element = $.spawn("div") - .addClass("icon-container") - .append(image); - container_icons.append(icon.html_tag = element); + client.fileManager.requestFileList("/icons").then(icons => { + const container_icons_remote_parent = container_icons_remote.parent(); + container_icons_remote.detach().empty(); - element.on('click', event => { - container_icons.find(".selected").removeClass("selected"); - element.addClass("selected"); + const chunk_size = 50; + const icon_chunks: FileEntry[][] = []; + let index = 0; + while(icons.length > index) { + icon_chunks.push(icons.slice(index, index + chunk_size)); + index += chunk_size; + } - selected_icon = icon; - button_delete.prop("disabled", false); - }); + const process_next_chunk = () => { + const chunk = icon_chunks.pop_front(); + if(!chunk) return; - update_upload_button(); - } - }); - }; - button_delete.on('click', event => { - if(!selected_icon) - return; - icons = icons.filter(e => e !== selected_icon); - if(selected_icon.html_tag) - selected_icon.html_tag.detach(); - button_delete.prop("disabled", true); - update_upload_button(); - }); - - button_add.on('click', event => input_file.click()); - input_file.on('change', event => { - if(input_file[0].files.length > 0) { - for(let index = 0; index < input_file[0].files.length; index++) { - const file = input_file[0].files.item(index); - { - let duplicate = false; - - for(const icon of icons) - if(icon.file.name === file.name && icon.file.lastModified === file.lastModified && icon.state !== "error") { - duplicate = true; - break; - } - if(duplicate) - continue; + for(const icon of chunk) { + const icon_id = parseInt(icon.name.substr("icon_".length)); + if(Number.isNaN(icon_id)) { + log.warn(LogCategory.GENERAL, tr("Received an unparsable icon within icon list (%o)"), icon); + continue; } + const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id); + if(callback_icon || allow_manage) { + tag.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + tag.addClass("selected"); - add_icon(handle_icon_upload(file, client)); + selected_container.empty().append(tag.clone()); + selected_icon = icon_id; + button_select.prop("disabled", false); + button_delete.prop("disabled", !allow_manage); + }); + tag.on('dblclick', event => { + if(!callback_icon) + return; + + callback_icon(icon_id); + modal.close(); + }); + if(icon_id == selected_icon) + tag.trigger('click'); + } + tag.appendTo(container_icons_remote); + } + setTimeout(process_next_chunk, 100); + }; + process_next_chunk(); + + container_icons_remote_parent.append(container_icons_remote); + container_error.hide(); + container_loading.hide(); + container_no_permissions.hide(); + }).catch(error => { + if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + container_no_permissions.show(); + } else { + log.error(LogCategory.GENERAL, tr("Failed to fetch icon list. Error: %o"), error); + display_remote_error(tr("Failed to fetch icon list")); + } + container_loading.hide(); + }); + }; + + button_delete.on('click', event => { + if(!selected_icon) + return; + + const selected = modal.htmlTag.find(".selected"); + if(selected.length != 1) + console.warn(tr("UI selected icon length does not equal with 1! (%o)"), selected.length); + + if(selected_icon < 1000) return; /* we cant delete local icons */ + + client.fileManager.icons.delete_icon(selected_icon).then(() => { + selected.detach(); + }).catch(error => { + if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) + return; + console.warn(tr("Failed to delete icon %d: %o"), selected_icon, error); + + error = error instanceof CommandResult ? error.extra_message || error.message : error; + + createErrorModal(tr("Failed to delete icon"), tra("Failed to delete icon.
Error: ", error)).open(); + }); + }); + + button_upload.on('click', event => spawnIconUpload(client)); + + update_local_icons([100, 200, 300, 500, 600]); + update_remote_icons(); + modal.htmlTag.find('.button-reload').on('click', () => update_remote_icons()); + button_select.prop("disabled", true).on('click', () => { + if(callback_icon) callback_icon(selected_icon); + modal.close(); + }); + modal.htmlTag.find(".button-select-no-icon").on('click', () => { + if(callback_icon) callback_icon(0); + modal.close(); + }); + modal.open(); +} + +interface UploadingIcon { + file: File; + state: "loading" | "valid" | "error"; + upload_state: "unset" | "uploading" | "uploaded" | "error"; + + html_tag?: JQuery; + image_element?: () => HTMLImageElement; + + loader: Promise; + + upload_icon: () => () => Promise; + upload_html_tag?: JQuery; + + icon_id: string; +} + +function handle_icon_upload(file: File, client: ConnectionHandler) : UploadingIcon { + const icon = {} as UploadingIcon; + icon.file = file; + icon.upload_state = "unset"; + + const file_too_big = () => { + console.error(tr("Failed to load file %s: File is too big!"), file.name); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
The given file is too big!", file.name)).open(); + icon.state = "error"; + }; + if(file.size > 1024 * 1024 * 512) { + file_too_big(); + } else if((file.size | 0) <= 0) { + console.error(tr("Failed to load file %s: Your browser does not support file sizes!"), file.name); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Your browser does not support file sizes!", file.name)).open(); + icon.state = "error"; + return; + } else { + icon.state = "loading"; + icon.loader = (async () => { + const reader = new FileReader(); + + try { + await new Promise((resolve, reject) => { + reader.onload = resolve; + reader.onerror = reject; + reader.readAsDataURL(file); + }); + } catch(error) { + console.log("Image failed to load (%o)", error); + console.error(tr("Failed to load file %s: Image failed to load"), file.name); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Failed to load image", file.name)).open(); + icon.state = "error"; + return; + } + + const result = reader.result as string; + if(typeof(result) !== "string") { + console.error(tr("Failed to load file %s: Result is not an media string (%o)"), file.name, result); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Result is not an media string", file.name)).open(); + icon.state = "error"; + return; + } + + + /* get the CRC32 sum */ + { + if(!result.startsWith("data:image/")) { + console.error(tr("Failed to load file %s: Invalid data media type (%o)"), file.name, result); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
File is not an image", file.name)).open(); + icon.state = "error"; + return; + } + const semi = result.indexOf(';'); + const type = result.substring(11, semi); + console.log(tr("Given image has type %s"), type); + if(!result.substr(semi + 1).startsWith("base64,")) { + console.error(tr("Failed to load file %s: Mimetype isnt base64 encoded (%o)"), file.name, result.substr(semi + 1)); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.
Decoder returned unknown result", file.name)).open(); + icon.state = "error"; + return; + } + + const crc = new crc32.Crc32(); + crc.update(arrayBufferBase64(result.substr(semi + 8))); + icon.icon_id = crc.digest(10); + } + + + const image = document.createElement("img"); + try { + await new Promise((resolve, reject) => { + image.onload = resolve; + image.onerror = reject; + image.src = result; + }); + } catch(error) { + console.log("Image failed to load (%o)", error); + console.error(tr("Failed to load file %s: Image failed to load"), file.name); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.{:br:}Failed to load image", file.name)).open(); + icon.state = "error"; + } + + const width_error = message => { + console.error(tr("Failed to load file %s: Invalid bounds: %s"), file.name, message); + createErrorModal(tr("Icon upload failed"), tra("Failed to upload icon {}.{:br:}Image is too large ({})", file.name, message)).open(); + icon.state = "error"; + }; + + if(!result.startsWith("data:image/svg+xml")) { + if(image.naturalWidth > 32 && image.naturalHeight > 32) { + width_error("width and height (max 32px). Given: " + image.naturalWidth + "x" + image.naturalHeight); + return; + } + if(image.naturalWidth > 32) { + width_error("width (max 32px)"); + return; + } + if(image.naturalHeight > 32) { + width_error("height (max 32px)"); + return; } } + console.log("Image loaded (%dx%d) %s (%s)", image.naturalWidth, image.naturalHeight, image.name, icon.icon_id); + icon.image_element = () => { + const image = document.createElement("img"); + image.src = result; + return image; + }; + icon.state = "valid"; + })(); + + icon.upload_icon = () => { + const create_progress_bar = () => { + const html = $.spawn("div").addClass("progress"); + const indicator = $.spawn("div").addClass("progress-bar bg-success progress-bar-striped progress-bar-animated"); + const message = $.spawn("div").addClass("progress-message"); + const set_value = value => { + indicator.stop(true, false).animate({width: value + "%"}, 250); + if(value === 100) + setTimeout(() => indicator.removeClass("progress-bar-striped progress-bar-animated"), 900) + }; + + return { + html_tag: html.append(indicator).append(message), + set_value: set_value, + set_message: msg => message.text(msg), + set_error: (msg: string) => { + let index = msg.lastIndexOf(':'); + message.text(index == -1 ? msg : msg.substring(index + 1)); + message.attr('title', msg); + set_value(100); + indicator.removeClass("bg-success").addClass("bg-danger"); + } + } + }; + + const container_image = $.spawn("div").addClass("container-icon"); + const bar = create_progress_bar(); + + const set_error = message => { + bar.set_value(100); + bar.set_message(tr("error: ") + message); + }; + + const html_tag = $.spawn("div") + .addClass("upload-entry") + .append(container_image) + .append(bar.html_tag); + + icon.upload_html_tag = html_tag; + + let icon_added = false; + if(icon.image_element) { + container_image.append(icon.image_element()); + icon_added = true; + } + + + bar.set_value(0); + bar.set_value(tr("waiting")); + + return async () => { + const time_begin = Date.now(); + + if(icon.state === "loading") { + bar.set_message(tr("Awaiting local processing")); + await icon.loader; + // @ts-ignore Could happen because the loader function updates the state + if(icon.state !== "valid") { + set_error(tr("local processing failed")); + icon.upload_state = "error"; + return; + } + } else if(icon.state === "error") { + set_error(tr("local processing error")); + icon.upload_state = "error"; + return; + } + if(!icon_added) + container_image.append(icon.image_element()); + + bar.set_value(25); + bar.set_message(tr("initializing")); + + let upload_key: UploadKey; + try { + upload_key = await client.fileManager.upload_file({ + channel: undefined, + channel_password: undefined, + name: '/icon_' + icon.icon_id, + overwrite: false, + path: '', + size: icon.file.size + }) + } catch(error) { + if(error instanceof CommandResult && error.id == ErrorID.FILE_ALREADY_EXISTS) { + if(!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) + await new Promise(resolve => setTimeout(resolve, 500 + Math.floor(Math.random() * 500))); + bar.set_message(tr("icon already exists")); + bar.set_value(100); + icon.upload_state = "uploaded"; + return; + } + console.error(tr("Failed to initialize upload: %o"), error); + bar.set_error(tr("failed to initialize upload")); + icon.upload_state = "error"; + return; + } + bar.set_value(50); + bar.set_message(tr("uploading")); + + const connection = spawn_upload_transfer(upload_key); + try { + await connection.put_data(icon.file) + } catch(error) { + console.error(tr("Icon upload failed for icon %s: %o"), icon.file.name, error); + if(typeof(error) === "string") + bar.set_error(tr("upload failed: ") + error); + else if(typeof(error.message) === "string") + bar.set_error(tr("upload failed: ") + error.message); + else + bar.set_error(tr("upload failed")); + icon.upload_state = "error"; + return; + } + + const time_end = Date.now(); + if(!settings.static_global(Settings.KEY_DISABLE_COSMETIC_SLOWDOWN, false)) + await new Promise(resolve => setTimeout(resolve, Math.max(0, 1000 - (time_end - time_begin)))); + bar.set_value(100); + bar.set_message(tr("upload completed")); + icon.upload_state = "uploaded"; + }; + }; + } + + return icon; +} + +export function spawnIconUpload(client: ConnectionHandler) { + const modal = createModal({ + header: tr("Upload Icons"), + footer: undefined, + body: () => $("#tmpl_icon_upload").renderTag(), + closeable: false, + + min_width: "20em" + }); + modal.htmlTag.find(".modal-body").addClass("modal-icon-upload"); + + const button_upload = modal.htmlTag.find(".button-upload"); + const button_delete = modal.htmlTag.find(".button-remove").prop("disabled", true); + const button_add = modal.htmlTag.find(".button-add"); + const button_upload_abort = modal.htmlTag.find(".button-upload-abort"); + const input_file = modal.htmlTag.find(".input-file-upload") as JQuery; + const container_icons = modal.htmlTag.find(".container-icons"); + + let selected_icon: UploadingIcon; + let icons: UploadingIcon[] = []; + + const update_upload_button = () => { + const icon_count = icons.filter(e => e.state === "valid").length; + button_upload.empty(); + tra("Upload icons ({})", icon_count).forEach(e => e.appendTo(button_upload)); + button_upload.prop("disabled", icon_count == 0); + }; + update_upload_button(); + + const add_icon = (icon: UploadingIcon) => { + icons.push(icon); + icon.loader.then(e => { + if(icon.state === "valid") { + const image = icon.image_element(); + const element = $.spawn("div") + .addClass("icon-container") + .append(image); + container_icons.append(icon.html_tag = element); + + element.on('click', event => { + container_icons.find(".selected").removeClass("selected"); + element.addClass("selected"); + + selected_icon = icon; + button_delete.prop("disabled", false); + }); + + update_upload_button(); + } }); + }; + button_delete.on('click', event => { + if(!selected_icon) + return; + icons = icons.filter(e => e !== selected_icon); + if(selected_icon.html_tag) + selected_icon.html_tag.detach(); + button_delete.prop("disabled", true); + update_upload_button(); + }); - container_icons.on('dragover', ((event: DragEvent) => { - event.stopPropagation(); - event.preventDefault(); - event.dataTransfer.dropEffect = 'copy'; - }) as any); - container_icons.on('drop', ((event: DragEvent) => { - event.stopPropagation(); - event.preventDefault(); - - for(let index = 0; index < event.dataTransfer.files.length; index++) { - const file = event.dataTransfer.files.item(index); + button_add.on('click', event => input_file.click()); + input_file.on('change', event => { + if(input_file[0].files.length > 0) { + for(let index = 0; index < input_file[0].files.length; index++) { + const file = input_file[0].files.item(index); { let duplicate = false; for(const icon of icons) - if(icon.file === file && icon.state !== "error") { + if(icon.file.name === file.name && icon.file.lastModified === file.lastModified && icon.state !== "error") { duplicate = true; break; } @@ -543,110 +522,137 @@ namespace Modals { add_icon(handle_icon_upload(file, client)); } - }) as any); - - /* upload process */ - { - const container_upload = modal.htmlTag.find(".container-upload"); - const container_error = container_upload.find(".container-error"); - const container_success = container_upload.find(".container-success"); - const container_process = container_upload.find(".container-process"); - const container_info = container_upload.find(".container-info"); - const container_statistics = container_upload.find(".uploaded-statistics"); - - const show_critical_error = message => { - container_error.find(".error-message").text(message); - container_error.removeClass("hidden"); - }; - - const finish_upload = () => { - icons = icons.filter(e => { - if(e.upload_state === "uploaded") { - e.html_tag.detach(); - return false; - } - return true; - }); - update_upload_button(); - button_upload.prop("disabled", false); - button_upload.prop("disabled", false); - container_upload.hide(); - container_error.addClass("hidden"); - container_error.addClass("hidden"); - modal.set_closeable(true); - }; - - - const execute_upload = async () => { - if(!client || !client.fileManager) { - show_critical_error(tr("Invalid client handle")); - return; - } - if(!client.connected) { - show_critical_error(tr("Not connected")); - return; - } - - let invoke_count = 0; - let succeed_count = 0; - let failed_count = 0; - - const uploads = icons.filter(e => e.state !== "error"); - - const executes: {icon: UploadingIcon, task: () => Promise}[] = []; - for(const icon of uploads) { - executes.push({ - icon: icon, - task: icon.upload_icon() - }); - - if(!icon.upload_html_tag) - continue; /* TODO: error? */ - icon.upload_html_tag.appendTo(container_process); - } - - const update_state = () => container_statistics.text(invoke_count + " | " + succeed_count + " | " + failed_count); - for(const execute of executes) { - invoke_count++; - update_state(); - try { - await execute.task(); - if(execute.icon.upload_state !== "uploaded") - throw "failed"; - succeed_count++; - } catch(error) { - failed_count++; - } - update_state(); - } - container_info.css({opacity: 1}).animate({opacity: 0}, 250, () => container_info.css({opacity: undefined}).hide()); - container_success.find(".message").html( - "Total icons: " + invoke_count + "
" + - "Succeeded icons: " + succeed_count + "
" + - "Failed icons: " + failed_count - ); - - container_success.removeClass("hidden"); - }; - - button_upload.on('click', event => { - modal.set_closeable(false); - button_upload.prop("disabled", true); - button_delete.prop("disabled", true); - button_add.prop("disabled", true); - container_process.empty(); - container_upload.show(); - execute_upload(); - }); - - button_upload_abort.on('click', event => finish_upload()); - - container_error.addClass("hidden"); - container_success.addClass("hidden"); - container_upload.hide(); } + }); - modal.open(); - modal.set_closeable(true); + container_icons.on('dragover', ((event: DragEvent) => { + event.stopPropagation(); + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + }) as any); + container_icons.on('drop', ((event: DragEvent) => { + event.stopPropagation(); + event.preventDefault(); + + for(let index = 0; index < event.dataTransfer.files.length; index++) { + const file = event.dataTransfer.files.item(index); + { + let duplicate = false; + + for(const icon of icons) + if(icon.file === file && icon.state !== "error") { + duplicate = true; + break; + } + if(duplicate) + continue; + } + + add_icon(handle_icon_upload(file, client)); + } + }) as any); + + /* upload process */ + { + const container_upload = modal.htmlTag.find(".container-upload"); + const container_error = container_upload.find(".container-error"); + const container_success = container_upload.find(".container-success"); + const container_process = container_upload.find(".container-process"); + const container_info = container_upload.find(".container-info"); + const container_statistics = container_upload.find(".uploaded-statistics"); + + const show_critical_error = message => { + container_error.find(".error-message").text(message); + container_error.removeClass("hidden"); + }; + + const finish_upload = () => { + icons = icons.filter(e => { + if(e.upload_state === "uploaded") { + e.html_tag.detach(); + return false; + } + return true; + }); + update_upload_button(); + button_upload.prop("disabled", false); + button_upload.prop("disabled", false); + container_upload.hide(); + container_error.addClass("hidden"); + container_error.addClass("hidden"); + modal.set_closeable(true); + }; + + + const execute_upload = async () => { + if(!client || !client.fileManager) { + show_critical_error(tr("Invalid client handle")); + return; + } + if(!client.connected) { + show_critical_error(tr("Not connected")); + return; + } + + let invoke_count = 0; + let succeed_count = 0; + let failed_count = 0; + + const uploads = icons.filter(e => e.state !== "error"); + + const executes: {icon: UploadingIcon, task: () => Promise}[] = []; + for(const icon of uploads) { + executes.push({ + icon: icon, + task: icon.upload_icon() + }); + + if(!icon.upload_html_tag) + continue; /* TODO: error? */ + icon.upload_html_tag.appendTo(container_process); + } + + const update_state = () => container_statistics.text(invoke_count + " | " + succeed_count + " | " + failed_count); + for(const execute of executes) { + invoke_count++; + update_state(); + try { + await execute.task(); + if(execute.icon.upload_state !== "uploaded") + throw "failed"; + succeed_count++; + } catch(error) { + failed_count++; + } + update_state(); + } + container_info.css({opacity: 1}).animate({opacity: 0}, 250, () => container_info.css({opacity: undefined}).hide()); + container_success.find(".message").html( + "Total icons: " + invoke_count + "
" + + "Succeeded icons: " + succeed_count + "
" + + "Failed icons: " + failed_count + ); + + container_success.removeClass("hidden"); + }; + + button_upload.on('click', event => { + modal.set_closeable(false); + button_upload.prop("disabled", true); + button_delete.prop("disabled", true); + button_add.prop("disabled", true); + container_process.empty(); + container_upload.show(); + execute_upload(); + }); + + button_upload_abort.on('click', event => finish_upload()); + + container_error.addClass("hidden"); + container_success.addClass("hidden"); + container_upload.hide(); } + + modal.open(); + modal.set_closeable(true); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalIdentity.ts b/shared/js/ui/modal/ModalIdentity.ts index 1c5f9296..1edab781 100644 --- a/shared/js/ui/modal/ModalIdentity.ts +++ b/shared/js/ui/modal/ModalIdentity.ts @@ -1,224 +1,227 @@ -namespace Modals { - export function spawnTeamSpeakIdentityImprove(identity: profiles.identities.TeaSpeakIdentity, name: string): Modal { - let modal: Modal; - let elapsed_timer: NodeJS.Timer; +import {createErrorModal, createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {formatMessage} from "tc-shared/ui/frames/chat"; - modal = createModal({ - header: tr("Improve identity"), - body: () => { - let template = $("#tmpl_settings-teamspeak_improve").renderTag({ - identity_name: name - }); - template = $.spawn("div").append(template); +export function spawnTeamSpeakIdentityImprove(identity: TeaSpeakIdentity, name: string): Modal { + let modal: Modal; + let elapsed_timer: NodeJS.Timer; - let active; - const button_start_stop = template.find(".button-start-stop"); - const button_close = template.find(".button-close"); - const input_current_level = template.find(".identity-level input"); - const input_target_level = template.find(".identity-target-level input"); - const input_threads = template.find(".threads input"); - const input_hash_rate = template.find(".hash-rate input"); - const input_elapsed = template.find(".time-elapsed input"); + modal = createModal({ + header: tr("Improve identity"), + body: () => { + let template = $("#tmpl_settings-teamspeak_improve").renderTag({ + identity_name: name + }); + template = $.spawn("div").append(template); - button_close.on('click', event => { - if (active) - button_start_stop.trigger('click'); + let active; + const button_start_stop = template.find(".button-start-stop"); + const button_close = template.find(".button-close"); + const input_current_level = template.find(".identity-level input"); + const input_target_level = template.find(".identity-target-level input"); + const input_threads = template.find(".threads input"); + const input_hash_rate = template.find(".hash-rate input"); + const input_elapsed = template.find(".time-elapsed input"); - if (modal.shown) - modal.close(); - }); + button_close.on('click', event => { + if (active) + button_start_stop.trigger('click'); - button_start_stop.on('click', event => { - button_start_stop - .toggleClass('btn-success', active) - .toggleClass('btn-danger', !active) - .text(active ? tr("Start") : tr("Stop")); - - input_threads.prop("disabled", !active); - input_target_level.prop("disabled", !active); - if (active) { - input_hash_rate.val(0); - clearInterval(elapsed_timer); - active = false; - return; - } - active = true; - input_hash_rate.val("nan"); - - const threads = parseInt(input_threads.val() as string); - const target_level = parseInt(input_target_level.val() as string); - if (target_level == 0) { - identity.improve_level(-1, threads, () => active, current_level => { - input_current_level.val(current_level); - }, hash_rate => { - input_hash_rate.val(hash_rate); - }).catch(error => { - console.error(error); - createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.
Error:") + error).open(); - if (active) - button_start_stop.trigger('click'); - }); - } else { - identity.improve_level(target_level, threads, () => active, current_level => { - input_current_level.val(current_level); - }, hash_rate => { - input_hash_rate.val(hash_rate); - }).then(success => { - if (success) { - identity.level().then(level => { - input_current_level.val(level); - createInfoModal(tr("Identity successfully improved"), MessageHelper.formatMessage(tr("Identity successfully improved to level {}"), level)).open(); - }).catch(error => { - input_current_level.val("error: " + error); - }); - } - if (active) - button_start_stop.trigger('click'); - }).catch(error => { - console.error(error); - createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.
Error:") + error).open(); - if (active) - button_start_stop.trigger('click'); - }); - } - - const begin = Date.now(); - elapsed_timer = setInterval(() => { - const time = (Date.now() - begin) / 1000; - let seconds = Math.floor(time % 60).toString(); - let minutes = Math.floor(time / 60).toString(); - - if (seconds.length < 2) - seconds = "0" + seconds; - - if (minutes.length < 2) - minutes = "0" + minutes; - - input_elapsed.val(minutes + ":" + seconds); - }, 1000); - }); - - - template.find(".identity-unique-id input").val(identity.uid()); - identity.level().then(level => { - input_current_level.val(level); - }).catch(error => { - input_current_level.val("error: " + error); - }); - tooltip(template); - return template.children(); - }, - footer: undefined, - width: 750 - }); - - modal.htmlTag.find(".modal-body").addClass("modal-identity-improve modal-green"); - modal.close_listener.push(() => modal.htmlTag.find(".button-close").trigger('click')); - modal.open(); - return modal; - } - - export function spawnTeamSpeakIdentityImport(callback: (identity: profiles.identities.TeaSpeakIdentity) => any): Modal { - let modal: Modal; - let selected_type: string; - let identities: {[key: string]: profiles.identities.TeaSpeakIdentity} = {}; - - modal = createModal({ - header: tr("Import identity"), - body: () => { - let template = $("#tmpl_settings-teamspeak_import").renderTag(); - - const button_import = template.find(".button-import"); - const button_file_select = template.find(".button-load-file"); - - const container_status = template.find(".container-status"); - const input_text = template.find(".input-identity-text"); - const input_file = template.find(".file-selector"); - - const set_status = (message: string | undefined, type: "error" | "loading" | "success") => { - container_status.toggleClass("hidden", !message); - if(message) { - container_status.toggleClass("error", type === "error"); - container_status.toggleClass("loading", type === "loading"); - container_status.find("a").text(message); - } - }; - - button_file_select.on('click', event => input_file.trigger('click')); - - template.find("input[name='type']").on('change', event => { - const type = (event.target as HTMLInputElement).value; - - button_file_select.prop("disabled", type !== "file"); - input_text.prop("disabled", type !== "text"); - - selected_type = type; - button_import.prop("disabled", !identities[type]); - }); - template.find("input[name='type'][value='file']").prop("checked", true).trigger("change"); - - const import_identity = (data: string, ini: boolean) => { - set_status(tr("Parsing identity"), "loading"); - profiles.identities.TeaSpeakIdentity.import_ts(data, ini).then(identity => { - identities[selected_type] = identity; - set_status("Identity parsed successfully.", "success"); - button_import.prop("disabled", false); - template.find(".success").show(); - }).catch(error => { - set_status(tr("Failed to parse identity: ") + error, "error"); - }); - }; - - /* file select button */ - input_file.on('change', event => { - const element = event.target as HTMLInputElement; - const file_reader = new FileReader(); - - set_status(tr("Loading file"), "loading"); - file_reader.onload = function () { - import_identity(file_reader.result as string, true); - }; - - file_reader.onerror = ev => { - console.error(tr("Failed to read give identity file: %o"), ev); - set_status(tr("Failed to read the identity file."), "error"); - return; - }; - - if (element.files && element.files.length > 0) - file_reader.readAsText(element.files[0]); - }); - - input_text.on('change keyup', event => { - const text = input_text.val() as string; - if(!text) { - set_status("", "success"); - return; - } - - if(text.indexOf('V') == -1) { - set_status(tr("Invalid identity string"), "error"); - return; - } - - import_identity(text, false); - }); - - button_import.on('click', event => { + if (modal.shown) modal.close(); - callback(identities[selected_type]); + }); + + button_start_stop.on('click', event => { + button_start_stop + .toggleClass('btn-success', active) + .toggleClass('btn-danger', !active) + .text(active ? tr("Start") : tr("Stop")); + + input_threads.prop("disabled", !active); + input_target_level.prop("disabled", !active); + if (active) { + input_hash_rate.val(0); + clearInterval(elapsed_timer); + active = false; + return; + } + active = true; + input_hash_rate.val("nan"); + + const threads = parseInt(input_threads.val() as string); + const target_level = parseInt(input_target_level.val() as string); + if (target_level == 0) { + identity.improve_level(-1, threads, () => active, current_level => { + input_current_level.val(current_level); + }, hash_rate => { + input_hash_rate.val(hash_rate); + }).catch(error => { + console.error(error); + createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.
Error:") + error).open(); + if (active) + button_start_stop.trigger('click'); + }); + } else { + identity.improve_level(target_level, threads, () => active, current_level => { + input_current_level.val(current_level); + }, hash_rate => { + input_hash_rate.val(hash_rate); + }).then(success => { + if (success) { + identity.level().then(level => { + input_current_level.val(level); + createInfoModal(tr("Identity successfully improved"), formatMessage(tr("Identity successfully improved to level {}"), level)).open(); + }).catch(error => { + input_current_level.val("error: " + error); + }); + } + if (active) + button_start_stop.trigger('click'); + }).catch(error => { + console.error(error); + createErrorModal(tr("Failed to improve identity"), tr("Failed to improve identity.
Error:") + error).open(); + if (active) + button_start_stop.trigger('click'); + }); + } + + const begin = Date.now(); + elapsed_timer = setInterval(() => { + const time = (Date.now() - begin) / 1000; + let seconds = Math.floor(time % 60).toString(); + let minutes = Math.floor(time / 60).toString(); + + if (seconds.length < 2) + seconds = "0" + seconds; + + if (minutes.length < 2) + minutes = "0" + minutes; + + input_elapsed.val(minutes + ":" + seconds); + }, 1000); + }); + + + template.find(".identity-unique-id input").val(identity.uid()); + identity.level().then(level => { + input_current_level.val(level); + }).catch(error => { + input_current_level.val("error: " + error); + }); + tooltip.initialize(template); + return template.children(); + }, + footer: undefined, + width: 750 + }); + + modal.htmlTag.find(".modal-body").addClass("modal-identity-improve modal-green"); + modal.close_listener.push(() => modal.htmlTag.find(".button-close").trigger('click')); + modal.open(); + return modal; +} + +export function spawnTeamSpeakIdentityImport(callback: (identity: TeaSpeakIdentity) => any): Modal { + let modal: Modal; + let selected_type: string; + let identities: {[key: string]: TeaSpeakIdentity} = {}; + + modal = createModal({ + header: tr("Import identity"), + body: () => { + let template = $("#tmpl_settings-teamspeak_import").renderTag(); + + const button_import = template.find(".button-import"); + const button_file_select = template.find(".button-load-file"); + + const container_status = template.find(".container-status"); + const input_text = template.find(".input-identity-text"); + const input_file = template.find(".file-selector"); + + const set_status = (message: string | undefined, type: "error" | "loading" | "success") => { + container_status.toggleClass("hidden", !message); + if(message) { + container_status.toggleClass("error", type === "error"); + container_status.toggleClass("loading", type === "loading"); + container_status.find("a").text(message); + } + }; + + button_file_select.on('click', event => input_file.trigger('click')); + + template.find("input[name='type']").on('change', event => { + const type = (event.target as HTMLInputElement).value; + + button_file_select.prop("disabled", type !== "file"); + input_text.prop("disabled", type !== "text"); + + selected_type = type; + button_import.prop("disabled", !identities[type]); + }); + template.find("input[name='type'][value='file']").prop("checked", true).trigger("change"); + + const import_identity = (data: string, ini: boolean) => { + set_status(tr("Parsing identity"), "loading"); + TeaSpeakIdentity.import_ts(data, ini).then(identity => { + identities[selected_type] = identity; + set_status("Identity parsed successfully.", "success"); + button_import.prop("disabled", false); + template.find(".success").show(); + }).catch(error => { + set_status(tr("Failed to parse identity: ") + error, "error"); }); + }; - set_status("", "success"); - button_import.prop("disabled", true); - return template.children(); - }, - footer: undefined, - width: 750 - }); + /* file select button */ + input_file.on('change', event => { + const element = event.target as HTMLInputElement; + const file_reader = new FileReader(); - modal.htmlTag.find(".modal-body").addClass("modal-identity-import modal-green"); - modal.open(); - return modal; - } + set_status(tr("Loading file"), "loading"); + file_reader.onload = function () { + import_identity(file_reader.result as string, true); + }; + + file_reader.onerror = ev => { + console.error(tr("Failed to read give identity file: %o"), ev); + set_status(tr("Failed to read the identity file."), "error"); + return; + }; + + if (element.files && element.files.length > 0) + file_reader.readAsText(element.files[0]); + }); + + input_text.on('change keyup', event => { + const text = input_text.val() as string; + if(!text) { + set_status("", "success"); + return; + } + + if(text.indexOf('V') == -1) { + set_status(tr("Invalid identity string"), "error"); + return; + } + + import_identity(text, false); + }); + + button_import.on('click', event => { + modal.close(); + callback(identities[selected_type]); + }); + + set_status("", "success"); + button_import.prop("disabled", true); + return template.children(); + }, + footer: undefined, + width: 750 + }); + + modal.htmlTag.find(".modal-body").addClass("modal-identity-import modal-green"); + modal.open(); + return modal; } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalInvite.ts b/shared/js/ui/modal/ModalInvite.ts index 45a7acaa..b0a17db7 100644 --- a/shared/js/ui/modal/ModalInvite.ts +++ b/shared/js/ui/modal/ModalInvite.ts @@ -1,217 +1,216 @@ -/// -/// -/// +import {settings, Settings} from "tc-shared/settings"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {ServerAddress} from "tc-shared/ui/server"; -namespace Modals { - type URLGeneratorSettings = { - flag_direct: boolean, - flag_resolved: boolean - } +type URLGeneratorSettings = { + flag_direct: boolean, + flag_resolved: boolean +} - const DefaultGeneratorSettings: URLGeneratorSettings = { - flag_direct: true, - flag_resolved: false - }; +const DefaultGeneratorSettings: URLGeneratorSettings = { + flag_direct: true, + flag_resolved: false +}; - type URLGenerator = { - generate: (properties: { - address: ServerAddress, - resolved_address: ServerAddress - } & URLGeneratorSettings) => string; +type URLGenerator = { + generate: (properties: { + address: ServerAddress, + resolved_address: ServerAddress + } & URLGeneratorSettings) => string; - setting_available: (key: keyof URLGeneratorSettings) => boolean; - }; + setting_available: (key: keyof URLGeneratorSettings) => boolean; +}; - const build_url = (base, params) => { - if(Object.keys(params).length == 0) - return base; +const build_url = (base, params) => { + if(Object.keys(params).length == 0) + return base; - return base + "?" + Object.keys(params) - .map(e => e + "=" + encodeURIComponent(params[e])) - .join("&"); - }; + return base + "?" + Object.keys(params) + .map(e => e + "=" + encodeURIComponent(params[e])) + .join("&"); +}; - //TODO: Server password - const url_generators: {[key: string]:URLGenerator} = { - "tea-web": { - generate: properties => { - const address = properties.resolved_address ? properties.resolved_address : properties.address; - const address_str = address.host + (address.port === 9987 ? "" : address.port); - const parameter = "connect_default=" + (properties.flag_direct ? 1 : 0) + "&connect_address=" + encodeURIComponent(address_str); - - let pathbase = ""; - if(document.location.protocol !== 'https:') { - /* - * Seems to be a test environment or the TeaClient for localhost where we dont have to use https. - */ - pathbase = "https://web.teaspeak.de/"; - } else if(document.location.hostname === "localhost" || document.location.host.startsWith("127.")) { - pathbase = "https://web.teaspeak.de/"; - } else { - pathbase = document.location.origin + document.location.pathname; - } - return pathbase + "?" + parameter; - }, - setting_available: setting => { - return { - flag_direct: true, - flag_resolved: true - }[setting] || false; - } - }, - "tea-client": { - generate: properties => { - const address = properties.resolved_address ? properties.resolved_address : properties.address; - - - let parameters = { - connect_default: properties.flag_direct ? 1 : 0 - }; - - if(address.port != 9987) - parameters["port"] = address.port; - - return build_url("teaclient://" + address.host + "/", parameters); - }, - setting_available: setting => { - return { - flag_direct: true, - flag_resolved: true - }[setting] || false; - } - }, - "teamspeak": { - generate: properties => { - const address = properties.resolved_address ? properties.resolved_address : properties.address; - - let parameters = {}; - if(address.port != 9987) - parameters["port"] = address.port; +//TODO: Server password +const url_generators: {[key: string]:URLGenerator} = { + "tea-web": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + const address_str = address.host + (address.port === 9987 ? "" : address.port); + const parameter = "connect_default=" + (properties.flag_direct ? 1 : 0) + "&connect_address=" + encodeURIComponent(address_str); + let pathbase = ""; + if(document.location.protocol !== 'https:') { /* - ts3server://? - port=9987 - nickname=UserNickname - password=serverPassword - channel=MyDefaultChannel - cid=channelID - channelpassword=defaultChannelPassword - token=TokenKey - addbookmark=MyBookMarkLabel - */ - return build_url("ts3server://" + address.host + "/", parameters); - }, - setting_available: setting => { - return { - flag_direct: false, - flag_resolved: true - }[setting] || false; + * Seems to be a test environment or the TeaClient for localhost where we dont have to use https. + */ + pathbase = "https://web.teaspeak.de/"; + } else if(document.location.hostname === "localhost" || document.location.host.startsWith("127.")) { + pathbase = "https://web.teaspeak.de/"; + } else { + pathbase = document.location.origin + document.location.pathname; } + return pathbase + "?" + parameter; + }, + setting_available: setting => { + return { + flag_direct: true, + flag_resolved: true + }[setting] || false; } + }, + "tea-client": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + + + let parameters = { + connect_default: properties.flag_direct ? 1 : 0 + }; + + if(address.port != 9987) + parameters["port"] = address.port; + + return build_url("teaclient://" + address.host + "/", parameters); + }, + setting_available: setting => { + return { + flag_direct: true, + flag_resolved: true + }[setting] || false; + } + }, + "teamspeak": { + generate: properties => { + const address = properties.resolved_address ? properties.resolved_address : properties.address; + + let parameters = {}; + if(address.port != 9987) + parameters["port"] = address.port; + + /* + ts3server://? + port=9987 + nickname=UserNickname + password=serverPassword + channel=MyDefaultChannel + cid=channelID + channelpassword=defaultChannelPassword + token=TokenKey + addbookmark=MyBookMarkLabel + */ + return build_url("ts3server://" + address.host + "/", parameters); + }, + setting_available: setting => { + return { + flag_direct: false, + flag_resolved: true + }[setting] || false; + } + } +}; + +export function spawnInviteEditor(connection: ConnectionHandler) { + let modal: Modal; + modal = createModal({ + header: tr("Invite URL creator"), + body: () => { + let template = $("#tmpl_invite").renderTag(); + + template.find(".button-close").on('click', event => modal.close()); + return template; + }, + footer: undefined, + min_width: "20em", + width: "50em" + }); + + modal.htmlTag.find(".modal-body").addClass("modal-invite"); + + const button_copy = modal.htmlTag.find(".button-copy"); + const input_type = modal.htmlTag.find(".property-type select"); + const label_output = modal.htmlTag.find(".text-output"); + + const invite_settings = [ + { + key: "flag_direct", + node: modal.htmlTag.find(".flag-direct-connect input"), + value: node => node.prop('checked'), + set_value: (node, value) => node.prop('checked', value == "1"), + disable: (node, flag) => node.prop('disabled', flag) + .firstParent('.checkbox').toggleClass('disabled', flag) + }, + + { + key: "flag_resolved", + node: modal.htmlTag.find(".flag-resolved-address input"), + value: node => node.prop('checked'), + set_value: (node, value) => node.prop('checked', value == "1"), + disable: (node, flag) => node.prop('disabled', flag) + .firstParent('.checkbox').toggleClass('disabled', flag) + } + ]; + + const update_buttons = () => { + const generator = url_generators[input_type.val() as string]; + if(!generator) { + for(const s of invite_settings) + s.disable(s.node, true); + return; + } + + for(const s of invite_settings) + s.disable(s.node, !generator.setting_available(s.key as any)); }; - export function spawnInviteEditor(connection: ConnectionHandler) { - let modal: Modal; - modal = createModal({ - header: tr("Invite URL creator"), - body: () => { - let template = $("#tmpl_invite").renderTag(); - - template.find(".button-close").on('click', event => modal.close()); - return template; - }, - footer: undefined, - min_width: "20em", - width: "50em" - }); - - modal.htmlTag.find(".modal-body").addClass("modal-invite"); - - const button_copy = modal.htmlTag.find(".button-copy"); - const input_type = modal.htmlTag.find(".property-type select"); - const label_output = modal.htmlTag.find(".text-output"); - - const invite_settings = [ - { - key: "flag_direct", - node: modal.htmlTag.find(".flag-direct-connect input"), - value: node => node.prop('checked'), - set_value: (node, value) => node.prop('checked', value == "1"), - disable: (node, flag) => node.prop('disabled', flag) - .firstParent('.checkbox').toggleClass('disabled', flag) - }, - - { - key: "flag_resolved", - node: modal.htmlTag.find(".flag-resolved-address input"), - value: node => node.prop('checked'), - set_value: (node, value) => node.prop('checked', value == "1"), - disable: (node, flag) => node.prop('disabled', flag) - .firstParent('.checkbox').toggleClass('disabled', flag) - } - ]; - - const update_buttons = () => { - const generator = url_generators[input_type.val() as string]; - if(!generator) { - for(const s of invite_settings) - s.disable(s.node, true); - return; - } - - for(const s of invite_settings) - s.disable(s.node, !generator.setting_available(s.key as any)); - }; - - const update_link = () => { - const generator = url_generators[input_type.val() as string]; - if(!generator) { - button_copy.prop('disabled', true); - label_output.text(tr("Missing link generator")); - return; - } - button_copy.prop('disabled', false); - - const properties = { - address: connection.channelTree.server.remote_address, - resolved_address: connection.channelTree.client.serverConnection.remote_address() - }; - for(const s of invite_settings) - properties[s.key] = s.value(s.node); - - label_output.text(generator.generate(properties as any)); - }; - - - for(const s of invite_settings) { - s.node.on('change keyup', () => { - settings.changeGlobal(Settings.FN_INVITE_LINK_SETTING(s.key), s.value(s.node)); - update_link() - }); - - s.set_value(s.node, settings.global(Settings.FN_INVITE_LINK_SETTING(s.key), DefaultGeneratorSettings[s.key])); + const update_link = () => { + const generator = url_generators[input_type.val() as string]; + if(!generator) { + button_copy.prop('disabled', true); + label_output.text(tr("Missing link generator")); + return; } + button_copy.prop('disabled', false); - input_type.on('change', () => { - settings.changeGlobal(Settings.KEY_LAST_INVITE_LINK_TYPE, input_type.val()); - update_buttons(); - update_link(); - }).val(settings.global(Settings.KEY_LAST_INVITE_LINK_TYPE)); + const properties = { + address: connection.channelTree.server.remote_address, + resolved_address: connection.channelTree.client.serverConnection.remote_address() + }; + for(const s of invite_settings) + properties[s.key] = s.value(s.node); - button_copy.on('click', event => { - label_output.select(); - document.execCommand('copy'); + label_output.text(generator.generate(properties as any)); + }; + + + for(const s of invite_settings) { + s.node.on('change keyup', () => { + settings.changeGlobal(Settings.FN_INVITE_LINK_SETTING(s.key), s.value(s.node)); + update_link() }); - update_buttons(); - update_link(); - modal.open(); + s.set_value(s.node, settings.global(Settings.FN_INVITE_LINK_SETTING(s.key), DefaultGeneratorSettings[s.key])); } + + input_type.on('change', () => { + settings.changeGlobal(Settings.KEY_LAST_INVITE_LINK_TYPE, input_type.val()); + update_buttons(); + update_link(); + }).val(settings.global(Settings.KEY_LAST_INVITE_LINK_TYPE)); + + button_copy.on('click', event => { + label_output.select(); + document.execCommand('copy'); + }); + + update_buttons(); + update_link(); + modal.open(); } /* - - - - */ \ No newline at end of file + + + +*/ \ No newline at end of file diff --git a/shared/js/ui/modal/ModalKeySelect.ts b/shared/js/ui/modal/ModalKeySelect.ts index 8ad60b2d..5bc0cce7 100644 --- a/shared/js/ui/modal/ModalKeySelect.ts +++ b/shared/js/ui/modal/ModalKeySelect.ts @@ -1,50 +1,53 @@ -namespace Modals { - export function spawnKeySelect(callback: (key?: ppt.KeyEvent) => void) { - let modal = createModal({ - header: tr("Select a key"), - body: () => $("#tmpl_key_select").renderTag().children(), - footer: null, +import {createModal} from "tc-shared/ui/elements/Modal"; +import {EventType, key_description, KeyEvent} from "tc-shared/PPTListener"; +import * as loader from "tc-loader"; +import * as ppt from "tc-backend/ppt"; - width: "", - closeable: false - }); +export function spawnKeySelect(callback: (key?: KeyEvent) => void) { + let modal = createModal({ + header: tr("Select a key"), + body: () => $("#tmpl_key_select").renderTag().children(), + footer: null, - const container_key = modal.htmlTag.find(".container-key a"); - const button_save = modal.htmlTag.find(".button-save"); - const button_cancel = modal.htmlTag.find(".button-cancel"); + width: "", + closeable: false + }); - let current_key_age: number; - let last_key: ppt.KeyEvent; - let current_key: ppt.KeyEvent; - const listener = (event: ppt.KeyEvent) => { - if(event.type === ppt.EventType.KEY_PRESS) { - //console.log(tr("Key select got key press for %o"), event); - last_key = current_key; - current_key = event; - current_key_age = Date.now(); + const container_key = modal.htmlTag.find(".container-key a"); + const button_save = modal.htmlTag.find(".button-save"); + const button_cancel = modal.htmlTag.find(".button-cancel"); - container_key.text(ppt.key_description(event)); - button_save.prop("disabled", false); - } - }; + let current_key_age: number; + let last_key: KeyEvent; + let current_key: KeyEvent; + const listener = (event: KeyEvent) => { + if(event.type === EventType.KEY_PRESS) { + //console.log(tr("Key select got key press for %o"), event); + last_key = current_key; + current_key = event; + current_key_age = Date.now(); + + container_key.text(key_description(event)); + button_save.prop("disabled", false); + } + }; - button_save.on('click', () => { - if(!app.is_web()) { - /* Because pressing the close button is also a mouse action */ - if(current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE2") - current_key = last_key; - } + button_save.on('click', () => { + if(loader.version().type !== "web") { + /* Because pressing the close button is also a mouse action */ + if(current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE2") + current_key = last_key; + } - callback(current_key); - modal.close(); - }).prop("disabled", true); - button_cancel.on('click', () => modal.close()); + callback(current_key); + modal.close(); + }).prop("disabled", true); + button_cancel.on('click', () => modal.close()); - ppt.register_key_listener(listener); - modal.close_listener.push(() => ppt.unregister_key_listener(listener)); + ppt.register_key_listener(listener); + modal.close_listener.push(() => ppt.unregister_key_listener(listener)); - modal.htmlTag.find(".modal-body").addClass("modal-keyselect modal-green"); - modal.open(); - } + modal.htmlTag.find(".modal-body").addClass("modal-keyselect modal-green"); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalMusicManage.ts b/shared/js/ui/modal/ModalMusicManage.ts index 1736ca05..2c47375a 100644 --- a/shared/js/ui/modal/ModalMusicManage.ts +++ b/shared/js/ui/modal/ModalMusicManage.ts @@ -1,1774 +1,1449 @@ -/// -/// -/// +import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {MusicClientEntry} from "tc-shared/ui/client"; +import {Registry} from "tc-shared/events"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import {tra} from "tc-shared/i18n/localize"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import { modal } from "tc-shared/events"; +import * as i18nc from "tc-shared/i18n/country"; +import {find} from "tc-shared/permission/PermissionManager"; +import ServerGroup = find.ServerGroup; +import * as htmltags from "tc-shared/ui/htmltags"; -namespace Modals { - export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) { - const ev_registry = new events.Registry(); - ev_registry.enable_debug("music-manage"); - //dummy_controller(ev_registry); - permission_controller(ev_registry, bot, client); +export function openMusicManage(client: ConnectionHandler, bot: MusicClientEntry) { + const ev_registry = new Registry(); + ev_registry.enable_debug("music-manage"); + //dummy_controller(ev_registry); + permission_controller(ev_registry, bot, client); - let modal = createModal({ - header: tr(tr("Playlist Manage")), - body: () => build_modal(ev_registry), - footer: null, + let modal = createModal({ + header: tr(tr("Playlist Manage")), + body: () => build_modal(ev_registry), + footer: null, - min_width: "35em", - closeable: true - }); - modal.htmlTag.find(".modal-body").addClass("modal-music-manage"); + min_width: "35em", + closeable: true + }); + modal.htmlTag.find(".modal-body").addClass("modal-music-manage"); - /* "controller" */ - { + /* "controller" */ + { - } - - modal.open(); } - function permission_controller(event_registry: events.Registry, bot: MusicClientEntry, client: ConnectionHandler) { - const error_msg = error => { - if(error instanceof CommandResult) { - if(error.id === ErrorID.PERMISSION_ERROR) { - const permission = client.permissions.resolveInfo(error.json["failed_permid"]); - return tr("failed on permission ") + (permission ? permission.name : tr("unknown")); - } - return error.extra_message || error.message; - } else if(typeof error === "string") - return error; - else - return tr("command error"); - }; + modal.open(); +} - { - event_registry.on("query_playlist_status", event => { - const playlist_id = bot.properties.client_playlist_id; - client.serverConnection.command_helper.request_playlist_info(playlist_id).then(result => { - event_registry.fire("playlist_status", { - status: "success", - data: { - replay_mode: result.playlist_replay_mode, - finished: result.playlist_flag_finished, - delete_played: result.playlist_flag_delete_played, - notify_song_change: bot.properties.client_flag_notify_song_change, - max_size: result.playlist_max_songs - } - }); - }).catch(error => { - event_registry.fire("playlist_status", { - status: "error", - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to query playlist info for playlist %d: %o"), playlist_id, error); - }); - }); +function permission_controller(event_registry: Registry, bot: MusicClientEntry, client: ConnectionHandler) { + const error_msg = error => { + if(error instanceof CommandResult) { + if(error.id === ErrorID.PERMISSION_ERROR) { + const permission = client.permissions.resolveInfo(error.json["failed_permid"]); + return tr("failed on permission ") + (permission ? permission.name : tr("unknown")); + } + return error.extra_message || error.message; + } else if(typeof error === "string") + return error; + else + return tr("command error"); + }; - event_registry.on("set_playlist_status", event => { - const playlist_id = bot.properties.client_playlist_id; - const property_map = { - "replay_mode": "playlist_replay_mode", - "finished": "playlist_flag_finished", - "delete_played": "playlist_flag_delete_played", - "max_size": "playlist_max_songs" - }; - - Promise.resolve().then(() => { - if(event.key === "notify_song_change") { - return client.serverConnection.send_command("clientedit", { - clid: bot.clientId(), - client_flag_notify_song_change: event.value - }); - } else { - const property = property_map[event.key]; - if(!property) return Promise.reject(tr("unknown property")); - - const data = { - playlist_id: playlist_id - }; - data[property] = event.value; - return client.serverConnection.send_command("playlistedit", data); + { + event_registry.on("query_playlist_status", event => { + const playlist_id = bot.properties.client_playlist_id; + client.serverConnection.command_helper.request_playlist_info(playlist_id).then(result => { + event_registry.fire("playlist_status", { + status: "success", + data: { + replay_mode: result.playlist_replay_mode, + finished: result.playlist_flag_finished, + delete_played: result.playlist_flag_delete_played, + notify_song_change: bot.properties.client_flag_notify_song_change, + max_size: result.playlist_max_songs } - }).then(() => { - event_registry.fire("set_playlist_status_result", { - status: "success", - key: event.key, - value: event.value - }); - }).catch(error => { - event_registry.fire("set_playlist_status_result", { - status: "error", - key: event.key, - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to change playlist status %s for playlist %d: %o"), event.key, playlist_id, error); }); + }).catch(error => { + event_registry.fire("playlist_status", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to query playlist info for playlist %d: %o"), playlist_id, error); }); + }); - event_registry.on("query_bot_status", event => { - setTimeout(() => { - event_registry.fire("bot_status", { - status: "success", - data: { - channel_commander: bot.properties.client_is_channel_commander, - volume: bot.properties.player_volume, - description: bot.properties.client_description, - default_country_code: ( - !bot.channelTree ? undefined : - !bot.channelTree.server ? undefined : bot.channelTree.server.properties.virtualserver_country_code) || "DE", - country_code: bot.properties.client_country, - name: bot.properties.client_nickname, - priority_speaker: bot.properties.client_is_priority_speaker, + event_registry.on("set_playlist_status", event => { + const playlist_id = bot.properties.client_playlist_id; + const property_map = { + "replay_mode": "playlist_replay_mode", + "finished": "playlist_flag_finished", + "delete_played": "playlist_flag_delete_played", + "max_size": "playlist_max_songs" + }; - bot_type: bot.properties.client_bot_type, - client_platform: bot.properties.client_platform, - client_version: bot.properties.client_version, - uptime_mode: bot.properties.client_uptime_mode - } + Promise.resolve().then(() => { + if(event.key === "notify_song_change") { + return client.serverConnection.send_command("clientedit", { + clid: bot.clientId(), + client_flag_notify_song_change: event.value }); - }, 0); - }); - - event_registry.on("set_bot_status", event => { - const property_map = { - "channel_commander": "client_is_channel_commander", - "volume": "player_volume", - "description": "client_description", - "country_code": "client_country", - "name": "client_nickname", - "priority_speaker": "client_is_priority_speaker", - - "bot_type": "client_bot_type", - "client_platform": "client_platform", - "client_version": "client_version", - "uptime_mode": "client_uptime_mode" - }; - - - Promise.resolve().then(() => { + } else { const property = property_map[event.key]; if(!property) return Promise.reject(tr("unknown property")); const data = { - clid: bot.clientId() + playlist_id: playlist_id }; data[property] = event.value; - return client.serverConnection.send_command("clientedit", data); - }).then(() => { - event_registry.fire("set_bot_status_result", { - status: "success", - key: event.key, - value: event.value - }); - }).catch(error => { - event_registry.fire("set_bot_status_result", { - status: "error", - key: event.key, - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to change bot setting %s: %o"), event.key, error); + return client.serverConnection.send_command("playlistedit", data); + } + }).then(() => { + event_registry.fire("set_playlist_status_result", { + status: "success", + key: event.key, + value: event.value }); - }); - } - - /* permissions */ - { - event_registry.on("query_general_permissions", event => { - const playlist_id = bot.properties.client_playlist_id; - client.permissions.requestPlaylistPermissions(playlist_id).then(result => { - const permissions = {}; - for(const permission of result) - if(permission.hasValue()) - permissions[permission.type.name] = permission.value; - event_registry.fire("general_permissions", { - status: "success", - permissions: permissions - }); - }).catch(error => { - event_registry.fire("general_permissions", { - status: "error", - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to query playlist general permissions for playlist %d: %o"), playlist_id, error); + }).catch(error => { + event_registry.fire("set_playlist_status_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) }); + log.error(LogCategory.CLIENT, tr("Failed to change playlist status %s for playlist %d: %o"), event.key, playlist_id, error); }); - - event_registry.on("set_general_permission", event => { - const playlist_id = bot.properties.client_playlist_id; - - client.serverConnection.send_command("playlistaddperm", { - playlist_id: playlist_id, - permsid: event.key, - permvalue: event.value, - permskip: false, - permnegated: false - }).then(() => { - event_registry.fire("set_general_permission_result", { - key: event.key, - status: "success", - value: event.value - }); - }).catch(error => { - event_registry.fire("set_general_permission_result", { - status: "error", - key: event.key, - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to set playlist general permissions for playlist %d and permission %d: %o"), playlist_id, event.key, error); - }); - }); - - event_registry.on("query_client_permissions", event => { - const playlist_id = bot.properties.client_playlist_id; - const client_id = event.client_database_id; - client.permissions.requestPlaylistClientPermissions(playlist_id, client_id).then(result => { - const permissions = {}; - for(const permission of result) - if(permission.hasValue()) - permissions[permission.type.name] = permission.value; - event_registry.fire("client_permissions", { - status: "success", - client_database_id: event.client_database_id, - permissions: permissions - }); - }).catch(error => { - event_registry.fire("client_permissions", { - status: "error", - client_database_id: event.client_database_id, - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to query playlist client permissions for playlist %d and client %d: %o"), playlist_id, client_id, error); - }); - }); - - event_registry.on("set_client_permission", event => { - const playlist_id = bot.properties.client_playlist_id; - const client_id = event.client_database_id; - - client.serverConnection.send_command("playlistclientaddperm", { - playlist_id: playlist_id, - cldbid: client_id, - - permsid: event.key, - permvalue: event.value, - permskip: false, - permnegated: false - }).then(() => { - event_registry.fire("set_client_permission_result", { - key: event.key, - status: "success", - client_database_id: client_id, - value: event.value - }); - }).catch(error => { - event_registry.fire("set_client_permission_result", { - status: "error", - key: event.key, - client_database_id: client_id, - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to set playlist client permissions for playlist %d, permission %d and client id %d: %o"), playlist_id, event.key, client_id, error); - }); - }); - - event_registry.on("query_special_clients", event => { - const playlist_id = bot.properties.client_playlist_id; - client.serverConnection.command_helper.request_playlist_client_list(playlist_id).then(clients => { - return client.serverConnection.command_helper.info_from_cldbid(...clients); - }).then(clients => { - event_registry.fire("special_client_list", { - status: "success", - clients: clients.map(e => { - return { - name: e.client_nickname, - unique_id: e.client_unique_id, - database_id: e.client_database_id - } - }) - }); - }).catch(error => { - event_registry.fire("special_client_list", { - status: "error", - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to query special client list for playlist %d: %o"), playlist_id, error); - }) - }); - - event_registry.on("search_client", event => { - if(!event.text) return; - - const text = event.text; - Promise.resolve().then(() => { - let is_uuid = false; - try { - is_uuid = atob(text).length === 32; - } catch(e) {} - if(is_uuid) { - return client.serverConnection.command_helper.info_from_uid(text); - } else if(text.match(/^[0-9]{1,7}$/) && !isNaN(parseInt(text))) { - return client.serverConnection.command_helper.info_from_cldbid(parseInt(text)); - } else { - //TODO: Database name lookup? - return Promise.reject("no results"); - } - }).then(result => { - if(result.length) { - const client = result[0]; - event_registry.fire("search_client_result", { - status: "success", - client: { - name: client.client_nickname, - unique_id: client.client_unique_id, - database_id: client.client_database_id - } - }); - } else { - event_registry.fire("search_client_result", { - status: "empty" - }); - } - }).catch(error => { - event_registry.fire("search_client_result", { - status: "error", - error_msg: error_msg(error) - }); - log.error(LogCategory.CLIENT, tr("Failed to lookup search text \"%s\": %o"), text, error); - }); - }); - - event_registry.on("query_group_permissions", event => { - client.permissions.find_permission(event.permission_name).then(result => { - let groups = []; - for(const e of result) { - if(e.type !== "server_group") continue; - - const group = client.groups.serverGroup((e as permissions.find.ServerGroup).group_id); - if(!group) continue; - - groups.push({ - name: group.name, - value: e.value, - id: group.id - }); - } - - event_registry.fire("group_permissions", { - status: "success", - groups: groups, - permission_name: event.permission_name - }); - }).catch(error => { - event_registry.fire("group_permissions", { - status: "error", - error_msg: error_msg(error), - permission_name: event.permission_name - }); - log.error(LogCategory.CLIENT, tr("Failed to execute permfind for permission %s: %o"), event.permission_name, error); - }); - }); - } - } - - function dummy_controller(event_registry: events.Registry) { - /* settings */ - { - event_registry.on("query_bot_status", event => { - setTimeout(() => { - event_registry.fire("bot_status", { - status: "success", - data: { - name: "Another TeaSpeak bot", - country_code: "DE", - default_country_code: "GB", - channel_commander: false, - description: "Hello World", - priority_speaker: true, - volume: 66, - - uptime_mode: 0, - client_version: "Version", - client_platform: "Platform", - bot_type: 0 - } - }) - }); - }); - - - event_registry.on("query_playlist_status", event => { - setTimeout(() => { - event_registry.fire("playlist_status", { - status: "success", - data: { - max_size: 55, - notify_song_change: true, - delete_played: false, - finished: false, - replay_mode: 2 - } - }) - }); - }); - } - - /* permissions */ - { - event_registry.on("query_special_clients", event => { - setTimeout(() => { - event_registry.fire("special_client_list", { - status: "success", - clients: [{ - name: "WolverinDEV", - database_id: 1, - unique_id: "abd" - }, { - name: "WolverinDEV 2", - database_id: 2, - unique_id: "abd1" - }, { - name: "WolverinDEV 3", - database_id: 3, - unique_id: "abd1" - }] - }); - }, 0); - }); - - event_registry.on("query_group_permissions", event => { - setTimeout(() => { - event_registry.fire("group_permissions", { - status: "success", - groups: [{ - value: 20, - name: "Server Admin p:20", - id: 0 - }, { - value: 10, - name: "Server Mod p:10", - id: 0 - }], - permission_name: event.permission_name - }); - }, 0); - }); - - event_registry.on("query_general_permissions", event => { - setTimeout(() => { - event_registry.fire("general_permissions", { - status: "success", - permissions: { - i_playlist_song_needed_add_power: 77 - } - }) - }, 0); - }); - - event_registry.on("set_general_permission", event => { - setTimeout(() => { - event_registry.fire("set_general_permission_result", { - key: event.key, - value: event.value, - status: "success" - }); - }); - }); - - event_registry.on("query_client_permissions", event => { - setTimeout(() => { - event_registry.fire("client_permissions", { - client_database_id: event.client_database_id, - status: "success", - permissions: { - i_playlist_song_needed_add_power: 77 - } - }) - }, 500); - }); - - event_registry.on("set_client_permission", event => { - setTimeout(() => { - event_registry.fire("set_client_permission_result", { - key: event.key, - client_database_id: event.client_database_id, - status: "success", - value: event.value - }) - }, 500); - }); - } - } - - - function build_modal(event_registry: events.Registry) : JQuery { - const tag = $("#tmpl_music_manage").renderTag(); - - const container_settings = tag.find(".body > .category-settings"); - build_settings_container(event_registry, container_settings); - - const container_permissions = tag.find(".body > .category-permissions"); - build_permission_container(event_registry, container_permissions); - - /* general switch */ - { - let shown_container: "settings" | "permissions"; - - const header = tag.find(".header"); - - const category_permissions = header.find(".category-permissions"); - event_registry.on("show_container", data => { - category_permissions.toggleClass("selected", data.container === "permissions"); - container_permissions.toggleClass("hidden", data.container !== "permissions"); - }); - category_permissions.on('click', event => { - if(shown_container === "permissions") return; - event_registry.fire("show_container", { container: "permissions" }); - }); - - const category_settings = header.find(".category-settings"); - event_registry.on("show_container", data => { - category_settings.toggleClass("selected", data.container === "settings"); - container_settings.toggleClass("hidden", data.container !== "settings"); - }); - category_settings.on('click', event => { - if(shown_container === "settings") return; - event_registry.fire("show_container", { container: "settings" }); - }); - - event_registry.on("show_container", data => shown_container = data.container); - } - - /* input length fix */ - tag.find("input[maxlength]").on("input", event => { - const input = event.target as HTMLInputElement; - const max = parseInt(input.getAttribute("maxlength")); - const text = input.value; - if(!isNaN(max) && text && text.length > max) - //input.value = text.substr(text.length - max); - input.value = text.substr(0, max); }); - /* initialize */ - event_registry.fire("show_container", { container: "settings" }); - return tag.children(); + event_registry.on("query_bot_status", event => { + setTimeout(() => { + event_registry.fire("bot_status", { + status: "success", + data: { + channel_commander: bot.properties.client_is_channel_commander, + volume: bot.properties.player_volume, + description: bot.properties.client_description, + default_country_code: ( + !bot.channelTree ? undefined : + !bot.channelTree.server ? undefined : bot.channelTree.server.properties.virtualserver_country_code) || "DE", + country_code: bot.properties.client_country, + name: bot.properties.client_nickname, + priority_speaker: bot.properties.client_is_priority_speaker, + + bot_type: bot.properties.client_bot_type, + client_platform: bot.properties.client_platform, + client_version: bot.properties.client_version, + uptime_mode: bot.properties.client_uptime_mode + } + }); + }, 0); + }); + + event_registry.on("set_bot_status", event => { + const property_map = { + "channel_commander": "client_is_channel_commander", + "volume": "player_volume", + "description": "client_description", + "country_code": "client_country", + "name": "client_nickname", + "priority_speaker": "client_is_priority_speaker", + + "bot_type": "client_bot_type", + "client_platform": "client_platform", + "client_version": "client_version", + "uptime_mode": "client_uptime_mode" + }; + + + Promise.resolve().then(() => { + const property = property_map[event.key]; + if(!property) return Promise.reject(tr("unknown property")); + + const data = { + clid: bot.clientId() + }; + data[property] = event.value; + return client.serverConnection.send_command("clientedit", data); + }).then(() => { + event_registry.fire("set_bot_status_result", { + status: "success", + key: event.key, + value: event.value + }); + }).catch(error => { + event_registry.fire("set_bot_status_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to change bot setting %s: %o"), event.key, error); + }); + }); } - function build_settings_container(event_registry: events.Registry, tag: JQuery) { - const show_change_error = (header, message) => { - createErrorModal(tr("Failed to change value"), header + "
" + message).open(); - }; - - /* music bot settings */ - { - const container = tag.find(".settings-bot"); - - /* bot name */ - { - const input = container.find(".option-bot-name"); - let last_value = undefined; - - event_registry.on("query_bot_status", event => { - last_value = undefined; - input - .prop("disabled", true) - .val(null) - .attr("placeholder", tr("loading...")); + /* permissions */ + { + event_registry.on("query_general_permissions", event => { + const playlist_id = bot.properties.client_playlist_id; + client.permissions.requestPlaylistPermissions(playlist_id).then(result => { + const permissions = {}; + for(const permission of result) + if(permission.hasValue()) + permissions[permission.type.name] = permission.value; + event_registry.fire("general_permissions", { + status: "success", + permissions: permissions }); - - event_registry.on("bot_status", event => { - if(event.status === "error") - input - .prop("disabled", true) - .val(null) - .attr("placeholder", event.error_msg || tr("error while loading")); - else - input - .prop("disabled", false) - .attr("placeholder", null) - .val(last_value = event.data.name); + }).catch(error => { + event_registry.fire("general_permissions", { + status: "error", + error_msg: error_msg(error) }); - - event_registry.on("set_bot_status_result", event => { - if(event.key !== "name") return; - - if(event.status !== "success") - show_change_error(tr("Failed to set bot name"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - input - .prop("disabled", false) - .attr("placeholder", null) - .val(last_value); - }); - - input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); - input.on("focusout", event => { - const value = input.val() as string; - if(value === last_value) return; - if(!value) { - input.val(last_value); - return; - } - - input - .prop("disabled", true) - .val(null) - .attr("placeholder", tr("applying...")); - - event_registry.fire("set_bot_status", { - key: "name", - value: value - }); - }); - } - - /* country flag */ - { - const input = container.find(".option-bot-country"); - const flag = container.find(".container-country .country"); - let last_value = undefined, fallback_country = undefined; - - const update_country_code = input => { - input = input || fallback_country || "ts"; - flag.each((_, e) => { - for(const [index, klass] of e.classList.entries()) - if(klass.startsWith("flag-")) - e.classList.remove(klass); - }); - flag.addClass("flag-" + input.toLowerCase()); - flag.attr("title", i18n.country_name(input, tr("Unknown country"))); - }; - - event_registry.on("query_bot_status", event => { - last_value = undefined; - input - .prop("disabled", true) - .val(null) - .attr("placeholder", "..."); - update_country_code("ts"); - }); - - event_registry.on("bot_status", event => { - if(event.status === "error") - input - .prop("disabled", true) - .val(null) - .attr("placeholder", "err"); - else { - input - .prop("disabled", false) - .attr("placeholder", null) - .val(last_value = event.data.country_code); - fallback_country = event.data.default_country_code; - } - update_country_code(last_value); - }); - - event_registry.on("set_bot_status_result", event => { - if(event.key !== "country_code") return; - - if(event.status !== "success") - show_change_error(tr("Failed to set bots country"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - input - .prop("disabled", false) - .attr("placeholder", null) - .val(last_value); - update_country_code(last_value); - }); - - input.on("input", () => { - update_country_code(input.val()); - input.firstParent(".input-boxed").removeClass("is-invalid"); - }); - - input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); - input.on("focusout", event => { - const value = input.val() as string; - if(value === last_value) return; - if(value && value.length != 2) { - input.firstParent(".input-boxed").addClass("is-invalid"); - return; - } - - input - .prop("disabled", true) - .val(null) - .attr("placeholder", "..."); - - event_registry.fire("set_bot_status", { - key: "country_code", - value: value - }); - }); - } - - /* flag channel commander */ - { - const input = container.find(".option-channel-commander") as JQuery; - const label = input.parents("label"); - - let last_value = undefined; - - event_registry.on("query_bot_status", event => { - last_value = undefined; - - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - }); - - event_registry.on("bot_status", event => { - if(event.status === "error") { - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - } else { - label.removeClass("disabled"); - input - .prop("checked", last_value = event.data.channel_commander) - .prop("disabled", false); - } - }); - - event_registry.on("set_bot_status_result", event => { - if(event.key !== "channel_commander") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change channel commander state"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - label.removeClass("disabled"); - input - .prop("checked", last_value) - .prop("disabled", false); - }); - - input.on("change", event => { - label.addClass("disabled"); - input.prop("disabled", true); - event_registry.fire("set_bot_status", { - key: "channel_commander", - value: input.prop("checked") - }); - }); - } - - /* flag priority speaker */ - { - const input = container.find(".option-priority-speaker") as JQuery; - const label = input.parents("label"); - - let last_value = undefined; - - event_registry.on("query_bot_status", event => { - last_value = undefined; - - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - }); - - event_registry.on("bot_status", event => { - if(event.status === "error") { - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - } else { - label.removeClass("disabled"); - input - .prop("checked", last_value = event.data.priority_speaker) - .prop("disabled", false); - } - }); - - event_registry.on("set_bot_status_result", event => { - if(event.key !== "priority_speaker") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change priority speaker state"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - label.removeClass("disabled"); - input - .prop("checked", last_value) - .prop("disabled", false); - }); - - input.on("change", event => { - label.addClass("disabled"); - input.prop("disabled", true); - event_registry.fire("set_bot_status", { - key: "priority_speaker", - value: input.prop("checked") - }); - }); - } - - /* status load timeout */ - { - let timeout; - event_registry.on("query_bot_status", event => { - timeout = setTimeout(() => { - event_registry.fire("bot_status", { - status: "error", - error_msg: tr("load timeout") - }); - }, 5000); - }); - - event_registry.on("bot_status", event => clearTimeout(timeout)); - } - - /* set status timeout */ - { - let timeouts: {[key: string]:any} = {}; - event_registry.on("set_bot_status", event => { - clearTimeout(timeouts[event.key]); - timeouts[event.key] = setTimeout(() => { - event_registry.fire("set_bot_status_result", { - status: "timeout", - key: event.key, - }); - }, 5000); - }); - - event_registry.on("set_bot_status_result", event => { - clearTimeout(timeouts[event.key]); - delete timeouts[event.key]; - }); - } - } - - /* music bot settings */ - { - const container = tag.find(".settings-playlist"); - - /* playlist replay mode */ - { - const input = container.find(".option-replay-mode") as JQuery; - let last_value = undefined; - - const update_value = text => { - if(text) { - input.prop("disabled", true).addClass("disabled"); - input.val("-1"); - input.find("option[value=-1]").text(text); - } else if(last_value >= 0 && last_value <= 3) { - input - .prop("disabled", false) - .removeClass("disabled"); - input.val(last_value); - } else { - update_value(tr("invalid value")); - } - }; - - event_registry.on("query_playlist_status", event => { - last_value = undefined; - update_value(tr("loading...")); - }); - - event_registry.on("playlist_status", event => { - if(event.status === "error") { - update_value(event.error_msg || tr("error while loading")); - } else { - last_value = event.data.replay_mode; - update_value(undefined); - } - }); - - event_registry.on("set_playlist_status_result", event => { - if(event.key !== "replay_mode") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change replay mode"), event.error_msg || tr("timeout")); - else - last_value = event.value; - update_value(undefined); - }); - - input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); - input.on("change", event => { - const value = parseInt(input.val() as string); - console.log(value); - if(isNaN(value)) return; - - update_value(tr("applying...")); - event_registry.fire("set_playlist_status", { - key: "replay_mode", - value: value - }); - }); - } - - /* playlist max size */ - { - const input = container.find(".container-max-playlist-size input"); - let last_value = undefined; - - event_registry.on("query_playlist_status", event => { - last_value = undefined; - input - .prop("disabled", true) - .val(null) - .attr("placeholder", tr("loading...")) - .firstParent(".input-boxed").addClass("disabled"); - }); - - event_registry.on("playlist_status", event => { - if(event.status === "error") - input - .prop("disabled", true) - .val(null) - .attr("placeholder", event.error_msg || tr("error while loading")) - .firstParent(".input-boxed").addClass("disabled"); - else - input - .prop("disabled", false) - .attr("placeholder", null) - .val((last_value = event.data.max_size).toString()) - .firstParent(".input-boxed").removeClass("disabled"); - }); - - event_registry.on("set_playlist_status_result", event => { - if(event.key !== "max_size") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change max playlist size"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - input - .prop("disabled", false) - .attr("placeholder", null) - .val(last_value) - .firstParent(".input-boxed").removeClass("disabled"); - }); - - input.on("input", event => input.parentsUntil(".input-boxed").removeClass("is-invalid")); - input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); - input.on("focusout", event => { - const value = input.val() as string; - if(value === last_value) return; - if(value === "") { - input.val(last_value); - return; - } - if(isNaN(parseInt(value))) { - input.parentsUntil(".input-boxed").addClass("is-invalid"); - return; - } - - input - .prop("disabled", true) - .val(null) - .attr("placeholder", tr("applying...")) - .firstParent(".input-boxed").addClass("disabled"); - - event_registry.fire("set_playlist_status", { - key: "max_size", - value: parseInt(value) - }); - }); - } - - /* flag delete played */ - { - const input = container.find(".option-delete-played-songs") as JQuery; - const label = input.parents("label"); - - let last_value = undefined; - - event_registry.on("query_playlist_status", event => { - last_value = undefined; - - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - }); - - event_registry.on("playlist_status", event => { - if(event.status === "error") { - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - } else { - label.removeClass("disabled"); - input - .prop("checked", last_value = event.data.delete_played) - .prop("disabled", false); - } - }); - - event_registry.on("set_playlist_status_result", event => { - if(event.key !== "delete_played") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change delete state"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - label.removeClass("disabled"); - input - .prop("checked", last_value) - .prop("disabled", false); - }); - - input.on("change", event => { - label.addClass("disabled"); - input.prop("disabled", true); - event_registry.fire("set_playlist_status", { - key: "delete_played", - value: input.prop("checked") - }); - }); - } - - /* flag notify song change */ - { - const input = container.find(".option-notify-songs-change") as JQuery; - const label = input.parents("label"); - - let last_value = undefined; - - event_registry.on("query_playlist_status", event => { - last_value = undefined; - - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - }); - - event_registry.on("playlist_status", event => { - if(event.status === "error") { - label.addClass("disabled"); - input - .prop("checked", false) - .prop("disabled", true); - } else { - label.removeClass("disabled"); - input - .prop("checked", last_value = event.data.notify_song_change) - .prop("disabled", false); - } - }); - - event_registry.on("set_playlist_status_result", event => { - if(event.key !== "notify_song_change") return; - - if(event.status !== "success") - show_change_error(tr("Failed to change notify state"), event.error_msg || tr("timeout")); - else - last_value = event.value; - - label.removeClass("disabled"); - input - .prop("checked", last_value) - .prop("disabled", false); - }); - - input.on("change", event => { - label.addClass("disabled"); - input.prop("disabled", true); - event_registry.fire("set_playlist_status", { - key: "notify_song_change", - value: input.prop("checked") - }); - }); - } - - /* status load timeout */ - { - let timeout; - event_registry.on("query_playlist_status", event => { - timeout = setTimeout(() => { - event_registry.fire("playlist_status", { - status: "error", - error_msg: tr("load timeout") - }); - }, 5000); - }); - - event_registry.on("playlist_status", event => clearTimeout(timeout)); - } - - /* set status timeout */ - { - let timeouts: {[key: string]:any} = {}; - event_registry.on("set_playlist_status", event => { - clearTimeout(timeouts[event.key]); - timeouts[event.key] = setTimeout(() => { - event_registry.fire("set_playlist_status_result", { - status: "timeout", - key: event.key, - }); - }, 5000); - }); - - event_registry.on("set_playlist_status_result", event => { - clearTimeout(timeouts[event.key]); - delete timeouts[event.key]; - }); - } - } - - /* reload button */ - { - const button = tag.find(".button-reload"); - let timeout; - - event_registry.on(["query_bot_status", "query_playlist_status"], event => { - button.prop("disabled", true); - - clearTimeout(timeout); - timeout = setTimeout(() => { - button.prop("disabled", false); - }, 1000); + log.error(LogCategory.CLIENT, tr("Failed to query playlist general permissions for playlist %d: %o"), playlist_id, error); }); + }); - button.on("click", event => { - event_registry.fire("query_bot_status"); - event_registry.fire("query_playlist_status"); + event_registry.on("set_general_permission", event => { + const playlist_id = bot.properties.client_playlist_id; + + client.serverConnection.send_command("playlistaddperm", { + playlist_id: playlist_id, + permsid: event.key, + permvalue: event.value, + permskip: false, + permnegated: false + }).then(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + status: "success", + value: event.value + }); + }).catch(error => { + event_registry.fire("set_general_permission_result", { + status: "error", + key: event.key, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to set playlist general permissions for playlist %d and permission %d: %o"), playlist_id, event.key, error); }); - } + }); - tooltip(tag); - - /* initialize on show */ - { - let initialized = false; - event_registry.on("show_container", event => { - if(event.container !== "settings" || initialized) return; - initialized = true; - - event_registry.fire("query_bot_status"); - event_registry.fire("query_playlist_status"); + event_registry.on("query_client_permissions", event => { + const playlist_id = bot.properties.client_playlist_id; + const client_id = event.client_database_id; + client.permissions.requestPlaylistClientPermissions(playlist_id, client_id).then(result => { + const permissions = {}; + for(const permission of result) + if(permission.hasValue()) + permissions[permission.type.name] = permission.value; + event_registry.fire("client_permissions", { + status: "success", + client_database_id: event.client_database_id, + permissions: permissions + }); + }).catch(error => { + event_registry.fire("client_permissions", { + status: "error", + client_database_id: event.client_database_id, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to query playlist client permissions for playlist %d and client %d: %o"), playlist_id, client_id, error); }); - } + }); + + event_registry.on("set_client_permission", event => { + const playlist_id = bot.properties.client_playlist_id; + const client_id = event.client_database_id; + + client.serverConnection.send_command("playlistclientaddperm", { + playlist_id: playlist_id, + cldbid: client_id, + + permsid: event.key, + permvalue: event.value, + permskip: false, + permnegated: false + }).then(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + status: "success", + client_database_id: client_id, + value: event.value + }); + }).catch(error => { + event_registry.fire("set_client_permission_result", { + status: "error", + key: event.key, + client_database_id: client_id, + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to set playlist client permissions for playlist %d, permission %d and client id %d: %o"), playlist_id, event.key, client_id, error); + }); + }); + + event_registry.on("query_special_clients", event => { + const playlist_id = bot.properties.client_playlist_id; + client.serverConnection.command_helper.request_playlist_client_list(playlist_id).then(clients => { + return client.serverConnection.command_helper.info_from_cldbid(...clients); + }).then(clients => { + event_registry.fire("special_client_list", { + status: "success", + clients: clients.map(e => { + return { + name: e.client_nickname, + unique_id: e.client_unique_id, + database_id: e.client_database_id + } + }) + }); + }).catch(error => { + event_registry.fire("special_client_list", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to query special client list for playlist %d: %o"), playlist_id, error); + }) + }); + + event_registry.on("search_client", event => { + if(!event.text) return; + + const text = event.text; + Promise.resolve().then(() => { + let is_uuid = false; + try { + is_uuid = atob(text).length === 32; + } catch(e) {} + if(is_uuid) { + return client.serverConnection.command_helper.info_from_uid(text); + } else if(text.match(/^[0-9]{1,7}$/) && !isNaN(parseInt(text))) { + return client.serverConnection.command_helper.info_from_cldbid(parseInt(text)); + } else { + //TODO: Database name lookup? + return Promise.reject("no results"); + } + }).then(result => { + if(result.length) { + const client = result[0]; + event_registry.fire("search_client_result", { + status: "success", + client: { + name: client.client_nickname, + unique_id: client.client_unique_id, + database_id: client.client_database_id + } + }); + } else { + event_registry.fire("search_client_result", { + status: "empty" + }); + } + }).catch(error => { + event_registry.fire("search_client_result", { + status: "error", + error_msg: error_msg(error) + }); + log.error(LogCategory.CLIENT, tr("Failed to lookup search text \"%s\": %o"), text, error); + }); + }); + + event_registry.on("query_group_permissions", event => { + client.permissions.find_permission(event.permission_name).then(result => { + let groups = []; + for(const e of result) { + if(e.type !== "server_group") continue; + + const group = client.groups.serverGroup((e as ServerGroup).group_id); + if(!group) continue; + + groups.push({ + name: group.name, + value: e.value, + id: group.id + }); + } + + event_registry.fire("group_permissions", { + status: "success", + groups: groups, + permission_name: event.permission_name + }); + }).catch(error => { + event_registry.fire("group_permissions", { + status: "error", + error_msg: error_msg(error), + permission_name: event.permission_name + }); + log.error(LogCategory.CLIENT, tr("Failed to execute permfind for permission %s: %o"), event.permission_name, error); + }); + }); + } +} + +function dummy_controller(event_registry: Registry) { + /* settings */ + { + event_registry.on("query_bot_status", event => { + setTimeout(() => { + event_registry.fire("bot_status", { + status: "success", + data: { + name: "Another TeaSpeak bot", + country_code: "DE", + default_country_code: "GB", + channel_commander: false, + description: "Hello World", + priority_speaker: true, + volume: 66, + + uptime_mode: 0, + client_version: "Version", + client_platform: "Platform", + bot_type: 0 + } + }) + }); + }); + + + event_registry.on("query_playlist_status", event => { + setTimeout(() => { + event_registry.fire("playlist_status", { + status: "success", + data: { + max_size: 55, + notify_song_change: true, + delete_played: false, + finished: false, + replay_mode: 2 + } + }) + }); + }); } - function build_permission_container(event_registry: events.Registry, tag: JQuery) { - /* client search mechanism */ - { - const container = tag.find(".table-head .column-client-specific .client-select"); - let list_shown = false; - - /* search list show/hide */ - { - const button_list_clients = container.find(".button-list-clients"); - button_list_clients.on('click', event => - event_registry.fire(list_shown ? "hide_client_list" : "show_client_list")); - - event_registry.on("show_client_list", () => { - list_shown = true; - button_list_clients.text(tr("Hide clients")); + /* permissions */ + { + event_registry.on("query_special_clients", event => { + setTimeout(() => { + event_registry.fire("special_client_list", { + status: "success", + clients: [{ + name: "WolverinDEV", + database_id: 1, + unique_id: "abd" + }, { + name: "WolverinDEV 2", + database_id: 2, + unique_id: "abd1" + }, { + name: "WolverinDEV 3", + database_id: 3, + unique_id: "abd1" + }] }); + }, 0); + }); - event_registry.on("hide_client_list", () => { - list_shown = false; - button_list_clients.text(tr("List clients")); + event_registry.on("query_group_permissions", event => { + setTimeout(() => { + event_registry.fire("group_permissions", { + status: "success", + groups: [{ + value: 20, + name: "Server Admin p:20", + id: 0 + }, { + value: 10, + name: "Server Mod p:10", + id: 0 + }], + permission_name: event.permission_name }); - } + }, 0); + }); - /* the search box */ - { - const input_search = container.find(".input-search"); - const button_search = container.find(".button-search"); - - let search_timeout; - let last_query; - input_search.on('keyup', event => { - const text = input_search.val() as string; - if(text === last_query) return; - - if(text) - event_registry.fire("filter_client_list", { filter: text }); - else - event_registry.fire("filter_client_list", { filter: undefined }); - - input_search.toggleClass("is-invalid", !list_shown && text === last_query); - if(!list_shown) { - button_search.prop("disabled", !text || !!search_timeout); - } else { - last_query = text; + event_registry.on("query_general_permissions", event => { + setTimeout(() => { + event_registry.fire("general_permissions", { + status: "success", + permissions: { + i_playlist_song_needed_add_power: 77 } + }) + }, 0); + }); + + event_registry.on("set_general_permission", event => { + setTimeout(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + value: event.value, + status: "success" }); + }); + }); - input_search.on('keydown', event => { - if(event.key === "Enter" && !list_shown && !button_search.prop("disabled")) - button_search.trigger("click"); - }); - - event_registry.on("show_client_list", () => { - button_search.prop("disabled", true); - input_search.attr("placeholder", tr("Search client list")); - }); - - event_registry.on("hide_client_list", () => { - button_search.prop("disabled", !input_search.val() || !!search_timeout); - input_search.attr("placeholder", tr("Client uid or database id")); - }); - - button_search.on("click", event => { - button_search.prop("disabled", true); - - input_search.blur(); - const text = input_search.val() as string; - last_query = text; - event_registry.fire("search_client", { - text: text - }); - search_timeout = setTimeout(() => event_registry.fire("search_client_result", { - status: "timeout" - }), 5000); - }); - - event_registry.on("search_client_result", event => { - clearTimeout(search_timeout); - search_timeout = 0; - - button_search.prop("disabled", !input_search.val()); - if(event.status === "timeout") { - createErrorModal(tr("Client search failed"), tr("Failed to perform client search.
Search resulted in a timeout.")).open(); - return; - } else if(event.status === "error" || event.status === "empty") { - //TODO: Display the error somehow? - input_search.addClass("is-invalid"); - return; - } else { - event_registry.fire("special_client_set", { - client: event.client - }); + event_registry.on("query_client_permissions", event => { + setTimeout(() => { + event_registry.fire("client_permissions", { + client_database_id: event.client_database_id, + status: "success", + permissions: { + i_playlist_song_needed_add_power: 77 } - }); - } + }) + }, 500); + }); - /* the client list */ - { - const container = tag.find(".overlay-client-list"); - - event_registry.on("show_client_list", () => container.removeClass("hidden")); - event_registry.on("hide_client_list", () => container.addClass("hidden")); - - const button_refresh = container.find(".button-clientlist-refresh"); - - const container_entries = container.find(".container-client-list"); - event_registry.on("special_client_list", data => { - button_refresh.prop("disabled", false); - container.find(".overlay").addClass("hidden"); - if(data.status === "error-permission") { - const overlay = container.find(".overlay-query-error-permissions"); - overlay.find("a").text(tr("Insufficient permissions")); - overlay.removeClass("hidden"); - } else if(data.status === "success") { - container_entries.find(".client").remove(); /* clear */ - - if(!data.clients.length) { - const overlay = container.find(".overlay-empty-list"); - overlay.removeClass("hidden"); - } else { - for(const client of data.clients) { - const tag = $.spawn("div").addClass("client").append( - htmltags.generate_client_object({ - add_braces: false, - client_id: 0, - client_database_id: client.database_id, - client_name: client.name, - client_unique_id: client.unique_id - }) - ); - tag.on('dblclick', event => event_registry.fire("special_client_set", { client: client })); - tag.attr("x-filter", client.database_id + "_" + client.name + "_" + client.unique_id); - container_entries.append(tag); - } - } - } else { - const overlay = container.find(".overlay-query-error"); - overlay.find("a").text(data.error_msg ? data.error_msg : tr("query failed")); - overlay.removeClass("hidden"); - } - }); - - /* refresh button */ - button_refresh.on('click', event => { - button_refresh.prop("disabled", true); - event_registry.fire("query_special_clients"); - }); + event_registry.on("set_client_permission", event => { + setTimeout(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + client_database_id: event.client_database_id, + status: "success", + value: event.value + }) + }, 500); + }); + } +} - /* special client list query timeout handler */ - { - let query_timeout; - event_registry.on("query_special_clients", event => { - query_timeout = setTimeout(() => { - event_registry.fire("special_client_list", { - status: "error", - error_msg: tr("Query timeout") - }); - }, 5000); - }); +function build_modal(event_registry: Registry) : JQuery { + const tag = $("#tmpl_music_manage").renderTag(); - event_registry.on("special_client_list", event => clearTimeout(query_timeout)); - } + const container_settings = tag.find(".body > .category-settings"); + build_settings_container(event_registry, container_settings); - /* first time client list show */ - { - let shown; - event_registry.on('show_client_list', event => { - if(shown) return; - shown = true; + const container_permissions = tag.find(".body > .category-permissions"); + build_permission_container(event_registry, container_permissions); - event_registry.fire("query_special_clients"); - }); - } + /* general switch */ + { + let shown_container: "settings" | "permissions"; - /* the client list filter */ - { - let filter; + const header = tag.find(".header"); - const overlay = container.find(".overlay-filter-no-result"); - const update_filter = () => { - let shown = 0, hidden = 0; - container_entries.find(".client").each(function () { - const text = this.getAttribute("x-filter"); - if(!filter || text.toLowerCase().indexOf(filter) != -1) { - this.classList.remove("hidden"); - shown++; - } else { - this.classList.add("hidden"); - hidden++; - } - }); - if(shown == 0 && hidden == 0) return; - overlay.toggleClass("hidden", shown != 0); - }; + const category_permissions = header.find(".category-permissions"); + event_registry.on("show_container", data => { + category_permissions.toggleClass("selected", data.container === "permissions"); + container_permissions.toggleClass("hidden", data.container !== "permissions"); + }); + category_permissions.on('click', event => { + if(shown_container === "permissions") return; + event_registry.fire("show_container", { container: "permissions" }); + }); - event_registry.on("special_client_list", event => update_filter()); - event_registry.on("filter_client_list", event => { - filter = (event.filter || "").toLowerCase(); - update_filter(); - }); - } - } + const category_settings = header.find(".category-settings"); + event_registry.on("show_container", data => { + category_settings.toggleClass("selected", data.container === "settings"); + container_settings.toggleClass("hidden", data.container !== "settings"); + }); + category_settings.on('click', event => { + if(shown_container === "settings") return; + event_registry.fire("show_container", { container: "settings" }); + }); - event_registry.on("special_client_set", event => { - container.toggleClass("hidden", !!event.client); - event_registry.fire("hide_client_list"); - }); - } + event_registry.on("show_container", data => shown_container = data.container); + } - /* the client info */ + /* input length fix */ + tag.find("input[maxlength]").on("input", event => { + const input = event.target as HTMLInputElement; + const max = parseInt(input.getAttribute("maxlength")); + const text = input.value; + if(!isNaN(max) && text && text.length > max) + //input.value = text.substr(text.length - max); + input.value = text.substr(0, max); + }); + + /* initialize */ + event_registry.fire("show_container", { container: "settings" }); + return tag.children(); +} + +function build_settings_container(event_registry: Registry, tag: JQuery) { + const show_change_error = (header, message) => { + createErrorModal(tr("Failed to change value"), header + "
" + message).open(); + }; + + /* music bot settings */ + { + const container = tag.find(".settings-bot"); + + /* bot name */ { - const container = tag.find(".table-head .column-client-specific .client-info"); + const input = container.find(".option-bot-name"); + let last_value = undefined; - container.find(".button-client-deselect").on("click", event => { - event_registry.fire("special_client_set", { client: undefined }); + event_registry.on("query_bot_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", tr("loading...")); }); - event_registry.on("special_client_set", event => { - container.toggleClass("hidden", !event.client); - - const client_container = container.find(".container-selected-client"); - client_container.find(".htmltag-client").remove(); - if(event.client) { - client_container.append(htmltags.generate_client_object({ - client_unique_id: event.client.unique_id, - client_name: event.client.name, - client_id: 0, - client_database_id: event.client.database_id, - add_braces: false - })); - } - }); - } - - const power_needed_map = { - i_client_music_rename_power: "i_client_music_needed_rename_power", - i_client_music_modify_power: "i_client_music_needed_modify_power", - i_client_music_delete_power: "i_client_music_needed_delete_power", - i_playlist_view_power: "i_playlist_needed_view_power", - i_playlist_modify_power: "i_playlist_needed_modify_power", - i_playlist_permission_modify_power: "i_playlist_needed_permission_modify_power", - i_playlist_song_add_power: "i_playlist_song_needed_add_power", - i_playlist_song_move_power: "i_playlist_song_needed_move_power", - i_playlist_song_remove_power: "i_playlist_song_needed_remove_power", - b_virtualserver_playlist_permission_list: "b_virtualserver_playlist_permission_list" - }; - const needed_power_map = Object.entries(power_needed_map).reduce((ret, entry) => { - const [key, value] = entry; - ret[value] = key; - return ret; - }, {}); - - /* general permissions */ - { - /* permission input functionality */ - { - tag.find(".general-permission").each((_, _e) => { - const elem = $(_e) as JQuery; - - const permission_name = elem.attr("x-permission"); - if(!permission_name) return; - - const input = elem.find("input"); - input.attr("maxlength", 6); - - let last_sync_value = undefined; - - event_registry.on("query_general_permissions", event => { - input.prop("disabled", true).val(null); - input.attr("placeholder", tr("loading...")); - }); - - event_registry.on("general_permissions", event => { - input.prop("disabled", true).val(null); - if(event.status === "timeout") { - input.attr("placeholder", tr("load timeout")); - } else if(event.status === "success") { - input.prop("disabled", false); //TODO: Check permissions? - input.attr("placeholder", null); - const value = event.permissions ? event.permissions[permission_name] || 0 : 0; - last_sync_value = value; - input.val(value); - } else { - input.attr("placeholder", event.error_msg || tr("load error")); - } - }); - - event_registry.on("set_general_permission_result", event => { - if(event.key !== permission_name) return; - - input.prop("disabled", false); //TODO: Check permissions? - input.attr("placeholder", null); - if(event.status === "success") { - input.val(event.value); - last_sync_value = event.value; - } else if(event.status === "error") { - if(typeof last_sync_value === "number") input.val(last_sync_value); - createErrorModal(tr("Failed to change permission"), tra("Failed to change permission:{:br:}{}", event.error_msg)).open(); - } - }); - - input.on("focusout", event => { - if(input.prop("disabled")) return; - - const value = parseInt(input.val() as string); - if(value === last_sync_value) return; - - input.prop("disabled", true).val(null); - input.attr("placeholder", tr("applying...")); - event_registry.fire("set_general_permission", { - key: permission_name, - value: value || 0 - }); - }); - input.on("keyup", event => event.key === "Enter" && input.blur()); - }); - } - - /* the tooltip functionality */ - { - tag.find(".general-permission").each((_, _e) => { - const elem = $(_e) as JQuery; - - const permission_name = elem.attr("x-permission"); - if(!permission_name) return; - - const required_power = needed_power_map[permission_name]; - if(!required_power) return; - - let last_sync_value = undefined; - let current_tag: JQuery; - - let loading = false; - let query_result: { - status: "error" | "timeout" | "success" - groups?: { - name: string, - value: number, - id: number - }[], - error_msg?: string - }; - - event_registry.on("general_permissions", event => { - if(event.status === "success") - last_sync_value = event.permissions ? event.permissions[permission_name] || 0 : 0; - }); - - event_registry.on("set_general_permission_result", event => { - if(event.key !== permission_name) return; - - if(event.status === "success") - last_sync_value = event.value; - }); - - event_registry.on("refresh_permissions", event => { - query_result = undefined; /* require for the next time */ - }); - - const show_query_result = () => { - if(!current_tag) return; - - const container_groups = current_tag.find(".container-groups"); - container_groups.children().remove(); - current_tag.find(".container-status").addClass("hidden"); - - if(loading) { - current_tag.find(".status-loading").removeClass("hidden"); - } else if(!query_result || query_result.status === "error") { - current_tag - .find(".status-error").removeClass("hidden") - .text((query_result ? query_result.error_msg : "") || tr("failed to query data")); - } else if(query_result.status === "timeout") { - current_tag - .find(".status-error").removeClass("hidden") - .text(tr("timeout while loading")); - } else { - let count = 0; - for(const group of (query_result.groups || [])) { - if(group.value !== -1 && group.value < last_sync_value) continue; - - count++; - container_groups.append($.spawn("div").addClass("group").text( - " - " + group.name + " (" + group.id + ")" - )); - } - - if(count === 0) current_tag.find(".status-no-groups").removeClass("hidden"); - } - }; - - tooltip.initialize(elem, { - on_show(tag: JQuery) { - current_tag = tag; - - if(!query_result && !loading) { - event_registry.fire("query_group_permissions", { - permission_name: required_power - }); - loading = true; - } - show_query_result(); - }, - on_hide(tag: JQuery) { - current_tag = undefined; - } - }); - - event_registry.on("group_permissions", event => { - if(event.permission_name !== required_power) return; - - loading = false; - query_result = event; - show_query_result(); - }); - }); - - - /* refresh mechanism */ - { - event_registry.on("refresh_permissions", event => event_registry.fire("query_general_permissions")); - } - } - - /* permission set timeout */ - { - let permission_timers: {[key: string]:any} = {}; - event_registry.on("set_general_permission", event => { - if(permission_timers[event.key]) - clearTimeout(permission_timers[event.key]); - permission_timers[event.key] = setTimeout(() => { - event_registry.fire("set_general_permission_result", { - key: event.key, - status: "error", - error_msg: tr("controller timeout") - }); - }, 5000); - }); - - event_registry.on("set_general_permission_result", event => { - clearTimeout(permission_timers[event.key]); - delete permission_timers[event.key]; - }); - } - - /* group query timeout */ - { - let timers: {[key: string]:any} = {}; - event_registry.on("query_group_permissions", event => { - if(timers[event.permission_name]) - clearTimeout(timers[event.permission_name]); - timers[event.permission_name] = setTimeout(() => { - event_registry.fire("group_permissions", { - permission_name: event.permission_name, - status: "timeout" - }); - }, 5000); - }); - - event_registry.on("group_permissions", event => { - clearTimeout(timers[event.permission_name]); - delete timers[event.permission_name]; - }); - } - - /* query timeout */ - { - let query_timeout; - event_registry.on("query_general_permissions", event => { - clearTimeout(query_timeout); - query_timeout = setTimeout(() => { - event_registry.fire("general_permissions", { - status: "timeout" - }); - }, 5000); - }); - - event_registry.on("general_permissions", event => clearTimeout(query_timeout)); - } - - /* refresh button */ - { - const button = tag.find(".button-permission-refresh"); - let refresh_timer; - - let loading_client_permissions = false; - let loading_general_permissions = false; - - const update_button = () => - button.prop("disabled", refresh_timer || loading_client_permissions || loading_general_permissions); - - event_registry.on("query_general_permissions", event => { - loading_general_permissions = true; - update_button(); - }); - - event_registry.on("general_permissions", event => { - loading_general_permissions = false; - update_button(); - }); - - event_registry.on("query_client_permissions", event => { - loading_client_permissions = true; - update_button(); - }); - - event_registry.on("client_permissions", event => { - loading_client_permissions = false; - update_button(); - }); - - button.on('click', event => { - event_registry.fire("refresh_permissions"); - - /* allow refreshes only every second */ - refresh_timer = setTimeout(() => { - refresh_timer = undefined; - update_button(); - }, 1000); - }); - } - } - - /* client specific permissions */ - { - const container = tag.find(".column-client-specific"); - - let client_database_id = 0; - let needed_permissions: {[key: string]:number} = {}; - - /* needed permissions updater */ - { - event_registry.on("general_permissions", event => { - if(event.status !== "success") return; - - needed_permissions = event.permissions; - }); - - event_registry.on("set_general_permission_result", event => { - if (event.status !== "success") return; - - needed_permissions[event.key] = event.value; - }); - } - - event_registry.on("special_client_set", event => { - client_database_id = event.client ? event.client.database_id : 0; - container.find(".client-permission").toggleClass("hidden", !event.client); - - if(client_database_id) - event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); + event_registry.on("bot_status", event => { + if(event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", event.error_msg || tr("error while loading")); + else + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value = event.data.name); }); - const enabled_class = "client-apply"; - const disabled_class = "client-delete"; + event_registry.on("set_bot_status_result", event => { + if(event.key !== "name") return; - container.find(".client-permission").each((_, _e) => { - const elem = $(_e); + if(event.status !== "success") + show_change_error(tr("Failed to set bot name"), event.error_msg || tr("timeout")); + else + last_value = event.value; - const input = elem.find("input"); - const status_indicator = elem.find(".icon_em"); + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value); + }); - const permission_name = elem.attr("x-permission") as string; - const permission_needed_name = power_needed_map[permission_name]; - - let last_sync_value = undefined; - let hide_indicator = false; - - if(typeof permission_needed_name !== "string") { - log.warn(LogCategory.GENERAL, tr("Missing permission needed mapping for %s"), permission_name); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val() as string; + if(value === last_value) return; + if(!value) { + input.val(last_value); return; } - const update_indicator = () => { - const value = parseInt(input.val() as string); - const needed = typeof needed_permissions[permission_needed_name] === "number" ? needed_permissions[permission_needed_name] : 0; - const flag = value == -1 ? true : isNaN(value) || value == 0 ? false : value >= needed; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", tr("applying...")); - status_indicator.toggle(!hide_indicator); - status_indicator.toggleClass(enabled_class, flag).toggleClass(disabled_class, !flag); + event_registry.fire("set_bot_status", { + key: "name", + value: value + }); + }); + } + + /* country flag */ + { + const input = container.find(".option-bot-country"); + const flag = container.find(".container-country .country"); + let last_value = undefined, fallback_country = undefined; + + const update_country_code = input => { + input = input || fallback_country || "ts"; + flag.each((_, e) => { + for(const [index, klass] of e.classList.entries()) + if(klass.startsWith("flag-")) + e.classList.remove(klass); + }); + flag.addClass("flag-" + input.toLowerCase()); + flag.attr("title", i18nc.country_name(input, tr("Unknown country"))); + }; + + event_registry.on("query_bot_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "..."); + update_country_code("ts"); + }); + + event_registry.on("bot_status", event => { + if(event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "err"); + else { + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value = event.data.country_code); + fallback_country = event.data.default_country_code; + } + update_country_code(last_value); + }); + + event_registry.on("set_bot_status_result", event => { + if(event.key !== "country_code") return; + + if(event.status !== "success") + show_change_error(tr("Failed to set bots country"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value); + update_country_code(last_value); + }); + + input.on("input", () => { + update_country_code(input.val()); + input.firstParent(".input-boxed").removeClass("is-invalid"); + }); + + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val() as string; + if(value === last_value) return; + if(value && value.length != 2) { + input.firstParent(".input-boxed").addClass("is-invalid"); + return; + } + + input + .prop("disabled", true) + .val(null) + .attr("placeholder", "..."); + + event_registry.fire("set_bot_status", { + key: "country_code", + value: value + }); + }); + } + + /* flag channel commander */ + { + const input = container.find(".option-channel-commander") as JQuery; + const label = input.parents("label"); + + let last_value = undefined; + + event_registry.on("query_bot_status", event => { + last_value = undefined; + + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + + event_registry.on("bot_status", event => { + if(event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.channel_commander) + .prop("disabled", false); + } + }); + + event_registry.on("set_bot_status_result", event => { + if(event.key !== "channel_commander") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change channel commander state"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_bot_status", { + key: "channel_commander", + value: input.prop("checked") + }); + }); + } + + /* flag priority speaker */ + { + const input = container.find(".option-priority-speaker") as JQuery; + const label = input.parents("label"); + + let last_value = undefined; + + event_registry.on("query_bot_status", event => { + last_value = undefined; + + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + + event_registry.on("bot_status", event => { + if(event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.priority_speaker) + .prop("disabled", false); + } + }); + + event_registry.on("set_bot_status_result", event => { + if(event.key !== "priority_speaker") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change priority speaker state"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_bot_status", { + key: "priority_speaker", + value: input.prop("checked") + }); + }); + } + + /* status load timeout */ + { + let timeout; + event_registry.on("query_bot_status", event => { + timeout = setTimeout(() => { + event_registry.fire("bot_status", { + status: "error", + error_msg: tr("load timeout") + }); + }, 5000); + }); + + event_registry.on("bot_status", event => clearTimeout(timeout)); + } + + /* set status timeout */ + { + let timeouts: {[key: string]:any} = {}; + event_registry.on("set_bot_status", event => { + clearTimeout(timeouts[event.key]); + timeouts[event.key] = setTimeout(() => { + event_registry.fire("set_bot_status_result", { + status: "timeout", + key: event.key, + }); + }, 5000); + }); + + event_registry.on("set_bot_status_result", event => { + clearTimeout(timeouts[event.key]); + delete timeouts[event.key]; + }); + } + } + + /* music bot settings */ + { + const container = tag.find(".settings-playlist"); + + /* playlist replay mode */ + { + const input = container.find(".option-replay-mode") as JQuery; + let last_value = undefined; + + const update_value = text => { + if(text) { + input.prop("disabled", true).addClass("disabled"); + input.val("-1"); + input.find("option[value=-1]").text(text); + } else if(last_value >= 0 && last_value <= 3) { + input + .prop("disabled", false) + .removeClass("disabled"); + input.val(last_value); + } else { + update_value(tr("invalid value")); + } + }; + + event_registry.on("query_playlist_status", event => { + last_value = undefined; + update_value(tr("loading...")); + }); + + event_registry.on("playlist_status", event => { + if(event.status === "error") { + update_value(event.error_msg || tr("error while loading")); + } else { + last_value = event.data.replay_mode; + update_value(undefined); + } + }); + + event_registry.on("set_playlist_status_result", event => { + if(event.key !== "replay_mode") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change replay mode"), event.error_msg || tr("timeout")); + else + last_value = event.value; + update_value(undefined); + }); + + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("change", event => { + const value = parseInt(input.val() as string); + console.log(value); + if(isNaN(value)) return; + + update_value(tr("applying...")); + event_registry.fire("set_playlist_status", { + key: "replay_mode", + value: value + }); + }); + } + + /* playlist max size */ + { + const input = container.find(".container-max-playlist-size input"); + let last_value = undefined; + + event_registry.on("query_playlist_status", event => { + last_value = undefined; + input + .prop("disabled", true) + .val(null) + .attr("placeholder", tr("loading...")) + .firstParent(".input-boxed").addClass("disabled"); + }); + + event_registry.on("playlist_status", event => { + if(event.status === "error") + input + .prop("disabled", true) + .val(null) + .attr("placeholder", event.error_msg || tr("error while loading")) + .firstParent(".input-boxed").addClass("disabled"); + else + input + .prop("disabled", false) + .attr("placeholder", null) + .val((last_value = event.data.max_size).toString()) + .firstParent(".input-boxed").removeClass("disabled"); + }); + + event_registry.on("set_playlist_status_result", event => { + if(event.key !== "max_size") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change max playlist size"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + input + .prop("disabled", false) + .attr("placeholder", null) + .val(last_value) + .firstParent(".input-boxed").removeClass("disabled"); + }); + + input.on("input", event => input.parentsUntil(".input-boxed").removeClass("is-invalid")); + input.on("keyup", event => event.key === "Enter" && input.trigger("focusout")); + input.on("focusout", event => { + const value = input.val() as string; + if(value === last_value) return; + if(value === "") { + input.val(last_value); + return; + } + if(isNaN(parseInt(value))) { + input.parentsUntil(".input-boxed").addClass("is-invalid"); + return; + } + + input + .prop("disabled", true) + .val(null) + .attr("placeholder", tr("applying...")) + .firstParent(".input-boxed").addClass("disabled"); + + event_registry.fire("set_playlist_status", { + key: "max_size", + value: parseInt(value) + }); + }); + } + + /* flag delete played */ + { + const input = container.find(".option-delete-played-songs") as JQuery; + const label = input.parents("label"); + + let last_value = undefined; + + event_registry.on("query_playlist_status", event => { + last_value = undefined; + + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + + event_registry.on("playlist_status", event => { + if(event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.delete_played) + .prop("disabled", false); + } + }); + + event_registry.on("set_playlist_status_result", event => { + if(event.key !== "delete_played") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change delete state"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_playlist_status", { + key: "delete_played", + value: input.prop("checked") + }); + }); + } + + /* flag notify song change */ + { + const input = container.find(".option-notify-songs-change") as JQuery; + const label = input.parents("label"); + + let last_value = undefined; + + event_registry.on("query_playlist_status", event => { + last_value = undefined; + + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + }); + + event_registry.on("playlist_status", event => { + if(event.status === "error") { + label.addClass("disabled"); + input + .prop("checked", false) + .prop("disabled", true); + } else { + label.removeClass("disabled"); + input + .prop("checked", last_value = event.data.notify_song_change) + .prop("disabled", false); + } + }); + + event_registry.on("set_playlist_status_result", event => { + if(event.key !== "notify_song_change") return; + + if(event.status !== "success") + show_change_error(tr("Failed to change notify state"), event.error_msg || tr("timeout")); + else + last_value = event.value; + + label.removeClass("disabled"); + input + .prop("checked", last_value) + .prop("disabled", false); + }); + + input.on("change", event => { + label.addClass("disabled"); + input.prop("disabled", true); + event_registry.fire("set_playlist_status", { + key: "notify_song_change", + value: input.prop("checked") + }); + }); + } + + /* status load timeout */ + { + let timeout; + event_registry.on("query_playlist_status", event => { + timeout = setTimeout(() => { + event_registry.fire("playlist_status", { + status: "error", + error_msg: tr("load timeout") + }); + }, 5000); + }); + + event_registry.on("playlist_status", event => clearTimeout(timeout)); + } + + /* set status timeout */ + { + let timeouts: {[key: string]:any} = {}; + event_registry.on("set_playlist_status", event => { + clearTimeout(timeouts[event.key]); + timeouts[event.key] = setTimeout(() => { + event_registry.fire("set_playlist_status_result", { + status: "timeout", + key: event.key, + }); + }, 5000); + }); + + event_registry.on("set_playlist_status_result", event => { + clearTimeout(timeouts[event.key]); + delete timeouts[event.key]; + }); + } + } + + /* reload button */ + { + const button = tag.find(".button-reload"); + let timeout; + + event_registry.on(["query_bot_status", "query_playlist_status"], event => { + button.prop("disabled", true); + + clearTimeout(timeout); + timeout = setTimeout(() => { + button.prop("disabled", false); + }, 1000); + }); + + button.on("click", event => { + event_registry.fire("query_bot_status"); + event_registry.fire("query_playlist_status"); + }); + } + + tooltip.initialize(tag); + + /* initialize on show */ + { + let initialized = false; + event_registry.on("show_container", event => { + if(event.container !== "settings" || initialized) return; + initialized = true; + + event_registry.fire("query_bot_status"); + event_registry.fire("query_playlist_status"); + }); + } +} + +function build_permission_container(event_registry: Registry, tag: JQuery) { + /* client search mechanism */ + { + const container = tag.find(".table-head .column-client-specific .client-select"); + let list_shown = false; + + /* search list show/hide */ + { + const button_list_clients = container.find(".button-list-clients"); + button_list_clients.on('click', event => + event_registry.fire(list_shown ? "hide_client_list" : "show_client_list")); + + event_registry.on("show_client_list", () => { + list_shown = true; + button_list_clients.text(tr("Hide clients")); + }); + + event_registry.on("hide_client_list", () => { + list_shown = false; + button_list_clients.text(tr("List clients")); + }); + } + + /* the search box */ + { + const input_search = container.find(".input-search"); + const button_search = container.find(".button-search"); + + let search_timeout; + let last_query; + input_search.on('keyup', event => { + const text = input_search.val() as string; + if(text === last_query) return; + + if(text) + event_registry.fire("filter_client_list", { filter: text }); + else + event_registry.fire("filter_client_list", { filter: undefined }); + + input_search.toggleClass("is-invalid", !list_shown && text === last_query); + if(!list_shown) { + button_search.prop("disabled", !text || !!search_timeout); + } else { + last_query = text; + } + }); + + input_search.on('keydown', event => { + if(event.key === "Enter" && !list_shown && !button_search.prop("disabled")) + button_search.trigger("click"); + }); + + event_registry.on("show_client_list", () => { + button_search.prop("disabled", true); + input_search.attr("placeholder", tr("Search client list")); + }); + + event_registry.on("hide_client_list", () => { + button_search.prop("disabled", !input_search.val() || !!search_timeout); + input_search.attr("placeholder", tr("Client uid or database id")); + }); + + button_search.on("click", event => { + button_search.prop("disabled", true); + + input_search.blur(); + const text = input_search.val() as string; + last_query = text; + event_registry.fire("search_client", { + text: text + }); + search_timeout = setTimeout(() => event_registry.fire("search_client_result", { + status: "timeout" + }), 5000); + }); + + event_registry.on("search_client_result", event => { + clearTimeout(search_timeout); + search_timeout = 0; + + button_search.prop("disabled", !input_search.val()); + if(event.status === "timeout") { + createErrorModal(tr("Client search failed"), tr("Failed to perform client search.
Search resulted in a timeout.")).open(); + return; + } else if(event.status === "error" || event.status === "empty") { + //TODO: Display the error somehow? + input_search.addClass("is-invalid"); + return; + } else { + event_registry.fire("special_client_set", { + client: event.client + }); + } + }); + } + + /* the client list */ + { + const container = tag.find(".overlay-client-list"); + + event_registry.on("show_client_list", () => container.removeClass("hidden")); + event_registry.on("hide_client_list", () => container.addClass("hidden")); + + const button_refresh = container.find(".button-clientlist-refresh"); + + const container_entries = container.find(".container-client-list"); + event_registry.on("special_client_list", data => { + button_refresh.prop("disabled", false); + container.find(".overlay").addClass("hidden"); + if(data.status === "error-permission") { + const overlay = container.find(".overlay-query-error-permissions"); + overlay.find("a").text(tr("Insufficient permissions")); + overlay.removeClass("hidden"); + } else if(data.status === "success") { + container_entries.find(".client").remove(); /* clear */ + + if(!data.clients.length) { + const overlay = container.find(".overlay-empty-list"); + overlay.removeClass("hidden"); + } else { + for(const client of data.clients) { + const tag = $.spawn("div").addClass("client").append( + htmltags.generate_client_object({ + add_braces: false, + client_id: 0, + client_database_id: client.database_id, + client_name: client.name, + client_unique_id: client.unique_id + }) + ); + tag.on('dblclick', event => event_registry.fire("special_client_set", { client: client })); + tag.attr("x-filter", client.database_id + "_" + client.name + "_" + client.unique_id); + container_entries.append(tag); + } + } + } else { + const overlay = container.find(".overlay-query-error"); + overlay.find("a").text(data.error_msg ? data.error_msg : tr("query failed")); + overlay.removeClass("hidden"); + } + }); + + /* refresh button */ + button_refresh.on('click', event => { + button_refresh.prop("disabled", true); + event_registry.fire("query_special_clients"); + }); + + + /* special client list query timeout handler */ + { + let query_timeout; + event_registry.on("query_special_clients", event => { + query_timeout = setTimeout(() => { + event_registry.fire("special_client_list", { + status: "error", + error_msg: tr("Query timeout") + }); + }, 5000); + }); + + event_registry.on("special_client_list", event => clearTimeout(query_timeout)); + } + + /* first time client list show */ + { + let shown; + event_registry.on('show_client_list', event => { + if(shown) return; + shown = true; + + event_registry.fire("query_special_clients"); + }); + } + + /* the client list filter */ + { + let filter; + + const overlay = container.find(".overlay-filter-no-result"); + const update_filter = () => { + let shown = 0, hidden = 0; + container_entries.find(".client").each(function () { + const text = this.getAttribute("x-filter"); + if(!filter || text.toLowerCase().indexOf(filter) != -1) { + this.classList.remove("hidden"); + shown++; + } else { + this.classList.add("hidden"); + hidden++; + } + }); + if(shown == 0 && hidden == 0) return; + overlay.toggleClass("hidden", shown != 0); }; - event_registry.on("special_client_set", event => { - last_sync_value = undefined; + event_registry.on("special_client_list", event => update_filter()); + event_registry.on("filter_client_list", event => { + filter = (event.filter || "").toLowerCase(); + update_filter(); }); - event_registry.on("general_permissions", event => update_indicator()); - event_registry.on("set_general_permission_result", event => { - if(event.key !== permission_needed_name) return; - if(event.status !== "success") return; + } + } - update_indicator(); - }); + event_registry.on("special_client_set", event => { + container.toggleClass("hidden", !!event.client); + event_registry.fire("hide_client_list"); + }); + } - /* loading the permission */ - event_registry.on("query_client_permissions", event => { - if(event.client_database_id !== client_database_id) return; + /* the client info */ + { + const container = tag.find(".table-head .column-client-specific .client-info"); - last_sync_value = undefined; - hide_indicator = true; + container.find(".button-client-deselect").on("click", event => { + event_registry.fire("special_client_set", { client: undefined }); + }); + + event_registry.on("special_client_set", event => { + container.toggleClass("hidden", !event.client); + + const client_container = container.find(".container-selected-client"); + client_container.find(".htmltag-client").remove(); + if(event.client) { + client_container.append(htmltags.generate_client_object({ + client_unique_id: event.client.unique_id, + client_name: event.client.name, + client_id: 0, + client_database_id: event.client.database_id, + add_braces: false + })); + } + }); + } + + const power_needed_map = { + i_client_music_rename_power: "i_client_music_needed_rename_power", + i_client_music_modify_power: "i_client_music_needed_modify_power", + i_client_music_delete_power: "i_client_music_needed_delete_power", + i_playlist_view_power: "i_playlist_needed_view_power", + i_playlist_modify_power: "i_playlist_needed_modify_power", + i_playlist_permission_modify_power: "i_playlist_needed_permission_modify_power", + i_playlist_song_add_power: "i_playlist_song_needed_add_power", + i_playlist_song_move_power: "i_playlist_song_needed_move_power", + i_playlist_song_remove_power: "i_playlist_song_needed_remove_power", + b_virtualserver_playlist_permission_list: "b_virtualserver_playlist_permission_list" + }; + const needed_power_map = Object.entries(power_needed_map).reduce((ret, entry) => { + const [key, value] = entry; + ret[value] = key; + return ret; + }, {}); + + /* general permissions */ + { + /* permission input functionality */ + { + tag.find(".general-permission").each((_, _e) => { + const elem = $(_e) as JQuery; + + const permission_name = elem.attr("x-permission"); + if(!permission_name) return; + + const input = elem.find("input"); + input.attr("maxlength", 6); + + let last_sync_value = undefined; + + event_registry.on("query_general_permissions", event => { input.prop("disabled", true).val(null); input.attr("placeholder", tr("loading...")); - update_indicator(); }); - event_registry.on('client_permissions', event => { - if(event.client_database_id !== client_database_id) return; - - hide_indicator = false; + event_registry.on("general_permissions", event => { input.prop("disabled", true).val(null); if(event.status === "timeout") { input.attr("placeholder", tr("load timeout")); @@ -1781,32 +1456,9 @@ namespace Modals { } else { input.attr("placeholder", event.error_msg || tr("load error")); } - update_indicator(); }); - /* permission editing */ - input.attr("maxlength", 6); - input.on("focusout", event => { - if(!client_database_id) return; - - const value = parseInt(input.val() as string); - if(value === last_sync_value) return; - - input.prop("disabled", true).val(null); - input.attr("placeholder", tr("applying...")); - event_registry.fire("set_client_permission", { - client_database_id: client_database_id, - key: permission_name, - value: value || 0 - }); - hide_indicator = true; - update_indicator(); - }); - - input.on("change", () => update_indicator()); - input.on("keyup", event => event.key === "Enter" && input.blur()); - - event_registry.on("set_client_permission_result", event => { + event_registry.on("set_general_permission_result", event => { if(event.key !== permission_name) return; input.prop("disabled", false); //TODO: Check permissions? @@ -1818,79 +1470,436 @@ namespace Modals { if(typeof last_sync_value === "number") input.val(last_sync_value); createErrorModal(tr("Failed to change permission"), tra("Failed to change permission:{:br:}{}", event.error_msg)).open(); } - hide_indicator = false; - update_indicator(); }); + + input.on("focusout", event => { + if(input.prop("disabled")) return; + + const value = parseInt(input.val() as string); + if(value === last_sync_value) return; + + input.prop("disabled", true).val(null); + input.attr("placeholder", tr("applying...")); + event_registry.fire("set_general_permission", { + key: permission_name, + value: value || 0 + }); + }); + input.on("keyup", event => event.key === "Enter" && input.blur()); }); - - /* client permission query timeout */ - { - let timeout: {[key: number]: any} = {}; - event_registry.on("query_client_permissions", event => { - if(timeout[event.client_database_id]) - clearTimeout(timeout[event.client_database_id]); - timeout[event.client_database_id] = setTimeout(() => { - event_registry.fire("client_permissions", { - status: "timeout", - client_database_id: event.client_database_id - }); - }, 5000); - }); - - event_registry.on("client_permissions", event => { - clearTimeout(timeout[event.client_database_id]); - }); - } - - /* client permission set timeout */ - { - let timeout: {[key: string]: any} = {}; - event_registry.on("set_client_permission", event => { - const key = event.client_database_id + "_" + event.key; - if(timeout[key]) - clearTimeout(timeout[key]); - - timeout[key] = setTimeout(() => { - event_registry.fire("set_client_permission_result", { - key: event.key, - status: "error", - client_database_id: event.client_database_id, - error_msg: tr("timeout") - }); - }, 5000); - }); - - event_registry.on("set_client_permission_result", event => { - const key = event.client_database_id + "_" + event.key; - if(timeout[key]) { - clearTimeout(timeout[key]); - delete timeout[key]; - } - }); - } - - event_registry.on("refresh_permissions", event => { - if(client_database_id) - event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); - }); - tooltip(container); } - /* a title attribute for permission column */ - tag.find(".table-body .column-permission a").each(function () { - this.setAttribute("title", this.textContent); - }); - - /* initialize on show */ + /* the tooltip functionality */ { - let initialized = false; - event_registry.on("show_container", event => { - if(event.container !== "permissions" || initialized) return; - initialized = true; + tag.find(".general-permission").each((_, _e) => { + const elem = $(_e) as JQuery; - event_registry.fire("special_client_set", { client: undefined }); - event_registry.fire("query_general_permissions", {}); + const permission_name = elem.attr("x-permission"); + if(!permission_name) return; + + const required_power = needed_power_map[permission_name]; + if(!required_power) return; + + let last_sync_value = undefined; + let current_tag: JQuery; + + let loading = false; + let query_result: { + status: "error" | "timeout" | "success" + groups?: { + name: string, + value: number, + id: number + }[], + error_msg?: string + }; + + event_registry.on("general_permissions", event => { + if(event.status === "success") + last_sync_value = event.permissions ? event.permissions[permission_name] || 0 : 0; + }); + + event_registry.on("set_general_permission_result", event => { + if(event.key !== permission_name) return; + + if(event.status === "success") + last_sync_value = event.value; + }); + + event_registry.on("refresh_permissions", event => { + query_result = undefined; /* require for the next time */ + }); + + const show_query_result = () => { + if(!current_tag) return; + + const container_groups = current_tag.find(".container-groups"); + container_groups.children().remove(); + current_tag.find(".container-status").addClass("hidden"); + + if(loading) { + current_tag.find(".status-loading").removeClass("hidden"); + } else if(!query_result || query_result.status === "error") { + current_tag + .find(".status-error").removeClass("hidden") + .text((query_result ? query_result.error_msg : "") || tr("failed to query data")); + } else if(query_result.status === "timeout") { + current_tag + .find(".status-error").removeClass("hidden") + .text(tr("timeout while loading")); + } else { + let count = 0; + for(const group of (query_result.groups || [])) { + if(group.value !== -1 && group.value < last_sync_value) continue; + + count++; + container_groups.append($.spawn("div").addClass("group").text( + " - " + group.name + " (" + group.id + ")" + )); + } + + if(count === 0) current_tag.find(".status-no-groups").removeClass("hidden"); + } + }; + + tooltip.initialize(elem, { + on_show(tag: JQuery) { + current_tag = tag; + + if(!query_result && !loading) { + event_registry.fire("query_group_permissions", { + permission_name: required_power + }); + loading = true; + } + show_query_result(); + }, + on_hide(tag: JQuery) { + current_tag = undefined; + } + }); + + event_registry.on("group_permissions", event => { + if(event.permission_name !== required_power) return; + + loading = false; + query_result = event; + show_query_result(); + }); + }); + + + /* refresh mechanism */ + { + event_registry.on("refresh_permissions", event => event_registry.fire("query_general_permissions")); + } + } + + /* permission set timeout */ + { + let permission_timers: {[key: string]:any} = {}; + event_registry.on("set_general_permission", event => { + if(permission_timers[event.key]) + clearTimeout(permission_timers[event.key]); + permission_timers[event.key] = setTimeout(() => { + event_registry.fire("set_general_permission_result", { + key: event.key, + status: "error", + error_msg: tr("controller timeout") + }); + }, 5000); + }); + + event_registry.on("set_general_permission_result", event => { + clearTimeout(permission_timers[event.key]); + delete permission_timers[event.key]; + }); + } + + /* group query timeout */ + { + let timers: {[key: string]:any} = {}; + event_registry.on("query_group_permissions", event => { + if(timers[event.permission_name]) + clearTimeout(timers[event.permission_name]); + timers[event.permission_name] = setTimeout(() => { + event_registry.fire("group_permissions", { + permission_name: event.permission_name, + status: "timeout" + }); + }, 5000); + }); + + event_registry.on("group_permissions", event => { + clearTimeout(timers[event.permission_name]); + delete timers[event.permission_name]; + }); + } + + /* query timeout */ + { + let query_timeout; + event_registry.on("query_general_permissions", event => { + clearTimeout(query_timeout); + query_timeout = setTimeout(() => { + event_registry.fire("general_permissions", { + status: "timeout" + }); + }, 5000); + }); + + event_registry.on("general_permissions", event => clearTimeout(query_timeout)); + } + + /* refresh button */ + { + const button = tag.find(".button-permission-refresh"); + let refresh_timer; + + let loading_client_permissions = false; + let loading_general_permissions = false; + + const update_button = () => + button.prop("disabled", refresh_timer || loading_client_permissions || loading_general_permissions); + + event_registry.on("query_general_permissions", event => { + loading_general_permissions = true; + update_button(); + }); + + event_registry.on("general_permissions", event => { + loading_general_permissions = false; + update_button(); + }); + + event_registry.on("query_client_permissions", event => { + loading_client_permissions = true; + update_button(); + }); + + event_registry.on("client_permissions", event => { + loading_client_permissions = false; + update_button(); + }); + + button.on('click', event => { + event_registry.fire("refresh_permissions"); + + /* allow refreshes only every second */ + refresh_timer = setTimeout(() => { + refresh_timer = undefined; + update_button(); + }, 1000); }); } } + + /* client specific permissions */ + { + const container = tag.find(".column-client-specific"); + + let client_database_id = 0; + let needed_permissions: {[key: string]:number} = {}; + + /* needed permissions updater */ + { + event_registry.on("general_permissions", event => { + if(event.status !== "success") return; + + needed_permissions = event.permissions; + }); + + event_registry.on("set_general_permission_result", event => { + if (event.status !== "success") return; + + needed_permissions[event.key] = event.value; + }); + } + + event_registry.on("special_client_set", event => { + client_database_id = event.client ? event.client.database_id : 0; + container.find(".client-permission").toggleClass("hidden", !event.client); + + if(client_database_id) + event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); + }); + + const enabled_class = "client-apply"; + const disabled_class = "client-delete"; + + container.find(".client-permission").each((_, _e) => { + const elem = $(_e); + + const input = elem.find("input"); + const status_indicator = elem.find(".icon_em"); + + const permission_name = elem.attr("x-permission") as string; + const permission_needed_name = power_needed_map[permission_name]; + + let last_sync_value = undefined; + let hide_indicator = false; + + if(typeof permission_needed_name !== "string") { + log.warn(LogCategory.GENERAL, tr("Missing permission needed mapping for %s"), permission_name); + return; + } + + const update_indicator = () => { + const value = parseInt(input.val() as string); + const needed = typeof needed_permissions[permission_needed_name] === "number" ? needed_permissions[permission_needed_name] : 0; + const flag = value == -1 ? true : isNaN(value) || value == 0 ? false : value >= needed; + + status_indicator.toggle(!hide_indicator); + status_indicator.toggleClass(enabled_class, flag).toggleClass(disabled_class, !flag); + }; + + event_registry.on("special_client_set", event => { + last_sync_value = undefined; + }); + event_registry.on("general_permissions", event => update_indicator()); + event_registry.on("set_general_permission_result", event => { + if(event.key !== permission_needed_name) return; + if(event.status !== "success") return; + + update_indicator(); + }); + + /* loading the permission */ + event_registry.on("query_client_permissions", event => { + if(event.client_database_id !== client_database_id) return; + + last_sync_value = undefined; + hide_indicator = true; + input.prop("disabled", true).val(null); + input.attr("placeholder", tr("loading...")); + update_indicator(); + }); + + event_registry.on('client_permissions', event => { + if(event.client_database_id !== client_database_id) return; + + hide_indicator = false; + input.prop("disabled", true).val(null); + if(event.status === "timeout") { + input.attr("placeholder", tr("load timeout")); + } else if(event.status === "success") { + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + const value = event.permissions ? event.permissions[permission_name] || 0 : 0; + last_sync_value = value; + input.val(value); + } else { + input.attr("placeholder", event.error_msg || tr("load error")); + } + update_indicator(); + }); + + /* permission editing */ + input.attr("maxlength", 6); + input.on("focusout", event => { + if(!client_database_id) return; + + const value = parseInt(input.val() as string); + if(value === last_sync_value) return; + + input.prop("disabled", true).val(null); + input.attr("placeholder", tr("applying...")); + event_registry.fire("set_client_permission", { + client_database_id: client_database_id, + key: permission_name, + value: value || 0 + }); + hide_indicator = true; + update_indicator(); + }); + + input.on("change", () => update_indicator()); + input.on("keyup", event => event.key === "Enter" && input.blur()); + + event_registry.on("set_client_permission_result", event => { + if(event.key !== permission_name) return; + + input.prop("disabled", false); //TODO: Check permissions? + input.attr("placeholder", null); + if(event.status === "success") { + input.val(event.value); + last_sync_value = event.value; + } else if(event.status === "error") { + if(typeof last_sync_value === "number") input.val(last_sync_value); + createErrorModal(tr("Failed to change permission"), tra("Failed to change permission:{:br:}{}", event.error_msg)).open(); + } + hide_indicator = false; + update_indicator(); + }); + }); + + /* client permission query timeout */ + { + let timeout: {[key: number]: any} = {}; + event_registry.on("query_client_permissions", event => { + if(timeout[event.client_database_id]) + clearTimeout(timeout[event.client_database_id]); + timeout[event.client_database_id] = setTimeout(() => { + event_registry.fire("client_permissions", { + status: "timeout", + client_database_id: event.client_database_id + }); + }, 5000); + }); + + event_registry.on("client_permissions", event => { + clearTimeout(timeout[event.client_database_id]); + }); + } + + /* client permission set timeout */ + { + let timeout: {[key: string]: any} = {}; + event_registry.on("set_client_permission", event => { + const key = event.client_database_id + "_" + event.key; + if(timeout[key]) + clearTimeout(timeout[key]); + + timeout[key] = setTimeout(() => { + event_registry.fire("set_client_permission_result", { + key: event.key, + status: "error", + client_database_id: event.client_database_id, + error_msg: tr("timeout") + }); + }, 5000); + }); + + event_registry.on("set_client_permission_result", event => { + const key = event.client_database_id + "_" + event.key; + if(timeout[key]) { + clearTimeout(timeout[key]); + delete timeout[key]; + } + }); + } + + event_registry.on("refresh_permissions", event => { + if(client_database_id) + event_registry.fire("query_client_permissions", { client_database_id: client_database_id }); + }); + tooltip.initialize(container); + } + + /* a title attribute for permission column */ + tag.find(".table-body .column-permission a").each(function () { + this.setAttribute("title", this.textContent); + }); + + /* initialize on show */ + { + let initialized = false; + event_registry.on("show_container", event => { + if(event.container !== "permissions" || initialized) return; + initialized = true; + + event_registry.fire("special_client_set", { client: undefined }); + event_registry.fire("query_general_permissions", {}); + }); + } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalNewcomer.ts b/shared/js/ui/modal/ModalNewcomer.ts index 20f414a8..b537573c 100644 --- a/shared/js/ui/modal/ModalNewcomer.ts +++ b/shared/js/ui/modal/ModalNewcomer.ts @@ -1,434 +1,437 @@ -/// -/// -/// +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {tra} from "tc-shared/i18n/localize"; +import {Registry} from "tc-shared/events"; +import * as loader from "tc-loader"; +import { modal as emodal } from "tc-shared/events"; +import {modal_settings} from "tc-shared/ui/modal/ModalSettings"; +import {profiles} from "tc-shared/profiles/ConnectionProfile"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; -namespace Modals { - const next_step: {[key: string]:string} = { - "welcome": "microphone", - //"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */ - "microphone": "identity", - "speaker": "identity", - "identity": "finish" - }; - const last_step: {[key: string]:string} = (() => { - const result = {}; - for(const key of Object.keys(next_step)) - if(!result[next_step[key]]) - result[next_step[key]] = key; - return result; - })(); +const next_step: {[key: string]:string} = { + "welcome": "microphone", + //"microphone": app.is_web() ? "identity" : "speaker", /* speaker setup only for the native client! */ + "microphone": "identity", + "speaker": "identity", + "identity": "finish" +}; +const last_step: {[key: string]:string} = (() => { + const result = {}; + for(const key of Object.keys(next_step)) + if(!result[next_step[key]]) + result[next_step[key]] = key; + return result; +})(); - export function openModalNewcomer() : Modal { - let modal = createModal({ - header: tra("Welcome to the {}", app.is_web() ? "TeaSpeak - Web client" : "TeaSpeak - Client"), - body: () => $("#tmpl_newcomer").renderTag({ - is_web: app.is_web() - }).children(), - footer: null, +export function openModalNewcomer() : Modal { + let modal = createModal({ + header: tra("Welcome to the {}", loader.version().type === "web" ? "TeaSpeak - Web client" : "TeaSpeak - Client"), + body: () => $("#tmpl_newcomer").renderTag({ + is_web: loader.version().type === "web" + }).children(), + footer: null, - width: "", - closeable: false - }); + width: "", + closeable: false + }); - const event_registry = new events.Registry(); - event_registry.enable_debug("newcomer"); + const event_registry = new Registry(); + event_registry.enable_debug("newcomer"); - modal.htmlTag.find(".modal-body").addClass("modal-newcomer"); + modal.htmlTag.find(".modal-body").addClass("modal-newcomer"); - initializeBasicFunctionality(modal.htmlTag, event_registry); - initializeStepWelcome(modal.htmlTag.find(".container-body .step.step-welcome"), event_registry); - initializeStepIdentity(modal.htmlTag.find(".container-body .step.step-identity"), event_registry); - initializeStepMicrophone(modal.htmlTag.find(".container-body .step.step-microphone"), event_registry, modal); - initializeStepFinish(modal.htmlTag.find(".container-body .step.step-finish"), event_registry); + initializeBasicFunctionality(modal.htmlTag, event_registry); + initializeStepWelcome(modal.htmlTag.find(".container-body .step.step-welcome"), event_registry); + initializeStepIdentity(modal.htmlTag.find(".container-body .step.step-identity"), event_registry); + initializeStepMicrophone(modal.htmlTag.find(".container-body .step.step-microphone"), event_registry, modal); + initializeStepFinish(modal.htmlTag.find(".container-body .step.step-finish"), event_registry); - event_registry.on("exit_guide", event => { - if(event.ask_yesno) - Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to skip the basic setup guide?"), result => { - if(result) - event_registry.fire("exit_guide", {ask_yesno: false}); - }); - else - modal.close(); - }); - - event_registry.fire("show_step", {step: "welcome"}); - modal.open(); - event_registry.fire_async("modal-shown"); - return modal; - } - - function initializeBasicFunctionality(tag: JQuery, event_registry: events.Registry) { - const container_header = tag.find(".container-header"); - const tag_body = tag.find(".container-body .body"); - - /* step navigation */ - event_registry.on("show_step", event => { - tag_body.find(".step").addClass("hidden"); - container_header.find(".step").addClass("hidden"); - - tag_body.find(".step.step-" + event.step).removeClass("hidden"); - container_header.find(".step.step-" + event.step).removeClass("hidden"); - }); - - /* button controller */ - { - const buttons = tag.find(".buttons"); - const button_last_step = buttons.find(".button-last-step"); - const button_next_step = buttons.find(".button-next-step"); - - button_last_step.on('click', event => { - if(last_step[current_step]) - event_registry.fire("show_step", { step: last_step[current_step] as any }); - else - event_registry.fire("exit_guide", {ask_yesno: true}); - }); - - let current_step; - button_next_step.on('click', event => { - if(next_step[current_step]) - event_registry.fire("show_step", { step: next_step[current_step] as any }); - else + event_registry.on("exit_guide", event => { + if(event.ask_yesno) + spawnYesNo(tr("Are you sure?"), tr("Do you really want to skip the basic setup guide?"), result => { + if(result) event_registry.fire("exit_guide", {ask_yesno: false}); }); + else + modal.close(); + }); - event_registry.on("show_step", event => { - current_step = event.step; - button_next_step.text(next_step[current_step] ? tr("Next step") : tr("Finish guide")); - button_last_step.text(last_step[current_step] ? tr("Last step") : tr("Skip guide")); - }); + event_registry.fire("show_step", {step: "welcome"}); + modal.open(); + event_registry.fire_async("modal-shown"); + return modal; +} - event_registry.on("show_step", event => button_next_step.prop("disabled", true)); - event_registry.on("show_step", event => button_last_step.prop("disabled", true)); +function initializeBasicFunctionality(tag: JQuery, event_registry: Registry) { + const container_header = tag.find(".container-header"); + const tag_body = tag.find(".container-body .body"); - event_registry.on("step-status", event => button_next_step.prop("disabled", !event.next_button)); - event_registry.on("step-status", event => button_last_step.prop("disabled", !event.previous_button)); - } - } + /* step navigation */ + event_registry.on("show_step", event => { + tag_body.find(".step").addClass("hidden"); + container_header.find(".step").addClass("hidden"); - function initializeStepWelcome(tag: JQuery, event_registry: events.Registry) { - event_registry.on("show_step", e => { - if(e.step !== "welcome") return; + tag_body.find(".step.step-" + event.step).removeClass("hidden"); + container_header.find(".step.step-" + event.step).removeClass("hidden"); + }); - event_registry.fire_async("step-status", { next_button: true, previous_button: true }); + /* button controller */ + { + const buttons = tag.find(".buttons"); + const button_last_step = buttons.find(".button-last-step"); + const button_next_step = buttons.find(".button-next-step"); + + button_last_step.on('click', event => { + if(last_step[current_step]) + event_registry.fire("show_step", { step: last_step[current_step] as any }); + else + event_registry.fire("exit_guide", {ask_yesno: true}); }); - } - function initializeStepFinish(tag: JQuery, event_registry: events.Registry) { - event_registry.on("show_step", e => { - if(e.step !== "finish") return; - - event_registry.fire_async("step-status", {next_button: true, previous_button: true }); + let current_step; + button_next_step.on('click', event => { + if(next_step[current_step]) + event_registry.fire("show_step", { step: next_step[current_step] as any }); + else + event_registry.fire("exit_guide", {ask_yesno: false}); }); + + event_registry.on("show_step", event => { + current_step = event.step; + button_next_step.text(next_step[current_step] ? tr("Next step") : tr("Finish guide")); + button_last_step.text(last_step[current_step] ? tr("Last step") : tr("Skip guide")); + }); + + event_registry.on("show_step", event => button_next_step.prop("disabled", true)); + event_registry.on("show_step", event => button_last_step.prop("disabled", true)); + + event_registry.on("step-status", event => button_next_step.prop("disabled", !event.next_button)); + event_registry.on("step-status", event => button_last_step.prop("disabled", !event.previous_button)); } +} - function initializeStepIdentity(tag: JQuery, event_registry: events.Registry) { - const profile_events = new events.Registry(); - profile_events.enable_debug("settings-identity"); - modal_settings.initialize_identity_profiles_controller(profile_events); - modal_settings.initialize_identity_profiles_view(tag, profile_events, { forum_setuppable: false }); +function initializeStepWelcome(tag: JQuery, event_registry: Registry) { + event_registry.on("show_step", e => { + if(e.step !== "welcome") return; - let step_shown = false; - let help_animation_done = false; - const profiles_valid = () => profiles.profiles().findIndex(e => e.valid()) !== -1; - const update_step_status = () => { - event_registry.fire_async("step-status", { next_button: help_animation_done && profiles_valid(), previous_button: help_animation_done }); + event_registry.fire_async("step-status", { next_button: true, previous_button: true }); + }); +} + +function initializeStepFinish(tag: JQuery, event_registry: Registry) { + event_registry.on("show_step", e => { + if(e.step !== "finish") return; + + event_registry.fire_async("step-status", {next_button: true, previous_button: true }); + }); +} + +function initializeStepIdentity(tag: JQuery, event_registry: Registry) { + const profile_events = new Registry(); + profile_events.enable_debug("settings-identity"); + modal_settings.initialize_identity_profiles_controller(profile_events); + modal_settings.initialize_identity_profiles_view(tag, profile_events, { forum_setuppable: false }); + + let step_shown = false; + let help_animation_done = false; + const profiles_valid = () => profiles().findIndex(e => e.valid()) !== -1; + const update_step_status = () => { + event_registry.fire_async("step-status", { next_button: help_animation_done && profiles_valid(), previous_button: help_animation_done }); + }; + profile_events.on("query-profile-validity-result", event => step_shown && event.status === "success" && event.valid && update_step_status()); + event_registry.on("show_step", e => { + step_shown = e.step === "identity"; + if(!step_shown) return; + + update_step_status(); + }); + + /* the help sequence */ + { + const container = tag.find(".container-settings-identity-profile"); + const container_help_text = tag.find(".container-help-text"); + + const container_profile_list = tag.find(".highlight-profile-list"); + const container_profile_settings = tag.find(".highlight-profile-settings"); + const container_identity_settings = tag.find(".highlight-identity-settings"); + + let is_first_show = true; + + event_registry.on("show_step", event => { + if(!is_first_show || event.step !== "identity") return; + is_first_show = false; + + container.addClass("help-shown"); + + + const text = tr( + "After you've successfully set upped your microphone,\n" + + "lets setup some profiles and identities!\n" + + "\n" + + "Connect profiles determine, how your're authenticating yourself with the server.\n" + + "So basically they're your identity.\n" + + "In the following I'll guid you thru the options and GUI elements.\n" + + "\n" + + "To continue click anywhere on the screen." + ); + set_help_text(text); + $("body").one('mousedown', event => show_profile_list_help()); + }); + + const set_help_text = text => { + container_help_text.empty(); + text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); }; - profile_events.on("query-profile-validity-result", event => step_shown && event.status === "success" && event.valid && update_step_status()); - event_registry.on("show_step", e => { - step_shown = e.step === "identity"; - if(!step_shown) return; + const show_profile_list_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_list.addClass("highlighted"); + + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + + const offset = container_profile_list.offset(); + const abs = container.offset(); + + container_help_text.css({ + top: offset.top - abs.top, + left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", + right: "1em", + bottom: "1em" + }); + }; + update_position(); + container_help_text.off('resize').on('resize', update_position); + + const text = tr( + "You could have as many connect profiles as you want.\n" + + "All created profiles will be listed here.\n" + + "\n" + + "To create a new profile just simply click the blue button \"Create profile\" and enter a profile name.\n" + + "If you want to delete a profile you've to select that profile and click the delete button.\n" + + "\n" + + "By default we're using the \"default\" profile\n" + + "to connect to any server. o change the default profile\n" + + "just select the new profile and press the \"select as default\" button.\n" + + "\n" + + "To continue click anywhere on the screen." + ); + set_help_text(text); + $("body").one('mousedown', event => show_profile_settings_help()); + }; + + const show_profile_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_settings.addClass("highlighted"); + + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_settings_offset = container_profile_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_settings_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); + }; + set_help_text(tr( + "In the upper left, you'll find the profile settings for the selected profile.\n" + + "You could give each profile an individual name. You could also specify the default connect nickname here.\n" + + "\n" + + "The last option \"Identity Type\" determines on what your identity is based on.\n" + + "TeaSpeak has two possibilities to identify yourself:\n" + + "1. Identify yourself by your TeaSpeak forum account\n" + + "2. Identify by an own generated cryptographic identity\n" + + "The second methods is also known as a TeamSpeak 3 identity.\n" + + "\n" + + "To continue click anywhere on the screen." + )); + update_position(); + container_help_text.off('resize').on('resize', update_position); + + $("body").one('mousedown', event => show_identity_settings_help()); + }; + + const show_identity_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_identity_settings.addClass("highlighted"); + + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_identity_offset = container_identity_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_identity_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); + }; + set_help_text(tr( + "When selecting an identify type, some corresponding will pop up in the highlighted area.\n" + + "\n" + + "But don't worry, we've already generated\n" + + "a cryptographic identity for you!\n" + + "So you don't have to change anything before you start." + )); + update_position(); + container_help_text.off('resize').on('resize', update_position); + + $("body").one('mousedown', event => hide_help()); + }; + + const hide_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container.addClass("hide-help"); + setTimeout(() => container.removeClass("help-shown"), 1000); + container_help_text.off('resize'); + + help_animation_done = true; update_step_status(); - }); - - /* the help sequence */ - { - const container = tag.find(".container-settings-identity-profile"); - const container_help_text = tag.find(".container-help-text"); - - const container_profile_list = tag.find(".highlight-profile-list"); - const container_profile_settings = tag.find(".highlight-profile-settings"); - const container_identity_settings = tag.find(".highlight-identity-settings"); - - let is_first_show = true; - - event_registry.on("show_step", event => { - if(!is_first_show || event.step !== "identity") return; - is_first_show = false; - - container.addClass("help-shown"); - - - const text = tr( - "After you've successfully set upped your microphone,\n" + - "lets setup some profiles and identities!\n" + - "\n" + - "Connect profiles determine, how your're authenticating yourself with the server.\n" + - "So basically they're your identity.\n" + - "In the following I'll guid you thru the options and GUI elements.\n" + - "\n" + - "To continue click anywhere on the screen." - ); - set_help_text(text); - $("body").one('mousedown', event => show_profile_list_help()); - }); - - const set_help_text = text => { - container_help_text.empty(); - text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); - }; - - const show_profile_list_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container_profile_list.addClass("highlighted"); - - const update_position = () => { - const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - - const offset = container_profile_list.offset(); - const abs = container.offset(); - - container_help_text.css({ - top: offset.top - abs.top, - left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", - right: "1em", - bottom: "1em" - }); - }; - update_position(); - container_help_text.off('resize').on('resize', update_position); - - const text = tr( - "You could have as many connect profiles as you want.\n" + - "All created profiles will be listed here.\n" + - "\n" + - "To create a new profile just simply click the blue button \"Create profile\" and enter a profile name.\n" + - "If you want to delete a profile you've to select that profile and click the delete button.\n" + - "\n" + - "By default we're using the \"default\" profile\n" + - "to connect to any server. o change the default profile\n" + - "just select the new profile and press the \"select as default\" button.\n" + - "\n" + - "To continue click anywhere on the screen." - ); - set_help_text(text); - $("body").one('mousedown', event => show_profile_settings_help()); - }; - - const show_profile_settings_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container_profile_settings.addClass("highlighted"); - - const update_position = () => { - const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - const container_settings_offset = container_profile_settings.offset(); - const right = container_profile_settings.outerWidth() + font_size * 2; - container_help_text.css({ - top: container_settings_offset.top - container.offset().top, - left: "1em", - right: right + "px", - bottom: "1em" - }); - }; - set_help_text(tr( - "In the upper left, you'll find the profile settings for the selected profile.\n" + - "You could give each profile an individual name. You could also specify the default connect nickname here.\n" + - "\n" + - "The last option \"Identity Type\" determines on what your identity is based on.\n" + - "TeaSpeak has two possibilities to identify yourself:\n" + - "1. Identify yourself by your TeaSpeak forum account\n" + - "2. Identify by an own generated cryptographic identity\n" + - "The second methods is also known as a TeamSpeak 3 identity.\n" + - "\n" + - "To continue click anywhere on the screen." - )); - update_position(); - container_help_text.off('resize').on('resize', update_position); - - $("body").one('mousedown', event => show_identity_settings_help()); - }; - - const show_identity_settings_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container_identity_settings.addClass("highlighted"); - - const update_position = () => { - const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - const container_identity_offset = container_identity_settings.offset(); - const right = container_profile_settings.outerWidth() + font_size * 2; - container_help_text.css({ - top: container_identity_offset.top - container.offset().top, - left: "1em", - right: right + "px", - bottom: "1em" - }); - }; - set_help_text(tr( - "When selecting an identify type, some corresponding will pop up in the highlighted area.\n" + - "\n" + - "But don't worry, we've already generated\n" + - "a cryptographic identity for you!\n" + - "So you don't have to change anything before you start." - )); - update_position(); - container_help_text.off('resize').on('resize', update_position); - - $("body").one('mousedown', event => hide_help()); - }; - - const hide_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container.addClass("hide-help"); - setTimeout(() => container.removeClass("help-shown"), 1000); - container_help_text.off('resize'); - - help_animation_done = true; - update_step_status(); - }; - } + }; } +} - function initializeStepMicrophone(tag: JQuery, event_registry: events.Registry, modal: Modal) { - const microphone_events = new events.Registry(); - //microphone_events.enable_debug("settings-microphone"); - modal_settings.initialize_audio_microphone_controller(microphone_events); - modal_settings.initialize_audio_microphone_view(tag, microphone_events); - modal.close_listener.push(() => microphone_events.fire_async("deinitialize")); +function initializeStepMicrophone(tag: JQuery, event_registry: Registry, modal: Modal) { + const microphone_events = new Registry(); + //microphone_events.enable_debug("settings-microphone"); + modal_settings.initialize_audio_microphone_controller(microphone_events); + modal_settings.initialize_audio_microphone_view(tag, microphone_events); + modal.close_listener.push(() => microphone_events.fire_async("deinitialize")); - let help_animation_done = false; - const update_step_status = () => event_registry.fire_async("step-status", { next_button: help_animation_done, previous_button: help_animation_done }); - event_registry.on("show_step", e => { - if(e.step !== "microphone") return; + let help_animation_done = false; + const update_step_status = () => event_registry.fire_async("step-status", { next_button: help_animation_done, previous_button: help_animation_done }); + event_registry.on("show_step", e => { + if(e.step !== "microphone") return; - update_step_status(); + update_step_status(); + }); + + /* the help sequence */ + { + const container = tag.find(".container-settings-audio-microphone"); + const container_help_text = tag.find(".container-help-text"); + + const container_profile_list = tag.find(".highlight-microphone-list"); + const container_profile_settings = tag.find(".highlight-microphone-settings"); + + let is_first_show = true; + event_registry.on("show_step", event => { + if(!is_first_show || event.step !== "microphone") return; + is_first_show = false; + + container.addClass("help-shown"); + const text = tr( + "Firstly we need to setup a microphone.\n" + + "Let me guide you thru the basic UI elements.\n" + + "\n" + + "To continue click anywhere on the screen." + ); + set_help_text(text); + $("body").one('mousedown', event => show_microphone_list_help()); }); - /* the help sequence */ - { - const container = tag.find(".container-settings-audio-microphone"); - const container_help_text = tag.find(".container-help-text"); + const set_help_text = text => { + container_help_text.empty(); + text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); + }; - const container_profile_list = tag.find(".highlight-microphone-list"); - const container_profile_settings = tag.find(".highlight-microphone-settings"); + const show_microphone_list_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_list.addClass("highlighted"); - let is_first_show = true; - event_registry.on("show_step", event => { - if(!is_first_show || event.step !== "microphone") return; - is_first_show = false; + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - container.addClass("help-shown"); - const text = tr( - "Firstly we need to setup a microphone.\n" + - "Let me guide you thru the basic UI elements.\n" + - "\n" + - "To continue click anywhere on the screen." - ); - set_help_text(text); - $("body").one('mousedown', event => show_microphone_list_help()); - }); + const offset = container_profile_list.offset(); + const abs = container.offset(); - const set_help_text = text => { - container_help_text.empty(); - text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e))); + container_help_text.css({ + top: offset.top - abs.top, + left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", + right: "1em", + bottom: "1em" + }); + }; + update_position(); + container_help_text.off('resize').on('resize', update_position); + + const text = tr( + "All your available microphones are listed within this box.\n" + + "\n" + + "The currently selected microphone\n" + + "is marked with a green checkmark. To change the selected microphone\n" + + "just click on the new one.\n" + + "\n" + + "To continue click anywhere on the screen." + ); + set_help_text(text); + $("body").one('mousedown', event => show_microphone_settings_help()); + }; + + const show_microphone_settings_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container_profile_settings.addClass("highlighted"); + + const update_position = () => { + const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); + const container_settings_offset = container_profile_settings.offset(); + const right = container_profile_settings.outerWidth() + font_size * 2; + container_help_text.css({ + top: container_settings_offset.top - container.offset().top, + left: "1em", + right: right + "px", + bottom: "1em" + }); }; - const show_microphone_list_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container_profile_list.addClass("highlighted"); - - const update_position = () => { - const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - - const offset = container_profile_list.offset(); - const abs = container.offset(); - - container_help_text.css({ - top: offset.top - abs.top, - left: ((offset.left - abs.left) + container_profile_list.outerWidth() + font_size) + "px", - right: "1em", - bottom: "1em" - }); - }; - update_position(); - container_help_text.off('resize').on('resize', update_position); - - const text = tr( - "All your available microphones are listed within this box.\n" + - "\n" + - "The currently selected microphone\n" + - "is marked with a green checkmark. To change the selected microphone\n" + - "just click on the new one.\n" + - "\n" + - "To continue click anywhere on the screen." - ); - set_help_text(text); - $("body").one('mousedown', event => show_microphone_settings_help()); - }; - - const show_microphone_settings_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container_profile_settings.addClass("highlighted"); - - const update_position = () => { - const font_size = parseFloat(getComputedStyle(container_help_text[0]).fontSize); - const container_settings_offset = container_profile_settings.offset(); - const right = container_profile_settings.outerWidth() + font_size * 2; - container_help_text.css({ - top: container_settings_offset.top - container.offset().top, - left: "1em", - right: right + "px", - bottom: "1em" - }); - }; - - container_help_text.empty(); - container_help_text.append($.spawn("div").addClass("help-microphone-settings").append( - $.spawn("a").text(tr("On the right side you'll find all microphone settings.")), - $.spawn("br"), - $.spawn("a").text("TeaSpeak has three voice activity detection types:"), - $.spawn("ol").append( - $.spawn("li").addClass("vad-type").append( - $.spawn("a").addClass("title").text(tr("Push to Talk")), - $.spawn("a").addClass("description").html(tr( - "To transmit audio data you'll have to
" + - "press a key. The key could be selected " + - "via the button right to the radio button." - )) - ), - $.spawn("li").addClass("vad-type").append( - $.spawn("a").addClass("title").text(tr("Voice activity detection")), - $.spawn("a").addClass("description").html(tr( - "In this mode, TeaSpeak will continuously analyze your microphone input. " + - "If the audio level is grater than a certain threshold, " + - "the audio will be transmitted. " + - "The threshold is changeable via the \"Sensitivity Settings\" slider." - )) - ), - $.spawn("li").addClass("vad-type").append( - $.spawn("a").addClass("title").html(tr("Always active")), - $.spawn("a").addClass("description").text(tr( - "Continuously transmit any audio data.\n" - )) - ) + container_help_text.empty(); + container_help_text.append($.spawn("div").addClass("help-microphone-settings").append( + $.spawn("a").text(tr("On the right side you'll find all microphone settings.")), + $.spawn("br"), + $.spawn("a").text("TeaSpeak has three voice activity detection types:"), + $.spawn("ol").append( + $.spawn("li").addClass("vad-type").append( + $.spawn("a").addClass("title").text(tr("Push to Talk")), + $.spawn("a").addClass("description").html(tr( + "To transmit audio data you'll have to
" + + "press a key. The key could be selected " + + "via the button right to the radio button." + )) ), - $.spawn("br"), - $.spawn("a").text(tr("Now you're ready to configure your microphone. Just click anywhere on the screen.")) - )); - update_position(); - container_help_text.off('resize').on('resize', update_position); + $.spawn("li").addClass("vad-type").append( + $.spawn("a").addClass("title").text(tr("Voice activity detection")), + $.spawn("a").addClass("description").html(tr( + "In this mode, TeaSpeak will continuously analyze your microphone input. " + + "If the audio level is grater than a certain threshold, " + + "the audio will be transmitted. " + + "The threshold is changeable via the \"Sensitivity Settings\" slider." + )) + ), + $.spawn("li").addClass("vad-type").append( + $.spawn("a").addClass("title").html(tr("Always active")), + $.spawn("a").addClass("description").text(tr( + "Continuously transmit any audio data.\n" + )) + ) + ), + $.spawn("br"), + $.spawn("a").text(tr("Now you're ready to configure your microphone. Just click anywhere on the screen.")) + )); + update_position(); + container_help_text.off('resize').on('resize', update_position); - $("body").one('mousedown', event => hide_help()); - }; + $("body").one('mousedown', event => hide_help()); + }; - const hide_help = () => { - container.find(".highlighted").removeClass("highlighted"); - container.addClass("hide-help"); - setTimeout(() => container.removeClass("help-shown"), 1000); - container_help_text.off('resize'); + const hide_help = () => { + container.find(".highlighted").removeClass("highlighted"); + container.addClass("hide-help"); + setTimeout(() => container.removeClass("help-shown"), 1000); + container_help_text.off('resize'); - help_animation_done = true; - update_step_status(); - }; - } + help_animation_done = true; + update_step_status(); + }; } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalPlaylistEdit.ts b/shared/js/ui/modal/ModalPlaylistEdit.ts index 69ec39c5..35c8533f 100644 --- a/shared/js/ui/modal/ModalPlaylistEdit.ts +++ b/shared/js/ui/modal/ModalPlaylistEdit.ts @@ -1,360 +1,360 @@ -/// -/// -/// +import {CommandResult, Playlist, PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration"; +import {createErrorModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import PermissionType from "tc-shared/permission/PermissionType"; -namespace Modals { - export function spawnPlaylistSongInfo(song: PlaylistSong) { - let modal: Modal; +export function spawnPlaylistSongInfo(song: PlaylistSong) { + let modal: Modal; - modal = createModal({ - header: tr("Song info"), - body: () => { - try { - (song).metadata = JSON.parse(song.song_metadata); - } catch(e) {} + modal = createModal({ + header: tr("Song info"), + body: () => { + try { + (song).metadata = JSON.parse(song.song_metadata); + } catch(e) {} - let template = $("#tmpl_playlist_edit-song_info").renderTag(song); - template = $.spawn("div").append(template); - const text_area = template.find(".property-metadata-raw textarea"); + let template = $("#tmpl_playlist_edit-song_info").renderTag(song); + template = $.spawn("div").append(template); + const text_area = template.find(".property-metadata-raw textarea"); - template.find(".toggle-metadata").on('click', event => { - if(text_area.is(":visible")) { - template.find(".toggle-metadata").text("show"); - } else { - template.find(".toggle-metadata").text("hide"); - } - text_area.slideToggle({duration: 250}); - }); - text_area.hide(); - - return template; - }, - footer: undefined, - width: 750 - }); - - modal.open(); - } - - export function spawnSongAdd(playlist: Playlist, callback_add: (url: string, loader: string) => any) { - let modal: Modal; - - modal = createModal({ - header: tr("Add a song"), - body: () => { - let template = $("#tmpl_playlist_edit-song_add").renderTag(); - template = $.spawn("div").append(template); - - const url = template.find(".property-url .value"); - const url_loader = template.find(".property-loader .value"); - const button_add = template.find(".container-buttons .button-add"); - const button_cancel = template.find(".container-buttons .button-cancel"); - - url.on('change keyup', event => { - button_add.prop("disabled", url.val().toString().length == 0); - }).trigger('change'); - - button_cancel.on('click', event => modal.close()); - button_add.on('click', event => { - callback_add(url.val() as string, url_loader.val() as string); - modal.close(); - }); - return template; - }, - footer: undefined, - width: 750 - }); - - modal.open(); - } - - export function spawnPlaylistEdit(client: ConnectionHandler, playlist: Playlist) { - { - createErrorModal(tr("Not implemented"), tr("Playlist editing hasn't yet been implemented")).open(); - return; - } - - let modal: Modal; - let changed_properties = {}; - let changed_permissions = {}; - let callback_permission_update: () => any; - - const update_save = () => { - const save_button = modal.htmlTag.find(".buttons .button-save"); - save_button.prop("disabled", (Object.keys(changed_properties).length + Object.keys(changed_permissions).length) == 0); - }; - - modal = createModal({ - header: tr("Edit playlist"), - body: () => { - let template = $("#tmpl_playlist_edit").renderTag().tabify(); - - callback_permission_update = apply_permissions(template, client, playlist, (key, value) => { - console.log("Change permission %o => %o", key, value); - changed_permissions[key] = value; - update_save(); - }); - const callback_song_id = apply_songs(template, client, playlist); - apply_properties(template, client, playlist, (key, value) => { - console.log("Change property %o => %o", key, value); - changed_properties[key] = value; - update_save(); - }, callback_song_id); - - template.find(".buttons .button-save").on('click', event => { - if(Object.keys(changed_properties).length != 0) { - changed_properties["playlist_id"] = playlist.playlist_id; - client.serverConnection.send_command("playlistedit", changed_properties).then(() => { - changed_properties = {}; - update_save(); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to change properties."), tr("Failed to change playlist properties.
Error: ") + error).open(); - }); - } - if(Object.keys(changed_permissions).length != 0) { - const array: any[] = []; - - for(const permission_key of Object.keys(changed_permissions)) { - array.push({ - permvalue: changed_permissions[permission_key], - permnegated: false, - permskip: false, - permsid: permission_key - }); - } - - array[0]["playlist_id"] = playlist.playlist_id; - client.serverConnection.send_command("playlistaddperm", array).then(() => { - changed_permissions = {}; - update_save(); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to change permission."), tr("Failed to change playlist permissions.
Error: ") + error).open(); - }); - } - }); - - template.find(".buttons .button-close").on('click', event => { - if((Object.keys(changed_properties).length + Object.keys(changed_permissions).length) != 0) { - spawnYesNo(tr("Are you sure?"), tr("Do you really want to discard all your changes?"), result => { - if(result) - modal.close(); - }); - return; - } - modal.close(); - }); - return template; - }, - footer: undefined, - width: 750 - }); - update_save(); - - modal.open(); - return modal; - } - - function apply_songs(tag: JQuery, client: ConnectionHandler, playlist: Playlist) { - const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; - const song_tag = tag.find(".container-songs"); - - let replaying_song_id: number = 0; - let selected_song: PlaylistSong; - - const set_song_info = (text: string) => { - const tag = song_tag.find(".info-message"); - if(text && text.length > 0) { - tag.text(text).show(); - } else - tag.hide(); - }; - - const set_current_song = (id: number) => { - /* this method shall enforce an update */ - replaying_song_id = id; - update_songs(); - }; - - const update_songs = () => { - set_song_info(tr("loading song list")); - client.serverConnection.command_helper.request_playlist_songs(playlist.playlist_id).then(result => { - const entries_tag = song_tag.find(".song-list-entries"); - const entry_template = $("#tmpl_playlist_edit-song_entry"); - entries_tag.empty(); - - for(const song of result) { - const rendered = entry_template.renderTag(song); - - rendered.find(".button-info").on('click', event => { - spawnPlaylistSongInfo(song); - }); - - const button_delete = rendered.find(".button-delete"); - if(!owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_REMOVE_POWER).granted(playlist.needed_power_song_remove)) - button_delete.detach(); - else - button_delete.on('click', event => { - client.serverConnection.send_command("playlistsongremove", { - playlist_id: playlist.playlist_id, - song_id: song.song_id - }).then(() => { - rendered.slideToggle({duration: 250, done(animation: JQuery.Promise, jumpedToEnd: boolean): void { - rendered.detach(); - }}); - rendered.hide(250); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to remove song."), tr("Failed to remove song/url from the playlist.
Error: ") + error).open(); - }); - }); - - if(song.song_id == replaying_song_id) - rendered.addClass("playing"); - - rendered.on('click', event => { - selected_song = song; - entries_tag.find(".selected").removeClass("selected"); - rendered.addClass("selected"); - }); - - entries_tag.append(rendered); + template.find(".toggle-metadata").on('click', event => { + if(text_area.is(":visible")) { + template.find(".toggle-metadata").text("show"); + } else { + template.find(".toggle-metadata").text("hide"); } - - const entry_container = song_tag.find(".song-list-entries-container"); - if(entry_container.hasScrollBar()) - entry_container.addClass("scrollbar"); - - set_song_info("displaying " + result.length + " songs"); - }).catch(error => { - console.error(error); - set_song_info(tr("failed to load song list")); - //TODO improve error handling! + text_area.slideToggle({duration: 250}); }); - }; + text_area.hide(); - song_tag.find(".button-refresh").on('click', event => update_songs()); - song_tag.find(".button-song-add").on('click', event => { - spawnSongAdd(playlist, (url, loader) => { - //playlist_id invoker previous url - client.serverConnection.send_command("playlistsongadd", { - playlist_id: playlist.playlist_id, - invoker: loader, - url: url - }).then(() => { - update_songs(); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to add song."), tr("Failed to add song/url to the playlist.
Error: ") + error).open(); - }); + return template; + }, + footer: undefined, + width: 750 + }); + + modal.open(); +} + +export function spawnSongAdd(playlist: Playlist, callback_add: (url: string, loader: string) => any) { + let modal: Modal; + + modal = createModal({ + header: tr("Add a song"), + body: () => { + let template = $("#tmpl_playlist_edit-song_add").renderTag(); + template = $.spawn("div").append(template); + + const url = template.find(".property-url .value"); + const url_loader = template.find(".property-loader .value"); + const button_add = template.find(".container-buttons .button-add"); + const button_cancel = template.find(".container-buttons .button-cancel"); + + url.on('change keyup', event => { + button_add.prop("disabled", url.val().toString().length == 0); + }).trigger('change'); + + button_cancel.on('click', event => modal.close()); + button_add.on('click', event => { + callback_add(url.val() as string, url_loader.val() as string); + modal.close(); }); - }).prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_ADD_POWER).granted(playlist.needed_power_song_add)); - /* setTimeout(update_songs, 100); */ /* We dont have to call that here because it will get called over set_current_song when we received the current song id */ + return template; + }, + footer: undefined, + width: 750 + }); - return set_current_song; + modal.open(); +} + +export function spawnPlaylistEdit(client: ConnectionHandler, playlist: Playlist) { + { + createErrorModal(tr("Not implemented"), tr("Playlist editing hasn't yet been implemented")).open(); + return; } - function apply_permissions(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_permission: (key: string, value: number) => any) { - const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; - const permission_tag = tag.find(".container-permissions"); - const nopermission_tag = tag.find(".container-no-permissions"); + let modal: Modal; + let changed_properties = {}; + let changed_permissions = {}; + let callback_permission_update: () => any; - const update_permissions = () => { - if(!client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST).granted(1)) { - nopermission_tag.show(); - permission_tag.hide(); - } else { - nopermission_tag.hide(); - permission_tag.show(); + const update_save = () => { + const save_button = modal.htmlTag.find(".buttons .button-save"); + save_button.prop("disabled", (Object.keys(changed_properties).length + Object.keys(changed_permissions).length) == 0); + }; - permission_tag.find(".permission input").prop("disabled", true); - client.permissions.requestPlaylistPermissions(playlist.playlist_id).then(permissions => { - permission_tag.find(".permission input") - .val(0) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_PERMISSION_MODIFY_POWER).granted(playlist.needed_power_permission_modify)); + modal = createModal({ + header: tr("Edit playlist"), + body: () => { + let template = $("#tmpl_playlist_edit").renderTag().tabify(); - for(const permission of permissions) { - const tag = permission_tag.find(".permission[permission='" + permission.type.name + "']"); - if(permission.value != -2) - tag.find("input").val(permission.value); + callback_permission_update = apply_permissions(template, client, playlist, (key, value) => { + console.log("Change permission %o => %o", key, value); + changed_permissions[key] = value; + update_save(); + }); + const callback_song_id = apply_songs(template, client, playlist); + apply_properties(template, client, playlist, (key, value) => { + console.log("Change property %o => %o", key, value); + changed_properties[key] = value; + update_save(); + }, callback_song_id); + + template.find(".buttons .button-save").on('click', event => { + if(Object.keys(changed_properties).length != 0) { + changed_properties["playlist_id"] = playlist.playlist_id; + client.serverConnection.send_command("playlistedit", changed_properties).then(() => { + changed_properties = {}; + update_save(); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to change properties."), tr("Failed to change playlist properties.
Error: ") + error).open(); + }); + } + if(Object.keys(changed_permissions).length != 0) { + const array: any[] = []; + + for(const permission_key of Object.keys(changed_permissions)) { + array.push({ + permvalue: changed_permissions[permission_key], + permnegated: false, + permskip: false, + permsid: permission_key + }); } - }); - } - }; - permission_tag.find(".permission").each((index, _element) => { - const element = $(_element); - element.find("input").on('change', event => { - console.log(element.find("input").val()); - change_permission(element.attr("permission"), parseInt(element.find("input").val().toString())); + array[0]["playlist_id"] = playlist.playlist_id; + client.serverConnection.send_command("playlistaddperm", array).then(() => { + changed_permissions = {}; + update_save(); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to change permission."), tr("Failed to change playlist permissions.
Error: ") + error).open(); + }); + } + }); + + template.find(".buttons .button-close").on('click', event => { + if((Object.keys(changed_properties).length + Object.keys(changed_permissions).length) != 0) { + spawnYesNo(tr("Are you sure?"), tr("Do you really want to discard all your changes?"), result => { + if(result) + modal.close(); + }); + return; + } + modal.close(); + }); + return template; + }, + footer: undefined, + width: 750 + }); + update_save(); + + modal.open(); + return modal; +} + +function apply_songs(tag: JQuery, client: ConnectionHandler, playlist: Playlist) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + const song_tag = tag.find(".container-songs"); + + let replaying_song_id: number = 0; + let selected_song: PlaylistSong; + + const set_song_info = (text: string) => { + const tag = song_tag.find(".info-message"); + if(text && text.length > 0) { + tag.text(text).show(); + } else + tag.hide(); + }; + + const set_current_song = (id: number) => { + /* this method shall enforce an update */ + replaying_song_id = id; + update_songs(); + }; + + const update_songs = () => { + set_song_info(tr("loading song list")); + client.serverConnection.command_helper.request_playlist_songs(playlist.playlist_id).then(result => { + const entries_tag = song_tag.find(".song-list-entries"); + const entry_template = $("#tmpl_playlist_edit-song_entry"); + entries_tag.empty(); + + for(const song of result) { + const rendered = entry_template.renderTag(song); + + rendered.find(".button-info").on('click', event => { + spawnPlaylistSongInfo(song); + }); + + const button_delete = rendered.find(".button-delete"); + if(!owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_REMOVE_POWER).granted(playlist.needed_power_song_remove)) + button_delete.detach(); + else + button_delete.on('click', event => { + client.serverConnection.send_command("playlistsongremove", { + playlist_id: playlist.playlist_id, + song_id: song.song_id + }).then(() => { + rendered.slideToggle({duration: 250, done(animation: JQuery.Promise, jumpedToEnd: boolean): void { + rendered.detach(); + }}); + rendered.hide(250); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to remove song."), tr("Failed to remove song/url from the playlist.
Error: ") + error).open(); + }); + }); + + if(song.song_id == replaying_song_id) + rendered.addClass("playing"); + + rendered.on('click', event => { + selected_song = song; + entries_tag.find(".selected").removeClass("selected"); + rendered.addClass("selected"); + }); + + entries_tag.append(rendered); + } + + const entry_container = song_tag.find(".song-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + + set_song_info("displaying " + result.length + " songs"); + }).catch(error => { + console.error(error); + set_song_info(tr("failed to load song list")); + //TODO improve error handling! + }); + }; + + song_tag.find(".button-refresh").on('click', event => update_songs()); + song_tag.find(".button-song-add").on('click', event => { + spawnSongAdd(playlist, (url, loader) => { + //playlist_id invoker previous url + client.serverConnection.send_command("playlistsongadd", { + playlist_id: playlist.playlist_id, + invoker: loader, + url: url + }).then(() => { + update_songs(); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to add song."), tr("Failed to add song/url to the playlist.
Error: ") + error).open(); }); }); + }).prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_SONG_ADD_POWER).granted(playlist.needed_power_song_add)); + /* setTimeout(update_songs, 100); */ /* We dont have to call that here because it will get called over set_current_song when we received the current song id */ - update_permissions(); - return update_permissions; - } + return set_current_song; +} - function apply_properties(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) { - const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; +function apply_permissions(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_permission: (key: string, value: number) => any) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + const permission_tag = tag.find(".container-permissions"); + const nopermission_tag = tag.find(".container-no-permissions"); - client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => { - tag.find(".property-owner input") - .val(info.playlist_owner_name + " (" + info.playlist_owner_dbid + ")"); + const update_permissions = () => { + if(!client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_PLAYLIST_PERMISSION_LIST).granted(1)) { + nopermission_tag.show(); + permission_tag.hide(); + } else { + nopermission_tag.hide(); + permission_tag.show(); - tag.find(".property-title input") - .val(info.playlist_title) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_title", (event.target).value); - }); + permission_tag.find(".permission input").prop("disabled", true); + client.permissions.requestPlaylistPermissions(playlist.playlist_id).then(permissions => { + permission_tag.find(".permission input") + .val(0) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_PERMISSION_MODIFY_POWER).granted(playlist.needed_power_permission_modify)); - tag.find(".property-description textarea") - .val(info.playlist_description) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_description", (event.target).value); - }); + for(const permission of permissions) { + const tag = permission_tag.find(".permission[permission='" + permission.type.name + "']"); + if(permission.value != -2) + tag.find("input").val(permission.value); + } + }); + } + }; - tag.find(".property-type select") - .val(info.playlist_type.toString()) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_description", (event.target).selectedIndex.toString()); - }); - - tag.find(".property-replay-mode select") - .val(info.playlist_replay_mode.toString()) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_replay_mode", (event.target).selectedIndex.toString()); - }); - - tag.find(".property-flag-delete-played input") - .prop("checked", info.playlist_flag_delete_played) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_flag_delete_played", (event.target).checked ? "1" : "0"); - }); - - tag.find(".property-current-song input") - .val(info.playlist_current_song_id); - callback_current_song(info.playlist_current_song_id); - - tag.find(".property-flag-finished input") - .prop("checked", info.playlist_flag_finished) - .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) - .on('change', event => { - change_property("playlist_flag_finished", (event.target).checked ? "1" : "0"); - }); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to query playlist info"), tr("Failed to query playlist info.
Error:") + error).open(); + permission_tag.find(".permission").each((index, _element) => { + const element = $(_element); + element.find("input").on('change', event => { + console.log(element.find("input").val()); + change_permission(element.attr("permission"), parseInt(element.find("input").val().toString())); }); - } + }); + + update_permissions(); + return update_permissions; +} + +function apply_properties(tag: JQuery, client: ConnectionHandler, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) { + const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; + + client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => { + tag.find(".property-owner input") + .val(info.playlist_owner_name + " (" + info.playlist_owner_dbid + ")"); + + tag.find(".property-title input") + .val(info.playlist_title) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_title", (event.target).value); + }); + + tag.find(".property-description textarea") + .val(info.playlist_description) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_description", (event.target).value); + }); + + tag.find(".property-type select") + .val(info.playlist_type.toString()) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_description", (event.target).selectedIndex.toString()); + }); + + tag.find(".property-replay-mode select") + .val(info.playlist_replay_mode.toString()) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_replay_mode", (event.target).selectedIndex.toString()); + }); + + tag.find(".property-flag-delete-played input") + .prop("checked", info.playlist_flag_delete_played) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_flag_delete_played", (event.target).checked ? "1" : "0"); + }); + + tag.find(".property-current-song input") + .val(info.playlist_current_song_id); + callback_current_song(info.playlist_current_song_id); + + tag.find(".property-flag-finished input") + .prop("checked", info.playlist_flag_finished) + .prop("disabled", !owns_playlist && !client.permissions.neededPermission(PermissionType.I_PLAYLIST_MODIFY_POWER).granted(playlist.needed_power_modify)) + .on('change', event => { + change_property("playlist_flag_finished", (event.target).checked ? "1" : "0"); + }); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to query playlist info"), tr("Failed to query playlist info.
Error:") + error).open(); + }); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalPlaylistList.ts b/shared/js/ui/modal/ModalPlaylistList.ts index 58c7451c..f7d3c243 100644 --- a/shared/js/ui/modal/ModalPlaylistList.ts +++ b/shared/js/ui/modal/ModalPlaylistList.ts @@ -1,194 +1,196 @@ -/// -/// -/// -/// +import {settings} from "tc-shared/settings"; +import {createErrorModal, createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {CommandResult, Playlist} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {SingleCommandHandler} from "tc-shared/connection/ConnectionBase"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {spawnPlaylistEdit} from "tc-shared/ui/modal/ModalPlaylistEdit"; -namespace Modals { - export function spawnPlaylistManage(client: ConnectionHandler) { - { - createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open(); +export function spawnPlaylistManage(client: ConnectionHandler) { + { + createErrorModal(tr("Not implemented"), tr("Playlist management hasn't yet been implemented")).open(); + return; + } + + + let modal: Modal; + let selected_playlist: Playlist; + let available_playlists: Playlist[]; + let highlight_own = settings.global("playlist-list-highlight-own", true); + + const update_selected = () => { + const buttons = modal.htmlTag.find(".header .buttons"); + + buttons.find(".button-playlist-edit").prop( + "disabled", + !selected_playlist + ); + buttons.find(".button-playlist-delete").prop( + "disabled", + !selected_playlist || !( /* not owner or permission */ + client.permissions.neededPermission(PermissionType.I_PLAYLIST_DELETE_POWER).granted(selected_playlist.needed_power_delete) || /* client has permissions */ + client.getClient().properties.client_database_id == selected_playlist.playlist_owner_dbid /* client is playlist owner */ + ) + ); + buttons.find(".button-playlist-create").prop( + "disabled", + !client.permissions.neededPermission(PermissionType.B_PLAYLIST_CREATE).granted(1) + ); + if(selected_playlist) { + buttons.find(".button-playlist-edit").prop( + "disabled", + false + ); + } + }; + + const update_list = async () => { + const info_tag = modal.htmlTag.find(".footer .info a"); + info_tag.text("loading..."); + + selected_playlist = undefined; + update_selected(); + + try { + available_playlists = await client.serverConnection.command_helper.request_playlist_list(); + } catch(error) { + info_tag.text("failed to query playlist list."); + //FIXME error handling? return; } + const entries_tag = modal.htmlTag.find(".playlist-list-entries"); + const entry_template = $("#tmpl_playlist_list-list_entry"); + entries_tag.empty(); - let modal: Modal; - let selected_playlist: Playlist; - let available_playlists: Playlist[]; - let highlight_own = settings.global("playlist-list-highlight-own", true); + const owndbid = client.getClient().properties.client_database_id; + for(const query of available_playlists) { + const tag = entry_template.renderTag(query).on('click', event => { + entries_tag.find(".entry.selected").removeClass("selected"); + $(event.target).parent(".entry").addClass("selected"); + selected_playlist = query; + update_selected(); + }); - const update_selected = () => { - const buttons = modal.htmlTag.find(".header .buttons"); + if(highlight_own && query.playlist_owner_dbid == owndbid) + tag.addClass("highlighted"); - buttons.find(".button-playlist-edit").prop( - "disabled", - !selected_playlist - ); - buttons.find(".button-playlist-delete").prop( - "disabled", - !selected_playlist || !( /* not owner or permission */ - client.permissions.neededPermission(PermissionType.I_PLAYLIST_DELETE_POWER).granted(selected_playlist.needed_power_delete) || /* client has permissions */ - client.getClient().properties.client_database_id == selected_playlist.playlist_owner_dbid /* client is playlist owner */ - ) - ); - buttons.find(".button-playlist-create").prop( - "disabled", - !client.permissions.neededPermission(PermissionType.B_PLAYLIST_CREATE).granted(1) - ); - if(selected_playlist) { - buttons.find(".button-playlist-edit").prop( - "disabled", - false - ); - } - }; + entries_tag.append(tag); + } - const update_list = async () => { - const info_tag = modal.htmlTag.find(".footer .info a"); - info_tag.text("loading..."); + const entry_container = modal.htmlTag.find(".playlist-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); - selected_playlist = undefined; - update_selected(); + info_tag.text("Showing " + available_playlists.length + " entries"); + update_selected(); + }; - try { - available_playlists = await client.serverConnection.command_helper.request_playlist_list(); - } catch(error) { - info_tag.text("failed to query playlist list."); - //FIXME error handling? - return; - } + modal = createModal({ + header: tr("Manage playlists"), + body: () => { + let template = $("#tmpl_playlist_list").renderTag(); - const entries_tag = modal.htmlTag.find(".playlist-list-entries"); - const entry_template = $("#tmpl_playlist_list-list_entry"); - entries_tag.empty(); + /* first open the modal */ + setTimeout(() => { + const entry_container = template.find(".playlist-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + }, 100); - const owndbid = client.getClient().properties.client_database_id; - for(const query of available_playlists) { - const tag = entry_template.renderTag(query).on('click', event => { - entries_tag.find(".entry.selected").removeClass("selected"); - $(event.target).parent(".entry").addClass("selected"); - selected_playlist = query; - update_selected(); - }); + template.find(".footer .buttons .button-refresh").on('click', update_list); - if(highlight_own && query.playlist_owner_dbid == owndbid) - tag.addClass("highlighted"); - - entries_tag.append(tag); - } - - const entry_container = modal.htmlTag.find(".playlist-list-entries-container"); - if(entry_container.hasScrollBar()) - entry_container.addClass("scrollbar"); - - info_tag.text("Showing " + available_playlists.length + " entries"); - update_selected(); - }; - - modal = createModal({ - header: tr("Manage playlists"), - body: () => { - let template = $("#tmpl_playlist_list").renderTag(); - - /* first open the modal */ - setTimeout(() => { - const entry_container = template.find(".playlist-list-entries-container"); - if(entry_container.hasScrollBar()) - entry_container.addClass("scrollbar"); - }, 100); - - template.find(".footer .buttons .button-refresh").on('click', update_list); - - template.find(".button-playlist-create").on('click', event => { - const single_handler: connection.SingleCommandHandler = { - function: command => { - const json = command.arguments; - update_list().then(() => { - spawnYesNo(tr("Playlist created successful"), tr("The playlist has been successfully created.
Should we open the editor?"), result => { - if(result) { - for(const playlist of available_playlists) { - if(playlist.playlist_id == json[0]["playlist_id"]) { - spawnPlaylistEdit(client, playlist).close_listener.push(update_list); - return; - } + template.find(".button-playlist-create").on('click', event => { + const single_handler: SingleCommandHandler = { + function: command => { + const json = command.arguments; + update_list().then(() => { + spawnYesNo(tr("Playlist created successful"), tr("The playlist has been successfully created.
Should we open the editor?"), result => { + if(result) { + for(const playlist of available_playlists) { + if(playlist.playlist_id == json[0]["playlist_id"]) { + spawnPlaylistEdit(client, playlist).close_listener.push(update_list); + return; } } - }); - }); - - return true; - }, - command: "notifyplaylistcreated" - }; - client.serverConnection.command_handler_boss().register_single_handler(single_handler); - client.serverConnection.send_command("playlistcreate").catch(error => { - client.serverConnection.command_handler_boss().remove_single_handler(single_handler); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to create playlist"), tr("Failed to create playlist
Message: ") + error).open(); - }); - }); - - template.find(".button-playlist-edit").on('click', event => { - if(!selected_playlist) return; - spawnPlaylistEdit(client, selected_playlist).close_listener.push(update_list); - }); - - template.find(".button-playlist-delete").on('click', () => { - if(!selected_playlist) return; - - Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this playlist?"), result => { - if(result) { - client.serverConnection.send_command("playlistdelete", {playlist_id: selected_playlist.playlist_id}).then(() => { - createInfoModal(tr("Playlist deleted successful"), tr("This playlist has been deleted successfully.")).open(); - update_list(); - }).catch(error => { - if(error instanceof CommandResult) { - /* TODO extra handling here */ - //if(error.id == ErrorID.PLAYLIST_IS_IN_USE) { } - error = error.extra_message || error.message; } - createErrorModal(tr("Unable to delete playlist"), tr("Failed to delete playlist
Message: ") + error).open(); }); - } - }); - }); + }); - template.find(".input-search").on('change keyup', () => { - const text = (template.find(".input-search").val() as string || "").toLowerCase(); - if(text.length == 0) { - template.find(".playlist-list-entries .entry").show(); - } else { - template.find(".playlist-list-entries .entry").each((_, e) => { - const element = $(e); - if(element.text().toLowerCase().indexOf(text) == -1) - element.hide(); - else - element.show(); - }) + return true; + }, + command: "notifyplaylistcreated" + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + client.serverConnection.send_command("playlistcreate").catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to create playlist"), tr("Failed to create playlist
Message: ") + error).open(); + }); + }); + + template.find(".button-playlist-edit").on('click', event => { + if(!selected_playlist) return; + spawnPlaylistEdit(client, selected_playlist).close_listener.push(update_list); + }); + + template.find(".button-playlist-delete").on('click', () => { + if(!selected_playlist) return; + + spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this playlist?"), result => { + if(result) { + client.serverConnection.send_command("playlistdelete", {playlist_id: selected_playlist.playlist_id}).then(() => { + createInfoModal(tr("Playlist deleted successful"), tr("This playlist has been deleted successfully.")).open(); + update_list(); + }).catch(error => { + if(error instanceof CommandResult) { + /* TODO extra handling here */ + //if(error.id == ErrorID.PLAYLIST_IS_IN_USE) { } + error = error.extra_message || error.message; + } + createErrorModal(tr("Unable to delete playlist"), tr("Failed to delete playlist
Message: ") + error).open(); + }); } }); + }); - template.find(".button-highlight-own").on('change', event => { - const flag = (event.target).checked; - settings.changeGlobal("playlist-list-highlight-own", flag); - if(flag) { - const owndbid = client.getClient().properties.client_database_id; - template.find(".playlist-list-entries .entry").each((index, _element) => { - const element = $(_element); - if(parseInt(element.attr("playlist-owner-dbid")) == owndbid) - element.addClass("highlighted"); - }) - } else { - template.find(".playlist-list-entries .highlighted").removeClass("highlighted"); - } - }).prop("checked", highlight_own); - return template; - }, - footer: undefined, - width: 750 - }); + template.find(".input-search").on('change keyup', () => { + const text = (template.find(".input-search").val() as string || "").toLowerCase(); + if(text.length == 0) { + template.find(".playlist-list-entries .entry").show(); + } else { + template.find(".playlist-list-entries .entry").each((_, e) => { + const element = $(e); + if(element.text().toLowerCase().indexOf(text) == -1) + element.hide(); + else + element.show(); + }) + } + }); - update_list(); - modal.open(); - } + template.find(".button-highlight-own").on('change', event => { + const flag = (event.target).checked; + settings.changeGlobal("playlist-list-highlight-own", flag); + if(flag) { + const owndbid = client.getClient().properties.client_database_id; + template.find(".playlist-list-entries .entry").each((index, _element) => { + const element = $(_element); + if(parseInt(element.attr("playlist-owner-dbid")) == owndbid) + element.addClass("highlighted"); + }) + } else { + template.find(".playlist-list-entries .highlighted").removeClass("highlighted"); + } + }).prop("checked", highlight_own); + return template; + }, + footer: undefined, + width: 750 + }); + + update_list(); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalPoke.ts b/shared/js/ui/modal/ModalPoke.ts index d4769a2c..02ea59df 100644 --- a/shared/js/ui/modal/ModalPoke.ts +++ b/shared/js/ui/modal/ModalPoke.ts @@ -1,94 +1,94 @@ -/// -/// -/// +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import * as htmltags from "tc-shared/ui/htmltags"; +import {bbcode_chat} from "tc-shared/ui/frames/chat"; -namespace Modals { - let global_modal: PokeModal; +let global_modal: PokeModal; - interface ServerEntry { - source: ConnectionHandler; - add_message(invoker: PokeInvoker, message: string); +interface ServerEntry { + source: ConnectionHandler; + add_message(invoker: PokeInvoker, message: string); +} + +declare const moment; +class PokeModal { + private _handle: Modal; + private source_map: ServerEntry[] = []; + + constructor() { + this._handle = createModal({ + header: tr("You have been poked!"), + body: () => { + let template = $("#tmpl_poke_popup").renderTag(); + template.find(".button-close").on('click', event => this._handle_close()); + return template; + }, + footer: undefined, + width: 750 + }); + this._handle.close_listener.push(() => this._handle_close()); } - class PokeModal { - private _handle: Modal; - private source_map: ServerEntry[] = []; - - constructor() { - this._handle = createModal({ - header: tr("You have been poked!"), - body: () => { - let template = $("#tmpl_poke_popup").renderTag(); - template.find(".button-close").on('click', event => this._handle_close()); - return template; - }, - footer: undefined, - width: 750 - }); - this._handle.close_listener.push(() => this._handle_close()); - } - - modal() { return this._handle; } - add_poke(source: ConnectionHandler, invoker: PokeInvoker, message: string) { - let handler: ServerEntry; - for(const entry of this.source_map) - if(entry.source === source) { - handler = entry; - break; - } - if(!handler) { - const html_tag = $.spawn("div").addClass("server"); - const poke_list = $.spawn("div").addClass("poke-list"); - $.spawn("div") - .addClass("server-name") - .text(source && source.channelTree && source.channelTree.server ? source.channelTree.server.properties.virtualserver_name : "unknown") - .appendTo(html_tag); - poke_list.appendTo(html_tag); - - this.source_map.push(handler = { - source: source, - add_message: (invoker: PokeInvoker, message: string) => { - const container = $.spawn("div").addClass("entry"); - - $.spawn("div").addClass("date").text(moment().format("HH:mm:ss") + " - ").appendTo(container); - $.spawn("div").addClass("user").append($(htmltags.generate_client({ - add_braces: true, - client_id: invoker.id, - client_name: invoker.name, - client_unique_id: invoker.unique_id - }))).appendTo(container); - if(message) { - $.spawn("div").addClass("text").text(tr("pokes you:")).appendTo(container); - $.spawn("div").addClass("poke-message").append(...MessageHelper.bbcode_chat(message)).appendTo(container); - } else { - $.spawn("div").addClass("text").text(tr("pokes you.")).appendTo(container); - } - - container.appendTo(poke_list); - } - }); - - this._handle.htmlTag.find(".container-servers").append(html_tag); + modal() { return this._handle; } + add_poke(source: ConnectionHandler, invoker: PokeInvoker, message: string) { + let handler: ServerEntry; + for(const entry of this.source_map) + if(entry.source === source) { + handler = entry; + break; } - handler.add_message(invoker, message); - } + if(!handler) { + const html_tag = $.spawn("div").addClass("server"); + const poke_list = $.spawn("div").addClass("poke-list"); + $.spawn("div") + .addClass("server-name") + .text(source && source.channelTree && source.channelTree.server ? source.channelTree.server.properties.virtualserver_name : "unknown") + .appendTo(html_tag); + poke_list.appendTo(html_tag); - private _handle_close() { - this._handle.close(); - global_modal = undefined; + this.source_map.push(handler = { + source: source, + add_message: (invoker: PokeInvoker, message: string) => { + const container = $.spawn("div").addClass("entry"); + + $.spawn("div").addClass("date").text(moment().format("HH:mm:ss") + " - ").appendTo(container); + $.spawn("div").addClass("user").append($(htmltags.generate_client({ + add_braces: true, + client_id: invoker.id, + client_name: invoker.name, + client_unique_id: invoker.unique_id + }))).appendTo(container); + if(message) { + $.spawn("div").addClass("text").text(tr("pokes you:")).appendTo(container); + $.spawn("div").addClass("poke-message").append(...bbcode_chat(message)).appendTo(container); + } else { + $.spawn("div").addClass("text").text(tr("pokes you.")).appendTo(container); + } + + container.appendTo(poke_list); + } + }); + + this._handle.htmlTag.find(".container-servers").append(html_tag); } + handler.add_message(invoker, message); } - export type PokeInvoker = { - name: string, - id: number, - unique_id: string - }; - - export function spawnPoke(source: ConnectionHandler, invoker: PokeInvoker, message: string) { - if(!global_modal) - global_modal = new PokeModal(); - global_modal.add_poke(source, invoker, message); - global_modal.modal().open(); + private _handle_close() { + this._handle.close(); + global_modal = undefined; } +} + +export type PokeInvoker = { + name: string, + id: number, + unique_id: string +}; + +export function spawnPoke(source: ConnectionHandler, invoker: PokeInvoker, message: string) { + if(!global_modal) + global_modal = new PokeModal(); + global_modal.add_poke(source, invoker, message); + global_modal.modal().open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts index af3e8560..7ef528a2 100644 --- a/shared/js/ui/modal/ModalQuery.ts +++ b/shared/js/ui/modal/ModalQuery.ts @@ -1,86 +1,85 @@ -/// -/// -/// +import {createErrorModal, createModal} from "tc-shared/ui/elements/Modal"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {SingleCommandHandler} from "tc-shared/connection/ConnectionBase"; -namespace Modals { - export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) { - let modal; - modal = createModal({ - header: tr("Create a server query login"), - body: () => { - let template = $("#tmpl_query_create").renderTag(); - template = $.spawn("div").append(template); +export function spawnQueryCreate(connection: ConnectionHandler, callback_created?: (user, pass) => any) { + let modal; + modal = createModal({ + header: tr("Create a server query login"), + body: () => { + let template = $("#tmpl_query_create").renderTag(); + template = $.spawn("div").append(template); - template.find(".button-close").on('click', event => modal.close()); - template.find(".button-create").on('click', event => { - const name = template.find(".input-name").val() as string; - if(name.length < 3 || name.length > 64) { - createErrorModal(tr("Invalid username"), tr("Please enter a valid name!")).open(); - return; - } + template.find(".button-close").on('click', event => modal.close()); + template.find(".button-create").on('click', event => { + const name = template.find(".input-name").val() as string; + if(name.length < 3 || name.length > 64) { + createErrorModal(tr("Invalid username"), tr("Please enter a valid name!")).open(); + return; + } - const single_handler: connection.SingleCommandHandler = { - function: command => { - const json = command.arguments[0]; + const single_handler: SingleCommandHandler = { + function: command => { + const json = command.arguments[0]; - spawnQueryCreated({ - username: name, - password: json.client_login_password - }, true); + spawnQueryCreated({ + username: name, + password: json.client_login_password + }, true); - if(callback_created) - callback_created(name, json.client_login_password); - return true; - }, - command: "notifyquerycreated" - }; - connection.serverConnection.command_handler_boss().register_single_handler(single_handler); - connection.serverConnection.send_command("querycreate", { - client_login_name: name - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to create account"), tr("Failed to create account
Message: ") + error).open(); - }).then(() => connection.serverConnection.command_handler_boss().remove_single_handler(single_handler)); + if(callback_created) + callback_created(name, json.client_login_password); + return true; + }, + command: "notifyquerycreated" + }; + connection.serverConnection.command_handler_boss().register_single_handler(single_handler); + connection.serverConnection.send_command("querycreate", { + client_login_name: name + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to create account"), tr("Failed to create account
Message: ") + error).open(); + }).then(() => connection.serverConnection.command_handler_boss().remove_single_handler(single_handler)); - modal.close(); - }); - return template; - }, - footer: undefined, - width: 750 - }); - modal.open(); - } + modal.close(); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); +} - export function spawnQueryCreated(credentials: { - username: string, - password: string - }, just_created: boolean) { - let modal; - modal = createModal({ - header: just_created ? tr("Server query credentials") : tr("New server query credentials"), - body: () => { - let template = $("#tmpl_query_created").renderTag(credentials); - template = $.spawn("div").append(template); +export function spawnQueryCreated(credentials: { + username: string, + password: string +}, just_created: boolean) { + let modal; + modal = createModal({ + header: just_created ? tr("Server query credentials") : tr("New server query credentials"), + body: () => { + let template = $("#tmpl_query_created").renderTag(credentials); + template = $.spawn("div").append(template); - template.find(".button-close").on('click', event => modal.close()); - template.find(".query_name").text(credentials.username); - template.find(".query_password").text(credentials.password); + template.find(".button-close").on('click', event => modal.close()); + template.find(".query_name").text(credentials.username); + template.find(".query_password").text(credentials.password); - template.find(".btn_copy_name").on('click', () => { - template.find(".query_name").select(); - document.execCommand("copy"); - }); - template.find(".btn_copy_password").on('click', () => { - template.find(".query_password").select(); - document.execCommand("copy"); - }); - return template; - }, - footer: undefined, - width: 750 - }); - modal.open(); - } + template.find(".btn_copy_name").on('click', () => { + template.find(".query_name").select(); + document.execCommand("copy"); + }); + template.find(".btn_copy_password").on('click', () => { + template.find(".query_password").select(); + document.execCommand("copy"); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalQueryManage.ts b/shared/js/ui/modal/ModalQueryManage.ts index c856ab83..ce78b233 100644 --- a/shared/js/ui/modal/ModalQueryManage.ts +++ b/shared/js/ui/modal/ModalQueryManage.ts @@ -1,102 +1,385 @@ -/// -/// -/// +/* +export function spawnQueryManage(client: ConnectionHandler) { + let modal: Modal; + let selected_query: QueryListEntry; -namespace Modals { - /* - export function spawnQueryManage(client: ConnectionHandler) { - let modal: Modal; - let selected_query: QueryListEntry; + const update_selected = () => { + const buttons = modal.htmlTag.find(".header .buttons"); - const update_selected = () => { - const buttons = modal.htmlTag.find(".header .buttons"); + //TODO gray out if no permissions (Server needs to send that... :D) + buttons.find(".button-query-delete").prop("disabled", selected_query === undefined); + buttons.find(".button-query-rename").prop("disabled", selected_query === undefined); + buttons.find(".button-query-change-password").prop("disabled", selected_query === undefined); + }; - //TODO gray out if no permissions (Server needs to send that... :D) - buttons.find(".button-query-delete").prop("disabled", selected_query === undefined); - buttons.find(".button-query-rename").prop("disabled", selected_query === undefined); - buttons.find(".button-query-change-password").prop("disabled", selected_query === undefined); - }; + const update_list = () => { + const info_tag = modal.htmlTag.find(".footer .info a"); + info_tag.text("loading..."); + client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { + client.serverConnection.command_helper.request_query_list(server_id).then(result => { + selected_query = undefined; - const update_list = () => { - const info_tag = modal.htmlTag.find(".footer .info a"); - info_tag.text("loading..."); - client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { - client.serverConnection.command_helper.request_query_list(server_id).then(result => { - selected_query = undefined; + const entries_tag = modal.htmlTag.find(".query-list-entries"); + const entry_template = $("#tmpl_query_manager-list_entry"); + entries_tag.empty(); - const entries_tag = modal.htmlTag.find(".query-list-entries"); - const entry_template = $("#tmpl_query_manager-list_entry"); - entries_tag.empty(); + for(const query of result.queries || []) { + entries_tag.append(entry_template.renderTag(query).on('click', event => { + entries_tag.find(".entry.selected").removeClass("selected"); + $(event.target).parent(".entry").addClass("selected"); + selected_query = query; + update_selected(); + })); + } - for(const query of result.queries || []) { - entries_tag.append(entry_template.renderTag(query).on('click', event => { - entries_tag.find(".entry.selected").removeClass("selected"); - $(event.target).parent(".entry").addClass("selected"); - selected_query = query; - update_selected(); - })); + const entry_container = modal.htmlTag.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + + if(!result || result.flag_all) { + info_tag.text("Showing all server queries"); + } else { + info_tag.text("Showing your server queries") + } + update_selected(); + }); + }); + //TODO error handling + }; + + modal = createModal({ + header: tr("Manage query accounts"), + body: () => { + let template = $("#tmpl_query_manager").renderTag(); + template = $.spawn("div").append(template); + + /* first open the modal + setTimeout(() => { + const entry_container = template.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + }, 100); + + template.find(".footer .buttons .button-refresh").on('click', update_list); + template.find(".button-query-create").on('click', () => { + Modals.spawnQueryCreate(client, (user, pass) => update_list()); + }); + template.find(".button-query-rename").on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account name"), tr("Enter the new name for the login:
"), text => text.length >= 3, result => { + if(result) { + client.serverConnection.send_command("queryrename", { + client_login_name: selected_query.username, + client_new_login_name: result + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to rename account"), tr("Failed to rename account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); + update_list(); + }); } + }).open(); + }); + template.find(".button-query-change-password").on('click', () => { + if(!selected_query) return; - const entry_container = modal.htmlTag.find(".query-list-entries-container"); - if(entry_container.hasScrollBar()) - entry_container.addClass("scrollbar"); + createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):
"), text => true, result => { + if(result !== false) { + const single_handler: connection.SingleCommandHandler = { + command: "notifyquerypasswordchanges", + function: command => { + Modals.spawnQueryCreated({ + username: command.arguments[0]["client_login_name"], + password: command.arguments[0]["client_login_password"] + }, false); - if(!result || result.flag_all) { - info_tag.text("Showing all server queries"); - } else { - info_tag.text("Showing your server queries") + return true; + } + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + + client.serverConnection.send_command("querychangepassword", { + client_login_name: selected_query.username, + client_login_password: result + }).catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to change password"), tr("Failed to change password
Message: ") + error).open(); + }); + } + }).open(); + }); + template.find(".button-query-delete").on('click', () => { + if(!selected_query) return; + + Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { + if(result) { + client.serverConnection.send_command("querydelete", { + client_login_name: selected_query.username + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to delete account"), tr("Failed to delete account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); + update_list(); + }); } - update_selected(); }); }); - //TODO error handling - }; + template.find(".input-search").on('change keyup', () => { + const text = (template.find(".input-search").val() as string || "").toLowerCase(); + if(text.length == 0) { + template.find(".query-list-entries .entry").show(); + } else { + template.find(".query-list-entries .entry").each((_, e) => { + const element = $(e); + if(element.text().toLowerCase().indexOf(text) == -1) + element.hide(); + else + element.show(); + }) + } + }); + return template; + }, + footer: undefined, + width: 750 + }); - modal = createModal({ - header: tr("Manage query accounts"), - body: () => { - let template = $("#tmpl_query_manager").renderTag(); - template = $.spawn("div").append(template); + update_list(); + modal.open(); +} + */ - /* first open the modal - setTimeout(() => { - const entry_container = template.find(".query-list-entries-container"); - if(entry_container.hasScrollBar()) - entry_container.addClass("scrollbar"); - }, 100); +//tmpl_query_manager +import {createErrorModal, createInfoModal, createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {CommandResult, ErrorID, QueryListEntry} from "tc-shared/connection/ServerConnectionDeclaration"; +import {SingleCommandHandler} from "tc-shared/connection/ConnectionBase"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {LogCategory} from "tc-shared/log"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import * as log from "tc-shared/log"; +import {spawnQueryCreate, spawnQueryCreated} from "tc-shared/ui/modal/ModalQuery"; +import {formatMessage} from "tc-shared/ui/frames/chat"; - template.find(".footer .buttons .button-refresh").on('click', update_list); - template.find(".button-query-create").on('click', () => { - Modals.spawnQueryCreate(client, (user, pass) => update_list()); +export function spawnQueryManage(client: ConnectionHandler) { + let modal: Modal; + + modal = createModal({ + header: tr("Manage query accounts"), + body: () => { + let template = $("#tmpl_query_manager").renderTag(); + + let current_server: number; + let selected_query: QueryListEntry; + let filter_callbacks: ((text: string) => boolean)[] = []; + const container_list = template.find(".container-list .container-entries"); + const container_list_empty = container_list.find(".container-empty"); + const container_list_error = container_list.find(".container-error"); + + const detail_name = template.find(".detail.login-name .value"); + const detail_unique_id = template.find(".detail.unique-id .value"); + const detail_bound_server = template.find(".detail.bound-server .value"); + + const detail_unique_id_copy = template.find(".detail.unique-id .button-copy"); + + const input_filter = template.find(".filter-input"); + + const button_create = template.find(".button-create"); + const button_delete = template.find(".button-delete"); + const button_rename = template.find(".button-rename"); + const button_change_password = template.find(".button-change-password"); + const button_update = template.find(".button-update"); + + const permission_create = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1); + const permission_delete = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE).granted(1); + const permission_delete_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE_OWN).granted(1); + const permission_rename = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME).granted(1); + const permission_rename_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME_OWN).granted(1); + const permission_password = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD).granted(1); + const permission_password_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_OWN_PASSWORD).granted(1); + const permission_password_global = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL).granted(1); + button_create.prop('disabled', !permission_create); + + const set_error = (error: string | undefined) => { + if(typeof(error) === "string") + container_list_error.text(error).show(); + else + container_list_error.hide(); + }; + + const update_list = (selected_entry: string | undefined) => { + button_update.prop('disabled', true); + container_list_empty.text(tr("loading...")).show(); + set_error(undefined); + set_selected(undefined, false); + filter_callbacks = []; + container_list.find(".entry").remove(); + + client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { + current_server = server_id; + + client.serverConnection.command_helper.request_query_list(server_id).then(result => { + if(!result || !result.queries.length) { + container_list_empty.text(tr("No queries available")); + return; + } + + for(const entry of result.queries) { + const tag = $.spawn("div").addClass("entry").text(entry.username + " (" + entry.unique_id + ")"); + tag.on('click', event => { + container_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + set_selected(entry, false); + }); + container_list.append(tag); + if(entry.username === selected_entry) tag.trigger('click'); + + const text_mesh = (entry.username + " " + entry.unique_id + " " + entry.bounded_server).toLowerCase(); + filter_callbacks.push(text => { + if(typeof(text) === "undefined" || text_mesh.indexOf(text) != -1) { + tag.show(); + return true; + } else { + tag.hide(); + return false; + } + }); + } + + update_filter(); + container_list_empty.hide(); + button_update.prop('disabled', false); + }).catch(error => { + button_update.prop('disabled', false); + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { + set_error(tr("No permissions")); + return; + } + log.error(LogCategory.CLIENT, tr("Failed to request the query list: %o"), error); + set_error(tr("Failed to request list")); + }); + }).catch(error => { + button_update.prop('disabled', false); + log.error(LogCategory.CLIENT, tr("Failed to get own virtual server id: %o"), error); + set_error(tr("Failed to query server id")); }); - template.find(".button-query-rename").on('click', () => { + }; + + const set_selected = (entry: QueryListEntry | undefined, force: boolean) => { + if(entry === selected_query && !force) return; + selected_query = entry; + + if(!selected_query) { + detail_name.text("-"); + detail_unique_id.text("-"); + detail_bound_server.text("-"); + + button_delete.prop('disabled', true); + button_rename.prop('disabled', true); + button_change_password.prop('disabled', true); + } else { + detail_name.text(selected_query.username); + detail_unique_id.text(selected_query.unique_id); + if(selected_query.bounded_server == 0) + detail_bound_server.text(tr("On the instance")); + else if(selected_query.bounded_server === current_server) + detail_bound_server.text(tr("On the current server")); + else + detail_bound_server.text(selected_query.bounded_server.toString()); + + button_delete.prop('disabled', !permission_delete && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_delete_own)); + button_rename.prop('disabled', !permission_rename && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_rename_own)); + if(selected_query.bounded_server != 0) { + button_change_password.prop('disabled', !permission_password && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); + } else { + button_change_password.prop('disabled', !permission_password_global && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); + } + } + }; + + const update_filter = () => { + let value = input_filter.val() as string; + if(!value) value = undefined; + else value = value.toLowerCase(); + + const shown = filter_callbacks.filter(e => e(value)).length; + if(shown > 0) { + container_list_empty.hide(); + } else { + container_list_empty.text(tr("No accounts found")).show(); + } + }; + input_filter.on('change keyup', update_filter); + + /* all buttons */ + { + detail_unique_id_copy.on('click', event => { if(!selected_query) return; - createInputModal(tr("Change account name"), tr("Enter the new name for the login:
"), text => text.length >= 3, result => { + copy_to_clipboard(selected_query.unique_id); + createInfoModal(tr("Unique ID copied"), tr("The unique id has been successfully copied to your clipboard.")).open(); + }); + + button_create.on('click', event => { + spawnQueryCreate(client, (user, pass) => update_list(user)); + }); + + button_delete.on('click', event => { + if(!selected_query) return; + + spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { + if(result) { + client.serverConnection.send_command("querydelete", { + client_login_name: selected_query.username + }).then(() => { + createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); + update_list(undefined); + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to delete account"), formatMessage(tr("Failed to delete account{:br:}Message: {}"), error)).open(); + }); + } + }); + }); + + button_rename.on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account name"), tr("Enter the new name for the login:"), text => text.length >= 3, result => { if(result) { client.serverConnection.send_command("queryrename", { client_login_name: selected_query.username, client_new_login_name: result + }).then(() => { + createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); + update_list(result as string); }).catch(error => { if(error instanceof CommandResult) error = error.extra_message || error.message; - createErrorModal(tr("Unable to rename account"), tr("Failed to rename account
Message: ") + error).open(); - }).then(() => { - createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); - update_list(); + createErrorModal(tr("Unable to rename account"), formatMessage(tr("Failed to rename account{:br:}Message: {}"), error)).open(); }); } }).open(); }); - template.find(".button-query-change-password").on('click', () => { + + button_change_password.on('click', () => { if(!selected_query) return; - createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):
"), text => true, result => { + createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):"), text => true, result => { if(result !== false) { - const single_handler: connection.SingleCommandHandler = { + const single_handler: SingleCommandHandler = { command: "notifyquerypasswordchanges", function: command => { - Modals.spawnQueryCreated({ + spawnQueryCreated({ username: command.arguments[0]["client_login_name"], password: command.arguments[0]["client_login_password"] }, false); @@ -113,304 +396,27 @@ namespace Modals { client.serverConnection.command_handler_boss().remove_single_handler(single_handler); if(error instanceof CommandResult) error = error.extra_message || error.message; - createErrorModal(tr("Unable to change password"), tr("Failed to change password
Message: ") + error).open(); + createErrorModal(tr("Unable to change password"), formatMessage(tr("Failed to change password{:br:}Message: {}"), error)).open(); }); } }).open(); }); - template.find(".button-query-delete").on('click', () => { - if(!selected_query) return; - Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { - if(result) { - client.serverConnection.send_command("querydelete", { - client_login_name: selected_query.username - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to delete account"), tr("Failed to delete account
Message: ") + error).open(); - }).then(() => { - createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); - update_list(); - }); - } - }); - }); - template.find(".input-search").on('change keyup', () => { - const text = (template.find(".input-search").val() as string || "").toLowerCase(); - if(text.length == 0) { - template.find(".query-list-entries .entry").show(); - } else { - template.find(".query-list-entries .entry").each((_, e) => { - const element = $(e); - if(element.text().toLowerCase().indexOf(text) == -1) - element.hide(); - else - element.show(); - }) - } - }); - return template; - }, - footer: undefined, - width: 750 - }); + button_update.on('click', event => update_list(selected_query ? selected_query.username : undefined)); + } - update_list(); - modal.open(); - } - */ + modal.close_listener.push(() => filter_callbacks = undefined); - //tmpl_query_manager - export function spawnQueryManage(client: ConnectionHandler) { - let modal: Modal; + set_selected(undefined, true); + update_list(undefined); + template.dividerfy(); + return template; + }, + footer: null, - modal = createModal({ - header: tr("Manage query accounts"), - body: () => { - let template = $("#tmpl_query_manager").renderTag(); + min_width: "25em" + }); - let current_server: number; - let selected_query: QueryListEntry; - let filter_callbacks: ((text: string) => boolean)[] = []; - const container_list = template.find(".container-list .container-entries"); - const container_list_empty = container_list.find(".container-empty"); - const container_list_error = container_list.find(".container-error"); - - const detail_name = template.find(".detail.login-name .value"); - const detail_unique_id = template.find(".detail.unique-id .value"); - const detail_bound_server = template.find(".detail.bound-server .value"); - - const detail_unique_id_copy = template.find(".detail.unique-id .button-copy"); - - const input_filter = template.find(".filter-input"); - - const button_create = template.find(".button-create"); - const button_delete = template.find(".button-delete"); - const button_rename = template.find(".button-rename"); - const button_change_password = template.find(".button-change-password"); - const button_update = template.find(".button-update"); - - const permission_create = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CREATE).granted(1); - const permission_delete = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE).granted(1); - const permission_delete_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_DELETE_OWN).granted(1); - const permission_rename = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME).granted(1); - const permission_rename_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_RENAME_OWN).granted(1); - const permission_password = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD).granted(1); - const permission_password_own = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_OWN_PASSWORD).granted(1); - const permission_password_global = client.permissions.neededPermission(PermissionType.B_CLIENT_QUERY_CHANGE_PASSWORD_GLOBAL).granted(1); - button_create.prop('disabled', !permission_create); - - const set_error = (error: string | undefined) => { - if(typeof(error) === "string") - container_list_error.text(error).show(); - else - container_list_error.hide(); - }; - - const update_list = (selected_entry: string | undefined) => { - button_update.prop('disabled', true); - container_list_empty.text(tr("loading...")).show(); - set_error(undefined); - set_selected(undefined, false); - filter_callbacks = []; - container_list.find(".entry").remove(); - - client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { - current_server = server_id; - - client.serverConnection.command_helper.request_query_list(server_id).then(result => { - if(!result || !result.queries.length) { - container_list_empty.text(tr("No queries available")); - return; - } - - for(const entry of result.queries) { - const tag = $.spawn("div").addClass("entry").text(entry.username + " (" + entry.unique_id + ")"); - tag.on('click', event => { - container_list.find(".selected").removeClass("selected"); - tag.addClass("selected"); - set_selected(entry, false); - }); - container_list.append(tag); - if(entry.username === selected_entry) tag.trigger('click'); - - const text_mesh = (entry.username + " " + entry.unique_id + " " + entry.bounded_server).toLowerCase(); - filter_callbacks.push(text => { - if(typeof(text) === "undefined" || text_mesh.indexOf(text) != -1) { - tag.show(); - return true; - } else { - tag.hide(); - return false; - } - }); - } - - update_filter(); - container_list_empty.hide(); - button_update.prop('disabled', false); - }).catch(error => { - button_update.prop('disabled', false); - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) { - set_error(tr("No permissions")); - return; - } - log.error(LogCategory.CLIENT, tr("Failed to request the query list: %o"), error); - set_error(tr("Failed to request list")); - }); - }).catch(error => { - button_update.prop('disabled', false); - log.error(LogCategory.CLIENT, tr("Failed to get own virtual server id: %o"), error); - set_error(tr("Failed to query server id")); - }); - }; - - const set_selected = (entry: QueryListEntry | undefined, force: boolean) => { - if(entry === selected_query && !force) return; - selected_query = entry; - - if(!selected_query) { - detail_name.text("-"); - detail_unique_id.text("-"); - detail_bound_server.text("-"); - - button_delete.prop('disabled', true); - button_rename.prop('disabled', true); - button_change_password.prop('disabled', true); - } else { - detail_name.text(selected_query.username); - detail_unique_id.text(selected_query.unique_id); - if(selected_query.bounded_server == 0) - detail_bound_server.text(tr("On the instance")); - else if(selected_query.bounded_server === current_server) - detail_bound_server.text(tr("On the current server")); - else - detail_bound_server.text(selected_query.bounded_server.toString()); - - button_delete.prop('disabled', !permission_delete && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_delete_own)); - button_rename.prop('disabled', !permission_rename && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_rename_own)); - if(selected_query.bounded_server != 0) { - button_change_password.prop('disabled', !permission_password && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); - } else { - button_change_password.prop('disabled', !permission_password_global && !(selected_query.unique_id === client.getClient().properties.client_unique_identifier && permission_password_own)); - } - } - }; - - const update_filter = () => { - let value = input_filter.val() as string; - if(!value) value = undefined; - else value = value.toLowerCase(); - - const shown = filter_callbacks.filter(e => e(value)).length; - if(shown > 0) { - container_list_empty.hide(); - } else { - container_list_empty.text(tr("No accounts found")).show(); - } - }; - input_filter.on('change keyup', update_filter); - - /* all buttons */ - { - detail_unique_id_copy.on('click', event => { - if(!selected_query) return; - - copy_to_clipboard(selected_query.unique_id); - createInfoModal(tr("Unique ID copied"), tr("The unique id has been successfully copied to your clipboard.")).open(); - }); - - button_create.on('click', event => { - Modals.spawnQueryCreate(client, (user, pass) => update_list(user)); - }); - - button_delete.on('click', event => { - if(!selected_query) return; - - Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { - if(result) { - client.serverConnection.send_command("querydelete", { - client_login_name: selected_query.username - }).then(() => { - createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); - update_list(undefined); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to delete account"), MessageHelper.formatMessage(tr("Failed to delete account{:br:}Message: {}"), error)).open(); - }); - } - }); - }); - - button_rename.on('click', () => { - if(!selected_query) return; - - createInputModal(tr("Change account name"), tr("Enter the new name for the login:"), text => text.length >= 3, result => { - if(result) { - client.serverConnection.send_command("queryrename", { - client_login_name: selected_query.username, - client_new_login_name: result - }).then(() => { - createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); - update_list(result as string); - }).catch(error => { - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to rename account"), MessageHelper.formatMessage(tr("Failed to rename account{:br:}Message: {}"), error)).open(); - }); - } - }).open(); - }); - - button_change_password.on('click', () => { - if(!selected_query) return; - - createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):"), text => true, result => { - if(result !== false) { - const single_handler: connection.SingleCommandHandler = { - command: "notifyquerypasswordchanges", - function: command => { - Modals.spawnQueryCreated({ - username: command.arguments[0]["client_login_name"], - password: command.arguments[0]["client_login_password"] - }, false); - - return true; - } - }; - client.serverConnection.command_handler_boss().register_single_handler(single_handler); - - client.serverConnection.send_command("querychangepassword", { - client_login_name: selected_query.username, - client_login_password: result - }).catch(error => { - client.serverConnection.command_handler_boss().remove_single_handler(single_handler); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Unable to change password"), MessageHelper.formatMessage(tr("Failed to change password{:br:}Message: {}"), error)).open(); - }); - } - }).open(); - }); - - button_update.on('click', event => update_list(selected_query ? selected_query.username : undefined)); - } - - modal.close_listener.push(() => filter_callbacks = undefined); - - set_selected(undefined, true); - update_list(undefined); - template.dividerfy(); - return template; - }, - footer: null, - - min_width: "25em" - }); - - modal.htmlTag.find(".modal-body").addClass("modal-query-manage"); - modal.open(); - } + modal.htmlTag.find(".modal-body").addClass("modal-query-manage"); + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalServerEdit.ts b/shared/js/ui/modal/ModalServerEdit.ts index 089f1522..bcac98b1 100644 --- a/shared/js/ui/modal/ModalServerEdit.ts +++ b/shared/js/ui/modal/ModalServerEdit.ts @@ -1,767 +1,774 @@ -namespace Modals { - export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise) { - const properties = Object.assign({}, server.properties); +import {ServerEntry, ServerProperties} from "tc-shared/ui/server"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {GroupManager} from "tc-shared/permission/GroupManager"; +import {hashPassword} from "tc-shared/utils/helpers"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect"; +import {network} from "tc-shared/ui/frames/chat"; - let _valid_states: {[key: string]:boolean} = { - general: false - }; +export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise) { + const properties = Object.assign({}, server.properties); - let _toggle_valid = (key: string | undefined, value?: boolean) => { - if(typeof(key) === "string") { - _valid_states[key] = value; + let _valid_states: {[key: string]:boolean} = { + general: false + }; + + let _toggle_valid = (key: string | undefined, value?: boolean) => { + if(typeof(key) === "string") { + _valid_states[key] = value; + } + + let flag = true; + for(const key of Object.keys(_valid_states)) + if(!_valid_states[key]) { + flag = false; + break; } - let flag = true; - for(const key of Object.keys(_valid_states)) - if(!_valid_states[key]) { - flag = false; + if(flag) { + flag = false; + for(const property_name of Object.keys(properties)) { + if(server.properties[property_name] !== properties[property_name]) { + flag = true; break; } - - if(flag) { - flag = false; - for(const property_name of Object.keys(properties)) { - if(server.properties[property_name] !== properties[property_name]) { - flag = true; - break; - } - } } - - button_save.prop("disabled", !flag); - }; - - const modal = createModal({ - header: tr("Manage the Virtual Server"), - body: () => { - const template = $("#tmpl_server_edit").renderTag(Object.assign(Object.assign({}, server.properties), { - server_icon: server.channelTree.client.fileManager.icons.generateTag(server.properties.virtualserver_icon_id) - })); - - /* the tab functionality */ - { - const container_tabs = template.find(".container-categories"); - container_tabs.find(".categories .entry").on('click', event => { - const entry = $(event.target); - - container_tabs.find(".bodies > .body").addClass("hidden"); - container_tabs.find(".categories > .selected").removeClass("selected"); - - entry.addClass("selected"); - container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); - }); - - container_tabs.find(".entry").first().trigger('click'); - } - - apply_general_listener(template.find(".container-general"), server, properties, _toggle_valid); - apply_host_listener(template.find(".container-host"), server, properties, _toggle_valid); - apply_network_listener(template.find(".container-network"), server, properties, _toggle_valid, modal); - apply_security_listener(template.find(".container-security"), server, properties, _toggle_valid); - apply_messages_listener(template.find(".container-messages"), server, properties, _toggle_valid); - apply_misc_listener(template.find(".container-misc"), server, properties, _toggle_valid); - - return template.contents(); - }, - footer: null, - min_width: "35em" - }); - - tooltip(modal.htmlTag); - - const button_save = modal.htmlTag.find(".button-save"); - button_save.on('click', event => { - const changed = {} as ServerProperties; - for(const property_name of Object.keys(properties)) - if(server.properties[property_name] !== properties[property_name]) - changed[property_name] = properties[property_name]; - callback(changed).then(() => { - _toggle_valid(undefined); - }); - }); - - modal.htmlTag.find(".button-cancel").on('click', event => { - modal.close(); - callback(); - }); - - _toggle_valid("general", true); - modal.htmlTag.find(".modal-body").addClass("modal-server-edit modal-blue"); - modal.open(); - } - - - function apply_general_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { - /* name */ - { - const container = tag.find(".virtualserver_name"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1); - - container.on('change', event => { - properties.virtualserver_name = container.val() as string; - - const invalid = properties.virtualserver_name.length > 70 || properties.virtualserver_name.length < 1; - container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_name", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); } - /* icon */ - { - tag.find(".button-select-icon").on('click', event => { - Modals.spawnIconSelect(server.channelTree.client, id => { - const icon_node = tag.find(".icon-preview"); - icon_node.children().remove(); - icon_node.append(server.channelTree.client.fileManager.icons.generateTag(id)); + button_save.prop("disabled", !flag); + }; - console.log("Selected icon ID: %d", id); - properties.virtualserver_icon_id = id; - callback_valid(undefined); //Toggle save button update - }, properties.virtualserver_icon_id); - }); + const modal = createModal({ + header: tr("Manage the Virtual Server"), + body: () => { + const template = $("#tmpl_server_edit").renderTag(Object.assign(Object.assign({}, server.properties), { + server_icon: server.channelTree.client.fileManager.icons.generateTag(server.properties.virtualserver_icon_id) + })); - tag.find(".button-icon-remove").on('click', event => { + /* the tab functionality */ + { + const container_tabs = template.find(".container-categories"); + container_tabs.find(".categories .entry").on('click', event => { + const entry = $(event.target); + + container_tabs.find(".bodies > .body").addClass("hidden"); + container_tabs.find(".categories > .selected").removeClass("selected"); + + entry.addClass("selected"); + container_tabs.find(".bodies > .body." + entry.attr("container")).removeClass("hidden"); + }); + + container_tabs.find(".entry").first().trigger('click'); + } + + apply_general_listener(template.find(".container-general"), server, properties, _toggle_valid); + apply_host_listener(template.find(".container-host"), server, properties, _toggle_valid); + apply_network_listener(template.find(".container-network"), server, properties, _toggle_valid, modal); + apply_security_listener(template.find(".container-security"), server, properties, _toggle_valid); + apply_messages_listener(template.find(".container-messages"), server, properties, _toggle_valid); + apply_misc_listener(template.find(".container-misc"), server, properties, _toggle_valid); + + return template.contents(); + }, + footer: null, + min_width: "35em" + }); + + tooltip.initialize(modal.htmlTag); + + const button_save = modal.htmlTag.find(".button-save"); + button_save.on('click', event => { + const changed = {} as ServerProperties; + for(const property_name of Object.keys(properties)) + if(server.properties[property_name] !== properties[property_name]) + changed[property_name] = properties[property_name]; + callback(changed).then(() => { + _toggle_valid(undefined); + }); + }); + + modal.htmlTag.find(".button-cancel").on('click', event => { + modal.close(); + callback(); + }); + + _toggle_valid("general", true); + modal.htmlTag.find(".modal-body").addClass("modal-server-edit modal-blue"); + modal.open(); +} + + +function apply_general_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { + /* name */ + { + const container = tag.find(".virtualserver_name"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NAME).granted(1); + + container.on('change', event => { + properties.virtualserver_name = container.val() as string; + + const invalid = properties.virtualserver_name.length > 70 || properties.virtualserver_name.length < 1; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_name", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* icon */ + { + tag.find(".button-select-icon").on('click', event => { + spawnIconSelect(server.channelTree.client, id => { const icon_node = tag.find(".icon-preview"); icon_node.children().remove(); - icon_node.append(server.channelTree.client.fileManager.icons.generateTag(0)); + icon_node.append(server.channelTree.client.fileManager.icons.generateTag(id)); - console.log("Remove server icon"); - properties.virtualserver_icon_id = 0; + console.log("Selected icon ID: %d", id); + properties.virtualserver_icon_id = id; callback_valid(undefined); //Toggle save button update - }); + }, properties.virtualserver_icon_id); + }); + + tag.find(".button-icon-remove").on('click', event => { + const icon_node = tag.find(".icon-preview"); + icon_node.children().remove(); + icon_node.append(server.channelTree.client.fileManager.icons.generateTag(0)); + + console.log("Remove server icon"); + properties.virtualserver_icon_id = 0; + callback_valid(undefined); //Toggle save button update + }); + } + + /* password */ + { + //TODO: On save let the user retype his password? + const container = tag.find(".virtualserver_password"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PASSWORD).granted(1); + + container.on('change', event => { + const password = container.val() as string; + properties.virtualserver_flag_password = !!password; + if(properties.virtualserver_flag_password) { + hashPassword(password).then(pass => properties.virtualserver_password = pass); + } + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* slots */ + { + const container_max = tag.find(".virtualserver_maxclients"); + const container_reserved = tag.find(".virtualserver_reserved_slots"); + + /* max users */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_MAXCLIENTS).granted(1); + + container_max.on('change', event => { + properties.virtualserver_maxclients = parseInt(container_max.val() as string); + + const invalid = properties.virtualserver_maxclients < 1 || properties.virtualserver_maxclients > 1024; + container_max.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_maxclients", !invalid); + + container_reserved.trigger('change'); /* update the flag */ + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); } - /* password */ + /* reserved */ { - //TODO: On save let the user retype his password? - const container = tag.find(".virtualserver_password"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PASSWORD).granted(1); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS).granted(1); + + container_reserved.on('change', event => { + properties.virtualserver_reserved_slots = parseInt(container_reserved.val() as string); + + const invalid = properties.virtualserver_reserved_slots > properties.virtualserver_maxclients; + container_reserved.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_reserved_slots", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + + /* Welcome message */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE).granted(1); + const container = tag.find(".container-welcome-message"); + const input = container.find("textarea"); + + const insert_tag = (open: string, close: string) => { + if(input.prop("disabled")) + return; + + const node = input[0] as HTMLTextAreaElement; + if (node.selectionStart || node.selectionStart == 0) { + const startPos = node.selectionStart; + const endPos = node.selectionEnd; + node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); + node.selectionEnd = endPos + open.length; + node.selectionStart = node.selectionEnd; + } else { + node.value += open + close; + node.selectionEnd = node.value.length - close.length; + node.selectionStart = node.selectionEnd; + } + + input.focus().trigger('change'); + }; + + input.on('change', event => { + console.log(tr("Welcome message edited: %o"), input.val()); + properties.virtualserver_welcomemessage = input.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); + container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); + container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); + container.find(".button-color input").on('change', event => { + insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') + }); + } +} + +function apply_network_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void, modal: Modal) { + /* binding */ + { + /* host */ + { + const container = tag.find(".virtualserver_host"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOST).granted(1); container.on('change', event => { - const password = container.val() as string; - properties.virtualserver_flag_password = !!password; - if(properties.virtualserver_flag_password) { - helpers.hashPassword(password).then(pass => properties.virtualserver_password = pass); - } - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* slots */ - { - const container_max = tag.find(".virtualserver_maxclients"); - const container_reserved = tag.find(".virtualserver_reserved_slots"); - - /* max users */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_MAXCLIENTS).granted(1); - - container_max.on('change', event => { - properties.virtualserver_maxclients = parseInt(container_max.val() as string); - - const invalid = properties.virtualserver_maxclients < 1 || properties.virtualserver_maxclients > 1024; - container_max.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_maxclients", !invalid); - - container_reserved.trigger('change'); /* update the flag */ - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* reserved */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_RESERVED_SLOTS).granted(1); - - container_reserved.on('change', event => { - properties.virtualserver_reserved_slots = parseInt(container_reserved.val() as string); - - const invalid = properties.virtualserver_reserved_slots > properties.virtualserver_maxclients; - container_reserved.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_reserved_slots", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - } - - /* Welcome message */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WELCOMEMESSAGE).granted(1); - const container = tag.find(".container-welcome-message"); - const input = container.find("textarea"); - - const insert_tag = (open: string, close: string) => { - if(input.prop("disabled")) - return; - - const node = input[0] as HTMLTextAreaElement; - if (node.selectionStart || node.selectionStart == 0) { - const startPos = node.selectionStart; - const endPos = node.selectionEnd; - node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos); - node.selectionEnd = endPos + open.length; - node.selectionStart = node.selectionEnd; - } else { - node.value += open + close; - node.selectionEnd = node.value.length - close.length; - node.selectionStart = node.selectionEnd; - } - - input.focus().trigger('change'); - }; - - input.on('change', event => { - console.log(tr("Welcome message edited: %o"), input.val()); - properties.virtualserver_welcomemessage = input.val() as string; + properties.virtualserver_host = container.val() as string; callback_valid(undefined); //Toggle save button update }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - container.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]')); - container.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]')); - container.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]')); - container.find(".button-color input").on('change', event => { - insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]') - }); + server.updateProperties().then(() => container.val(server.properties.virtualserver_host)); } - } - function apply_network_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void, modal: Modal) { - /* binding */ + /* port */ { - /* host */ - { - const container = tag.find(".virtualserver_host"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOST).granted(1); - - container.on('change', event => { - properties.virtualserver_host = container.val() as string; - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_host)); - } - - /* port */ - { - const container = tag.find(".virtualserver_port"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PORT).granted(1); - - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_port = value; - - const valid = value >= 1 && value < 65536; - callback_valid("virtualserver_port", valid); - container.firstParent(".input-boxed").toggleClass("is-invalid", !valid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_port)); - } - - /* TeamSpeak server list */ - { - const container = tag.find(".virtualserver_weblist_enabled"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WEBLIST).granted(1); - - container.on('change', event => { - properties.virtualserver_weblist_enabled = container.prop("checked"); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".checkbox").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.prop("checked", server.properties.virtualserver_weblist_enabled)); - } - } - - /* file download */ - { - /* bandwidth */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); - const container = tag.find(".virtualserver_max_download_total_bandwidth"); - - container.on('change', event => { - properties.virtualserver_max_download_total_bandwidth = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_max_download_total_bandwidth)); - } - - /* Quota */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); - const container = tag.find(".virtualserver_download_quota"); - - container.on('change', event => { - properties.virtualserver_download_quota = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_download_quota)); - } - } - - /* file upload */ - { - /* bandwidth */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); - const container = tag.find(".virtualserver_max_upload_total_bandwidth"); - - container.on('change', event => { - properties.virtualserver_max_upload_total_bandwidth = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_max_upload_total_bandwidth)); - } - - /* Quota */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); - const container = tag.find(".virtualserver_upload_quota"); - - container.on('change', event => { - properties.virtualserver_upload_quota = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_upload_quota)); - } - } - - /* quota info */ - { - server.updateProperties().then(() => { - tag.find(".value.virtualserver_month_bytes_downloaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_month_bytes_downloaded)); - tag.find(".value.virtualserver_month_bytes_uploaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_month_bytes_uploaded)); - - tag.find(".value.virtualserver_total_bytes_downloaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_total_bytes_downloaded)); - tag.find(".value.virtualserver_total_bytes_uploaded").text(MessageHelper.network.format_bytes(server.properties.virtualserver_total_bytes_uploaded)); - }); - } - - /* quota update task */ - if(server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CONNECTIONINFO_VIEW).granted(1)) { - const month_bytes_downloaded = tag.find(".value.virtualserver_month_bytes_downloaded")[0]; - const month_bytes_uploaded = tag.find(".value.virtualserver_month_bytes_uploaded")[0]; - const total_bytes_downloaded = tag.find(".value.virtualserver_total_bytes_downloaded")[0]; - const total_bytes_uploaded = tag.find(".value.virtualserver_total_bytes_uploaded")[0]; - - let id = setInterval(() => { - if(!modal.shown) { - clearInterval(id); - return; - } - - server.request_connection_info().then(info => { - if(info.connection_filetransfer_bytes_sent_month && month_bytes_downloaded) - month_bytes_downloaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_sent_month); - if(info.connection_filetransfer_bytes_received_month && month_bytes_uploaded) - month_bytes_uploaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_received_month); - - if(info.connection_filetransfer_bytes_sent_total && total_bytes_downloaded) - total_bytes_downloaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_sent_total); - if(info.connection_filetransfer_bytes_received_total && total_bytes_uploaded) - total_bytes_uploaded.innerText = MessageHelper.network.format_bytes(info.connection_filetransfer_bytes_received_total); - }); - }, 1000); - modal.close_listener.push(() => clearInterval(id)); - } - } - - function apply_host_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { - /* host message */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1); - - /* message */ - { - const container = tag.find(".virtualserver_hostmessage"); - - container.on('change', event => { - properties.virtualserver_hostmessage = container.val() as string; - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* mode */ - { - const container = tag.find(".virtualserver_hostmessage_mode"); - - container.on('change', event => { - properties.virtualserver_hostmessage_mode = Math.min(3, Math.max(0, parseInt(container.val() as string))); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - } - - /* host banner */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1); - - /* URL */ - { - const container = tag.find(".virtualserver_hostbanner_url"); - - container.on('change', event => { - properties.virtualserver_hostbanner_url = container.val() as string; - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* Image URL/Image Preview */ - { - const container = tag.find(".virtualserver_hostbanner_gfx_url"); - const container_preview = tag.find(".container-host-message .container-gfx-preview img"); - - container.on('change', event => { - properties.virtualserver_hostbanner_gfx_url = container.val() as string; - container_preview.attr("src", properties.virtualserver_hostbanner_gfx_url).toggle(!!properties.virtualserver_hostbanner_gfx_url); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* Image Refresh */ - { - const container = tag.find(".virtualserver_hostbanner_gfx_interval"); - - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_hostbanner_gfx_interval = value; - - const invalid = value < 60 && value != 0; - container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_hostbanner_gfx_interval", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* mode */ - { - const container = tag.find(".virtualserver_hostbanner_mode"); - - container.on('change', event => { - properties.virtualserver_hostbanner_mode = Math.min(2, Math.max(0, parseInt(container.val() as string))); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - } - - /* host button */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1); - - /* URL */ - { - const container = tag.find(".virtualserver_hostbutton_url"); - - container.on('change', event => { - properties.virtualserver_hostbutton_url = container.val() as string; - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* Tooltip */ - { - const container = tag.find(".virtualserver_hostbutton_tooltip"); - - container.on('change', event => { - properties.virtualserver_hostbutton_tooltip = container.val() as string; - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* Icon URL/Icon Preview */ - { - const container = tag.find(".virtualserver_hostbutton_gfx_url"); - const container_preview = tag.find(".container-host-button .container-gfx-preview img"); - - container.on('change', event => { - properties.virtualserver_hostbutton_gfx_url = container.val() as string; - container_preview.attr("src", properties.virtualserver_hostbutton_gfx_url).toggle(!!properties.virtualserver_hostbutton_gfx_url); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - } - } - - function apply_security_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { - /* Anti flood */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1); - - /* reduce */ - { - const container = tag.find(".virtualserver_antiflood_points_tick_reduce"); - - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_antiflood_points_tick_reduce = value; - - const invalid = value < 1 || value > 999999; - container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_antiflood_points_tick_reduce", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_tick_reduce)); - } - - /* block commands */ - { - const container = tag.find(".virtualserver_antiflood_points_needed_command_block"); - - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_antiflood_points_needed_command_block = value; - - const invalid = value < 1 || value > 999999; - container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_antiflood_points_needed_command_block", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_command_block)); - } - - /* block ip */ - { - const container = tag.find(".virtualserver_antiflood_points_needed_ip_block"); - - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_antiflood_points_needed_ip_block = value; - - const invalid = value < 1 || value > 999999; - container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_antiflood_points_needed_ip_block", !invalid); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_ip_block)); - } - } - - /* encryption */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE).granted(1); - const container = tag.find(".virtualserver_codec_encryption_mode"); - - container.on('change', event => { - properties.virtualserver_codec_encryption_mode = Math.min(2, Math.max(0, parseInt(container.val() as string))); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } - - /* security level */ - { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL).granted(1); - const container = tag.find(".virtualserver_needed_identity_security_level"); + const container = tag.find(".virtualserver_port"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PORT).granted(1); container.on('change', event => { const value = parseInt(container.val() as string); - properties.virtualserver_needed_identity_security_level = value; + properties.virtualserver_port = value; - const invalid = value < 8 || value > 99; + const valid = value >= 1 && value < 65536; + callback_valid("virtualserver_port", valid); + container.firstParent(".input-boxed").toggleClass("is-invalid", !valid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_port)); + } + + /* TeamSpeak server list */ + { + const container = tag.find(".virtualserver_weblist_enabled"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_WEBLIST).granted(1); + + container.on('change', event => { + properties.virtualserver_weblist_enabled = container.prop("checked"); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".checkbox").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.prop("checked", server.properties.virtualserver_weblist_enabled)); + } + } + + /* file download */ + { + /* bandwidth */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); + const container = tag.find(".virtualserver_max_download_total_bandwidth"); + + container.on('change', event => { + properties.virtualserver_max_download_total_bandwidth = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_max_download_total_bandwidth)); + } + + /* Quota */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); + const container = tag.find(".virtualserver_download_quota"); + + container.on('change', event => { + properties.virtualserver_download_quota = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_download_quota)); + } + } + + /* file upload */ + { + /* bandwidth */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_SETTINGS).granted(1); + const container = tag.find(".virtualserver_max_upload_total_bandwidth"); + + container.on('change', event => { + properties.virtualserver_max_upload_total_bandwidth = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_max_upload_total_bandwidth)); + } + + /* Quota */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_FT_QUOTAS).granted(1); + const container = tag.find(".virtualserver_upload_quota"); + + container.on('change', event => { + properties.virtualserver_upload_quota = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_upload_quota)); + } + } + + /* quota info */ + { + server.updateProperties().then(() => { + tag.find(".value.virtualserver_month_bytes_downloaded").text(network.format_bytes(server.properties.virtualserver_month_bytes_downloaded)); + tag.find(".value.virtualserver_month_bytes_uploaded").text(network.format_bytes(server.properties.virtualserver_month_bytes_uploaded)); + + tag.find(".value.virtualserver_total_bytes_downloaded").text(network.format_bytes(server.properties.virtualserver_total_bytes_downloaded)); + tag.find(".value.virtualserver_total_bytes_uploaded").text(network.format_bytes(server.properties.virtualserver_total_bytes_uploaded)); + }); + } + + /* quota update task */ + if(server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CONNECTIONINFO_VIEW).granted(1)) { + const month_bytes_downloaded = tag.find(".value.virtualserver_month_bytes_downloaded")[0]; + const month_bytes_uploaded = tag.find(".value.virtualserver_month_bytes_uploaded")[0]; + const total_bytes_downloaded = tag.find(".value.virtualserver_total_bytes_downloaded")[0]; + const total_bytes_uploaded = tag.find(".value.virtualserver_total_bytes_uploaded")[0]; + + let id = setInterval(() => { + if(!modal.shown) { + clearInterval(id); + return; + } + + server.request_connection_info().then(info => { + if(info.connection_filetransfer_bytes_sent_month && month_bytes_downloaded) + month_bytes_downloaded.innerText = network.format_bytes(info.connection_filetransfer_bytes_sent_month); + if(info.connection_filetransfer_bytes_received_month && month_bytes_uploaded) + month_bytes_uploaded.innerText = network.format_bytes(info.connection_filetransfer_bytes_received_month); + + if(info.connection_filetransfer_bytes_sent_total && total_bytes_downloaded) + total_bytes_downloaded.innerText = network.format_bytes(info.connection_filetransfer_bytes_sent_total); + if(info.connection_filetransfer_bytes_received_total && total_bytes_uploaded) + total_bytes_uploaded.innerText = network.format_bytes(info.connection_filetransfer_bytes_received_total); + }); + }, 1000); + modal.close_listener.push(() => clearInterval(id)); + } +} + +function apply_host_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { + /* host message */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTMESSAGE).granted(1); + + /* message */ + { + const container = tag.find(".virtualserver_hostmessage"); + + container.on('change', event => { + properties.virtualserver_hostmessage = container.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* mode */ + { + const container = tag.find(".virtualserver_hostmessage_mode"); + + container.on('change', event => { + properties.virtualserver_hostmessage_mode = Math.min(3, Math.max(0, parseInt(container.val() as string))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } + + /* host banner */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBANNER).granted(1); + + /* URL */ + { + const container = tag.find(".virtualserver_hostbanner_url"); + + container.on('change', event => { + properties.virtualserver_hostbanner_url = container.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* Image URL/Image Preview */ + { + const container = tag.find(".virtualserver_hostbanner_gfx_url"); + const container_preview = tag.find(".container-host-message .container-gfx-preview img"); + + container.on('change', event => { + properties.virtualserver_hostbanner_gfx_url = container.val() as string; + container_preview.attr("src", properties.virtualserver_hostbanner_gfx_url).toggle(!!properties.virtualserver_hostbanner_gfx_url); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* Image Refresh */ + { + const container = tag.find(".virtualserver_hostbanner_gfx_interval"); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_hostbanner_gfx_interval = value; + + const invalid = value < 60 && value != 0; container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); - callback_valid("virtualserver_needed_identity_security_level", !invalid); + callback_valid("virtualserver_hostbanner_gfx_interval", !invalid); }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } - server.updateProperties().then(() => container.val(server.properties.virtualserver_needed_identity_security_level)); + /* mode */ + { + const container = tag.find(".virtualserver_hostbanner_mode"); + + container.on('change', event => { + properties.virtualserver_hostbanner_mode = Math.min(2, Math.max(0, parseInt(container.val() as string))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); } } - function apply_messages_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1); + /* host button */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_HOSTBUTTON).granted(1); - /* channel topic */ + /* URL */ { - const container = tag.find(".virtualserver_default_channel_topic"); + const container = tag.find(".virtualserver_hostbutton_url"); container.on('change', event => { - properties.virtualserver_default_channel_topic = container.val() as string; + properties.virtualserver_hostbutton_url = container.val() as string; callback_valid(undefined); //Toggle save button update }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); } - /* channel description */ + /* Tooltip */ { - const container = tag.find(".virtualserver_default_channel_description"); + const container = tag.find(".virtualserver_hostbutton_tooltip"); container.on('change', event => { - properties.virtualserver_default_channel_description = container.val() as string; + properties.virtualserver_hostbutton_tooltip = container.val() as string; callback_valid(undefined); //Toggle save button update }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_default_channel_description)); } - /* client description */ + /* Icon URL/Icon Preview */ { - const container = tag.find(".virtualserver_default_client_description"); + const container = tag.find(".virtualserver_hostbutton_gfx_url"); + const container_preview = tag.find(".container-host-button .container-gfx-preview img"); container.on('change', event => { - properties.virtualserver_default_client_description = container.val() as string; + properties.virtualserver_hostbutton_gfx_url = container.val() as string; + container_preview.attr("src", properties.virtualserver_hostbutton_gfx_url).toggle(!!properties.virtualserver_hostbutton_gfx_url); callback_valid(undefined); //Toggle save button update }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + } +} - server.updateProperties().then(() => container.val(server.properties.virtualserver_default_client_description)); +function apply_security_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { + /* Anti flood */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_ANTIFLOOD).granted(1); + + /* reduce */ + { + const container = tag.find(".virtualserver_antiflood_points_tick_reduce"); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_antiflood_points_tick_reduce = value; + + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_tick_reduce", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_tick_reduce)); + } + + /* block commands */ + { + const container = tag.find(".virtualserver_antiflood_points_needed_command_block"); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_antiflood_points_needed_command_block = value; + + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_needed_command_block", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_command_block)); + } + + /* block ip */ + { + const container = tag.find(".virtualserver_antiflood_points_needed_ip_block"); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_antiflood_points_needed_ip_block = value; + + const invalid = value < 1 || value > 999999; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_antiflood_points_needed_ip_block", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_antiflood_points_needed_ip_block)); } } - function apply_misc_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { - /* default groups */ + /* encryption */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CODEC_ENCRYPTION_MODE).granted(1); + const container = tag.find(".virtualserver_codec_encryption_mode"); + + container.on('change', event => { + properties.virtualserver_codec_encryption_mode = Math.min(2, Math.max(0, parseInt(container.val() as string))); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* security level */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_NEEDED_IDENTITY_SECURITY_LEVEL).granted(1); + const container = tag.find(".virtualserver_needed_identity_security_level"); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_needed_identity_security_level = value; + + const invalid = value < 8 || value > 99; + container.firstParent(".input-boxed").toggleClass("is-invalid", invalid); + callback_valid("virtualserver_needed_identity_security_level", !invalid); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_needed_identity_security_level)); + } +} + +function apply_messages_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MESSAGES).granted(1); + + /* channel topic */ + { + const container = tag.find(".virtualserver_default_channel_topic"); + + container.on('change', event => { + properties.virtualserver_default_channel_topic = container.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* channel description */ + { + const container = tag.find(".virtualserver_default_channel_description"); + + container.on('change', event => { + properties.virtualserver_default_channel_description = container.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_default_channel_description)); + } + + /* client description */ + { + const container = tag.find(".virtualserver_default_client_description"); + + container.on('change', event => { + properties.virtualserver_default_client_description = container.val() as string; + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_default_client_description)); + } +} + +function apply_misc_listener(tag: JQuery, server: ServerEntry, properties: ServerProperties, callback_valid: (key: string | undefined, flag?: boolean) => void) { + /* default groups */ + { + /* Server Group */ { - /* Server Group */ - { - const container = tag.find(".virtualserver_default_server_group"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP).granted(1); + const container = tag.find(".virtualserver_default_server_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_SERVERGROUP).granted(1); - container.on('change', event => { - properties.virtualserver_default_server_group = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + container.on('change', event => { + properties.virtualserver_default_server_group = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - for(const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { - if(group.type != 2) continue; - let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); - if(group.id == server.properties.virtualserver_default_server_group) - group_tag.prop("selected", true); - group_tag.appendTo(container); - } - } - - /* Music Group */ - { - const container = tag.find(".virtualserver_default_music_group"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP).granted(1); - - container.on('change', event => { - properties.virtualserver_default_music_group = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - for(const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { - if(group.type != 2) continue; - let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); - if(group.id == server.properties.virtualserver_default_music_group) - group_tag.prop("selected", true); - group_tag.appendTo(container); - } - } - - /* Channel Admin Group */ - { - const container = tag.find(".virtualserver_default_channel_admin_group"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP).granted(1); - - container.on('change', event => { - properties.virtualserver_default_channel_admin_group = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - for(const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { - if(group.type != 2) continue; - let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); - if(group.id == server.properties.virtualserver_default_channel_admin_group) - group_tag.prop("selected", true); - group_tag.appendTo(container); - } - } - - /* Channel Guest Group */ - { - const container = tag.find(".virtualserver_default_channel_group"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP).granted(1); - - container.on('change', event => { - properties.virtualserver_default_channel_group = parseInt(container.val() as string); - callback_valid(undefined); //Toggle save button update - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - for(const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { - if(group.type != 2) continue; - let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); - if(group.id == server.properties.virtualserver_default_channel_group) - group_tag.prop("selected", true); - group_tag.appendTo(container); - } + for(const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { + if(group.type != 2) continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if(group.id == server.properties.virtualserver_default_server_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); } } - /* complains */ + /* Music Group */ { - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1); + const container = tag.find(".virtualserver_default_music_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_MUSICGROUP).granted(1); - /* ban threshold */ - { - const container = tag.find(".virtualserver_complain_autoban_count"); + container.on('change', event => { + properties.virtualserver_default_music_group = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - container.on('change', event => { - properties.virtualserver_complain_autoban_count = parseInt(container.val() as string); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_count)); - } - - /* ban time */ - { - const container = tag.find(".virtualserver_complain_autoban_time"); - - container.on('change', event => { - properties.virtualserver_complain_autoban_time = parseInt(container.val() as string); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_time)); - } - - /* auto remove time */ - { - const container = tag.find(".virtualserver_complain_remove_time"); - - container.on('change', event => { - properties.virtualserver_complain_remove_time = parseInt(container.val() as string); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - - server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_remove_time)); + for(const group of server.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { + if(group.type != 2) continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if(group.id == server.properties.virtualserver_default_music_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); } } - /* others */ + /* Channel Admin Group */ { - /* clients before silence */ - { - const container = tag.find(".virtualserver_min_clients_in_channel_before_forced_silence"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE).granted(1); + const container = tag.find(".virtualserver_default_channel_admin_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELADMINGROUP).granted(1); - container.on('change', event => { - const value = parseInt(container.val() as string); - properties.virtualserver_min_clients_in_channel_before_forced_silence = value; - callback_valid("virtualserver_min_clients_in_channel_before_forced_silence", value > 1); - container.firstParent(".input-boxed").toggleClass("is-invalid", value <= 1); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + container.on('change', event => { + properties.virtualserver_default_channel_admin_group = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - server.updateProperties().then(() => container.val(server.properties.virtualserver_min_clients_in_channel_before_forced_silence)); + for(const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { + if(group.type != 2) continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if(group.id == server.properties.virtualserver_default_channel_admin_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); } + } - /* priority speaker dim factor */ - { - const container = tag.find(".virtualserver_priority_speaker_dimm_modificator"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR).granted(1); + /* Channel Guest Group */ + { + const container = tag.find(".virtualserver_default_channel_group"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_DEFAULT_CHANNELGROUP).granted(1); - container.on('change', event => { - properties.virtualserver_priority_speaker_dimm_modificator = parseInt(container.val() as string); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + container.on('change', event => { + properties.virtualserver_default_channel_group = parseInt(container.val() as string); + callback_valid(undefined); //Toggle save button update + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + for(const group of server.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { + if(group.type != 2) continue; + let group_tag = $.spawn("option").text(group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]").attr("group-id", group.id); + if(group.id == server.properties.virtualserver_default_channel_group) + group_tag.prop("selected", true); + group_tag.appendTo(container); } + } + } - /* channel delete delay */ - { - const container = tag.find(".virtualserver_channel_temp_delete_delay_default"); - const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT).granted(1); + /* complains */ + { + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_COMPLAIN).granted(1); - container.on('change', event => { - properties.virtualserver_channel_temp_delete_delay_default = parseInt(container.val() as string); - callback_valid(undefined); - }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); - } + /* ban threshold */ + { + const container = tag.find(".virtualserver_complain_autoban_count"); + + container.on('change', event => { + properties.virtualserver_complain_autoban_count = parseInt(container.val() as string); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_count)); + } + + /* ban time */ + { + const container = tag.find(".virtualserver_complain_autoban_time"); + + container.on('change', event => { + properties.virtualserver_complain_autoban_time = parseInt(container.val() as string); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_autoban_time)); + } + + /* auto remove time */ + { + const container = tag.find(".virtualserver_complain_remove_time"); + + container.on('change', event => { + properties.virtualserver_complain_remove_time = parseInt(container.val() as string); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_complain_remove_time)); + } + } + + /* others */ + { + /* clients before silence */ + { + const container = tag.find(".virtualserver_min_clients_in_channel_before_forced_silence"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_FORCED_SILENCE).granted(1); + + container.on('change', event => { + const value = parseInt(container.val() as string); + properties.virtualserver_min_clients_in_channel_before_forced_silence = value; + callback_valid("virtualserver_min_clients_in_channel_before_forced_silence", value > 1); + container.firstParent(".input-boxed").toggleClass("is-invalid", value <= 1); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + + server.updateProperties().then(() => container.val(server.properties.virtualserver_min_clients_in_channel_before_forced_silence)); + } + + /* priority speaker dim factor */ + { + const container = tag.find(".virtualserver_priority_speaker_dimm_modificator"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_PRIORITY_SPEAKER_DIMM_MODIFICATOR).granted(1); + + container.on('change', event => { + properties.virtualserver_priority_speaker_dimm_modificator = parseInt(container.val() as string); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); + } + + /* channel delete delay */ + { + const container = tag.find(".virtualserver_channel_temp_delete_delay_default"); + const permission = server.channelTree.client.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_MODIFY_CHANNEL_TEMP_DELETE_DELAY_DEFAULT).granted(1); + + container.on('change', event => { + properties.virtualserver_channel_temp_delete_delay_default = parseInt(container.val() as string); + callback_valid(undefined); + }).prop("disabled", !permission).firstParent(".input-boxed").toggleClass("disabled", !permission); } } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalServerInfo.ts b/shared/js/ui/modal/ModalServerInfo.ts index 08fe0c71..5a3ef892 100644 --- a/shared/js/ui/modal/ModalServerInfo.ts +++ b/shared/js/ui/modal/ModalServerInfo.ts @@ -1,231 +1,245 @@ -namespace Modals { - export function openServerInfo(server: ServerEntry) { - let modal: Modal; - let update_callbacks: ServerBandwidthInfoUpdateCallback[] = []; +import { + openServerInfoBandwidth, + RequestInfoStatus, + ServerBandwidthInfoUpdateCallback +} from "tc-shared/ui/modal/ModalServerInfoBandwidth"; +import {ServerEntry} from "tc-shared/ui/server"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {createErrorModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import * as i18nc from "tc-shared/i18n/country"; +import {format_time, formatMessage} from "tc-shared/ui/frames/chat"; +import {Hostbanner} from "tc-shared/ui/frames/hostbanner"; - modal = createModal({ - header: tr("Server Information: ") + server.properties.virtualserver_name, - body: () => { - const template = $("#tmpl_server_info").renderTag(); +declare const moment; +export function openServerInfo(server: ServerEntry) { + let modal: Modal; + let update_callbacks: ServerBandwidthInfoUpdateCallback[] = []; - const children = template.children(); - const top = template.find(".container-top"); - const update_values = () => { - apply_hostbanner(server, top); - apply_category_1(server, children, update_callbacks); - apply_category_2(server, children, update_callbacks); - apply_category_3(server, children, update_callbacks); - }; + modal = createModal({ + header: tr("Server Information: ") + server.properties.virtualserver_name, + body: () => { + const template = $("#tmpl_server_info").renderTag(); - const button_update = template.find(".button-update"); - button_update.on('click', event => { - button_update.prop("disabled", true); - server.updateProperties().then(() => { - update_callbacks = []; - update_values(); - }).catch(error => { - log.warn(LogCategory.CLIENT, tr("Failed to refresh server properties: %o"), error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Refresh failed"), MessageHelper.formatMessage(tr("Failed to refresh server properties.{:br:}Error: {}"), error)).open(); - }).then(() => { - button_update.prop("disabled", false); - }); - }).trigger('click'); + const children = template.children(); + const top = template.find(".container-top"); + const update_values = () => { + apply_hostbanner(server, top); + apply_category_1(server, children, update_callbacks); + apply_category_2(server, children, update_callbacks); + apply_category_3(server, children, update_callbacks); + }; - update_values(); - tooltip(template); - return template.children(); - }, - footer: null, - min_width: "25em" + const button_update = template.find(".button-update"); + button_update.on('click', event => { + button_update.prop("disabled", true); + server.updateProperties().then(() => { + update_callbacks = []; + update_values(); + }).catch(error => { + log.warn(LogCategory.CLIENT, tr("Failed to refresh server properties: %o"), error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Refresh failed"), formatMessage(tr("Failed to refresh server properties.{:br:}Error: {}"), error)).open(); + }).then(() => { + button_update.prop("disabled", false); + }); + }).trigger('click'); + + update_values(); + tooltip.initialize(template); + return template.children(); + }, + footer: null, + min_width: "25em" + }); + + const updater = setInterval(() => { + server.request_connection_info().then(info => update_callbacks.forEach(e => e(RequestInfoStatus.SUCCESS, info))).catch(error => { + if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + update_callbacks.forEach(e => e(RequestInfoStatus.NO_PERMISSION)); + return; + } + update_callbacks.forEach(e => e(RequestInfoStatus.UNKNOWN)); + }); + }, 1000); + + + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".button-show-bandwidth").on('click', event => { + const custom_callbacks = []; + const custom_callback_caller = (status, info) => { custom_callbacks.forEach(e => e(status, info)); }; + + update_callbacks.push(custom_callback_caller); + openServerInfoBandwidth(server, custom_callbacks).close_listener.push(() => { + update_callbacks.remove(custom_callback_caller); + }); + }); + + modal.htmlTag.find(".modal-body").addClass("modal-server-info"); + modal.open(); + modal.close_listener.push(() => clearInterval(updater)); +} + +function apply_hostbanner(server: ServerEntry, tag: JQuery) { + let container: JQuery; + tag.empty().append( + container = $.spawn("div").addClass("container-hostbanner") + ).addClass("hidden"); + + const htag = Hostbanner.generate_tag(server.properties.virtualserver_hostbanner_gfx_url, server.properties.virtualserver_hostbanner_gfx_interval, server.properties.virtualserver_hostbanner_mode); + htag.then(t => { + if(!t) return; + + tag.removeClass("hidden"); + container.append(t); + }); +} + +function apply_category_1(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { + /* server name */ + { + const container = tag.find(".server-name"); + container.text(server.properties.virtualserver_name); + } + + /* server region */ + { + const container = tag.find(".server-region").empty(); + container.append( + $.spawn("div").addClass("country flag-" + server.properties.virtualserver_country_code.toLowerCase()), + $.spawn("a").text(i18nc.country_name(server.properties.virtualserver_country_code, tr("Global"))) + ); + } + + /* slots */ + { + const container = tag.find(".server-slots"); + + let text = server.properties.virtualserver_clientsonline + "/" + server.properties.virtualserver_maxclients; + if(server.properties.virtualserver_queryclientsonline) + text += " +" + (server.properties.virtualserver_queryclientsonline > 1 ? + server.properties.virtualserver_queryclientsonline + " " + tr("Queries") : + server.properties.virtualserver_queryclientsonline + " " + tr("Query")); + if(server.properties.virtualserver_reserved_slots) + text += " (" + server.properties.virtualserver_reserved_slots + " " + tr("Reserved") + ")"; + + container.text(text); + } + + /* first run */ + { + const container = tag.find(".server-first-run"); + + container.text( + server.properties.virtualserver_created > 0 ? + moment(server.properties.virtualserver_created * 1000).format('MMMM Do YYYY, h:mm:ss a') : + tr("Unknown") + ); + } + + /* uptime */ + { + const container = tag.find(".server-uptime"); + const update = () => container.text(format_time(server.calculateUptime() * 1000, tr("just started"))); + update_callbacks.push(update); + update(); + } +} + +function apply_category_2(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { + /* ip */ + { + const container = tag.find(".server-ip"); + container.text(server.remote_address.host + (server.remote_address.port == 9987 ? "" : (":" + server.remote_address.port))) + } + + /* version */ + { + const container = tag.find(".server-version"); + + let timestamp = -1; + const version = (server.properties.virtualserver_version || "unknwon").replace(/ ?\[build: ?([0-9]+)]/gmi, (group, ts) => { + timestamp = parseInt(ts); + return ""; }); - const updater = setInterval(() => { - server.request_connection_info().then(info => update_callbacks.forEach(e => e(RequestInfoStatus.SUCCESS, info))).catch(error => { - if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { - update_callbacks.forEach(e => e(RequestInfoStatus.NO_PERMISSION)); - return; - } - update_callbacks.forEach(e => e(RequestInfoStatus.UNKNOWN)); - }); - }, 1000); - - - modal.htmlTag.find(".button-close").on('click', event => modal.close()); - modal.htmlTag.find(".button-show-bandwidth").on('click', event => { - const custom_callbacks = []; - const custom_callback_caller = (status, info) => { custom_callbacks.forEach(e => e(status, info)); }; - - update_callbacks.push(custom_callback_caller); - Modals.openServerInfoBandwidth(server, custom_callbacks).close_listener.push(() => { - update_callbacks.remove(custom_callback_caller); - }); - }); - - modal.htmlTag.find(".modal-body").addClass("modal-server-info"); - modal.open(); - modal.close_listener.push(() => clearInterval(updater)); + container.find("a").text(version); + container.find(".container-tooltip").toggle(timestamp > 0).find(".tooltip a").text( + moment(timestamp * 1000).format('[Build timestamp:] YYYY-MM-DD HH:mm Z') + ); } - function apply_hostbanner(server: ServerEntry, tag: JQuery) { - let container: JQuery; - tag.empty().append( - container = $.spawn("div").addClass("container-hostbanner") - ).addClass("hidden"); - - const htag = Hostbanner.generate_tag(server.properties.virtualserver_hostbanner_gfx_url, server.properties.virtualserver_hostbanner_gfx_interval, server.properties.virtualserver_hostbanner_mode); - htag.then(t => { - if(!t) return; - - tag.removeClass("hidden"); - container.append(t); - }); + /* platform */ + { + const container = tag.find(".server-platform"); + container.text(server.properties.virtualserver_platform); } - function apply_category_1(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { - /* server name */ - { - const container = tag.find(".server-name"); - container.text(server.properties.virtualserver_name); - } - - /* server region */ - { - const container = tag.find(".server-region").empty(); - container.append( - $.spawn("div").addClass("country flag-" + server.properties.virtualserver_country_code.toLowerCase()), - $.spawn("a").text(i18n.country_name(server.properties.virtualserver_country_code, tr("Global"))) - ); - } - - /* slots */ - { - const container = tag.find(".server-slots"); - - let text = server.properties.virtualserver_clientsonline + "/" + server.properties.virtualserver_maxclients; - if(server.properties.virtualserver_queryclientsonline) - text += " +" + (server.properties.virtualserver_queryclientsonline > 1 ? - server.properties.virtualserver_queryclientsonline + " " + tr("Queries") : - server.properties.virtualserver_queryclientsonline + " " + tr("Query")); - if(server.properties.virtualserver_reserved_slots) - text += " (" + server.properties.virtualserver_reserved_slots + " " + tr("Reserved") + ")"; - - container.text(text); - } - - /* first run */ - { - const container = tag.find(".server-first-run"); - - container.text( - server.properties.virtualserver_created > 0 ? - moment(server.properties.virtualserver_created * 1000).format('MMMM Do YYYY, h:mm:ss a') : - tr("Unknown") - ); - } - - /* uptime */ - { - const container = tag.find(".server-uptime"); - const update = () => container.text(MessageHelper.format_time(server.calculateUptime() * 1000, tr("just started"))); - update_callbacks.push(update); - update(); - } - } - - function apply_category_2(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { - /* ip */ - { - const container = tag.find(".server-ip"); - container.text(server.remote_address.host + (server.remote_address.port == 9987 ? "" : (":" + server.remote_address.port))) - } - - /* version */ - { - const container = tag.find(".server-version"); - - let timestamp = -1; - const version = (server.properties.virtualserver_version || "unknwon").replace(/ ?\[build: ?([0-9]+)]/gmi, (group, ts) => { - timestamp = parseInt(ts); - return ""; - }); - - container.find("a").text(version); - container.find(".container-tooltip").toggle(timestamp > 0).find(".tooltip a").text( - moment(timestamp * 1000).format('[Build timestamp:] YYYY-MM-DD HH:mm Z') - ); - } - - /* platform */ - { - const container = tag.find(".server-platform"); - container.text(server.properties.virtualserver_platform); - } - - /* ping */ - { - const container = tag.find(".server-ping"); - container.text(tr("calculating...")); - update_callbacks.push((status, data) => { - if(status === RequestInfoStatus.SUCCESS) - container.text(data.connection_ping.toFixed(0) + " " + "ms"); - else if(status === RequestInfoStatus.NO_PERMISSION) - container.text(tr("No Permissions")); - else - container.text(tr("receiving...")); - }); - } - - /* packet loss */ - { - const container = tag.find(".server-packet-loss"); - container.text(tr("receiving...")); - update_callbacks.push((status, data) => { - if(status === RequestInfoStatus.SUCCESS) - container.text(data.connection_packetloss_total.toFixed(2) + "%"); - else if(status === RequestInfoStatus.NO_PERMISSION) - container.text(tr("No Permissions")); - else - container.text(tr("receiving...")); - }); - } - } - - function apply_category_3(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { - /* unique id */ - { - const container = tag.find(".server-unique-id"); - container.text(server.properties.virtualserver_unique_identifier || tr("Unknown")); - } - - /* voice encryption */ - { - const container = tag.find(".server-voice-encryption"); - if(server.properties.virtualserver_codec_encryption_mode == 0) - container.text(tr("Globally off")); - else if(server.properties.virtualserver_codec_encryption_mode == 1) - container.text(tr("Individually configured per channel")); + /* ping */ + { + const container = tag.find(".server-ping"); + container.text(tr("calculating...")); + update_callbacks.push((status, data) => { + if(status === RequestInfoStatus.SUCCESS) + container.text(data.connection_ping.toFixed(0) + " " + "ms"); + else if(status === RequestInfoStatus.NO_PERMISSION) + container.text(tr("No Permissions")); else - container.text(tr("Globally on")); - } + container.text(tr("receiving...")); + }); + } - /* channel count */ - { - const container = tag.find(".server-channel-count"); - container.text(server.properties.virtualserver_channelsonline); - } + /* packet loss */ + { + const container = tag.find(".server-packet-loss"); + container.text(tr("receiving...")); + update_callbacks.push((status, data) => { + if(status === RequestInfoStatus.SUCCESS) + container.text(data.connection_packetloss_total.toFixed(2) + "%"); + else if(status === RequestInfoStatus.NO_PERMISSION) + container.text(tr("No Permissions")); + else + container.text(tr("receiving...")); + }); + } +} - /* minimal security level */ - { - const container = tag.find(".server-min-security-level"); - container.text(server.properties.virtualserver_needed_identity_security_level); - } +function apply_category_3(server: ServerEntry, tag: JQuery, update_callbacks: ServerBandwidthInfoUpdateCallback[]) { + /* unique id */ + { + const container = tag.find(".server-unique-id"); + container.text(server.properties.virtualserver_unique_identifier || tr("Unknown")); + } - /* complains */ - { - const container = tag.find(".server-complains"); - container.text(server.properties.virtualserver_complain_autoban_count); - } + /* voice encryption */ + { + const container = tag.find(".server-voice-encryption"); + if(server.properties.virtualserver_codec_encryption_mode == 0) + container.text(tr("Globally off")); + else if(server.properties.virtualserver_codec_encryption_mode == 1) + container.text(tr("Individually configured per channel")); + else + container.text(tr("Globally on")); + } + + /* channel count */ + { + const container = tag.find(".server-channel-count"); + container.text(server.properties.virtualserver_channelsonline); + } + + /* minimal security level */ + { + const container = tag.find(".server-min-security-level"); + container.text(server.properties.virtualserver_needed_identity_security_level); + } + + /* complains */ + { + const container = tag.find(".server-complains"); + container.text(server.properties.virtualserver_complain_autoban_count); } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalServerInfoBandwidth.ts b/shared/js/ui/modal/ModalServerInfoBandwidth.ts index 8bb6fdc8..b8c25629 100644 --- a/shared/js/ui/modal/ModalServerInfoBandwidth.ts +++ b/shared/js/ui/modal/ModalServerInfoBandwidth.ts @@ -1,184 +1,189 @@ -namespace Modals { - export enum RequestInfoStatus { - SUCCESS, - UNKNOWN, - NO_PERMISSION +import {ServerConnectionInfo, ServerEntry} from "tc-shared/ui/server"; +import {createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import {Graph} from "tc-shared/ui/elements/NetGraph"; +import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import {network} from "tc-shared/ui/frames/chat"; + +export enum RequestInfoStatus { + SUCCESS, + UNKNOWN, + NO_PERMISSION +} +export type ServerBandwidthInfoUpdateCallback = (status: RequestInfoStatus, info?: ServerConnectionInfo) => any; +export function openServerInfoBandwidth(server: ServerEntry, update_callbacks?: ServerBandwidthInfoUpdateCallback[]) : Modal { + let modal: Modal; + let own_callbacks = !update_callbacks; + update_callbacks = update_callbacks || []; + + modal = createModal({ + header: tr("Server bandwidth data"), + body: () => { + const template = $("#tmpl_server_info_bandwidth").renderTag(); + + const children = template.children(); + initialize_current_bandwidth(modal, children.find(".statistic-bandwidth"), update_callbacks); + initialize_ft_bandwidth(modal, children.find(".statistic-ft-bandwidth"), update_callbacks); + initialize_general(template.find(".top"), update_callbacks); + + tooltip.initialize(template); + return template.children(); + }, + footer: null, + min_width: "25em" + }); + + if(own_callbacks) { + const updater = setInterval(() => { + server.request_connection_info().then(info => update_callbacks.forEach(e => e(RequestInfoStatus.SUCCESS, info))).catch(error => { + if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { + update_callbacks.forEach(e => e(RequestInfoStatus.NO_PERMISSION)); + return; + } + update_callbacks.forEach(e => e(RequestInfoStatus.UNKNOWN)); + }); + }, 1000); + modal.close_listener.push(() => clearInterval(updater)); } - export type ServerBandwidthInfoUpdateCallback = (status: RequestInfoStatus, info?: ServerConnectionInfo) => any; - export function openServerInfoBandwidth(server: ServerEntry, update_callbacks?: ServerBandwidthInfoUpdateCallback[]) : Modal { - let modal: Modal; - let own_callbacks = !update_callbacks; - update_callbacks = update_callbacks || []; - modal = createModal({ - header: tr("Server bandwidth data"), - body: () => { - const template = $("#tmpl_server_info_bandwidth").renderTag(); - const children = template.children(); - initialize_current_bandwidth(modal, children.find(".statistic-bandwidth"), update_callbacks); - initialize_ft_bandwidth(modal, children.find(".statistic-ft-bandwidth"), update_callbacks); - initialize_general(template.find(".top"), update_callbacks); + modal.htmlTag.find(".button-close").on('click', event => modal.close()); + modal.htmlTag.find(".modal-body").addClass("modal-server-info-bandwidth"); + modal.open(); + return modal; +} - tooltip(template); - return template.children(); - }, - footer: null, - min_width: "25em" - }); +function initialize_graph(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[], fields: {uplaod: string, download: string}) { + const canvas = tag.find("canvas")[0] as HTMLCanvasElement; + const label_upload = tag.find(".upload"); + const label_download = tag.find(".download"); + let last_info: { status: RequestInfoStatus, info: ServerConnectionInfo }; + let custom_info = false; - if(own_callbacks) { - const updater = setInterval(() => { - server.request_connection_info().then(info => update_callbacks.forEach(e => e(RequestInfoStatus.SUCCESS, info))).catch(error => { - if(error instanceof CommandResult && error.id == ErrorID.PERMISSION_ERROR) { - update_callbacks.forEach(e => e(RequestInfoStatus.NO_PERMISSION)); - return; - } - update_callbacks.forEach(e => e(RequestInfoStatus.UNKNOWN)); - }); - }, 1000); - modal.close_listener.push(() => clearInterval(updater)); + const show_info = (upload: number | undefined, download: number | undefined) => { + let fallback_text = last_info && last_info.status === RequestInfoStatus.NO_PERMISSION ? tr("No permission") : tr("receiving..."); + + if(typeof upload !== "number") + upload = last_info ? last_info[fields.uplaod] : undefined; + + if(typeof download !== "number") + download = last_info ? last_info[fields.download] : undefined; + + if(typeof upload !== "number") + label_upload.text(fallback_text); + else + label_upload.text(network.format_bytes(upload, {unit: "Bytes", time: "s", exact: false})); + + if(typeof download !== "number") + label_download.text(fallback_text); + else + label_download.text(network.format_bytes(download, {unit: "Bytes", time: "s", exact: false})); + }; + show_info(undefined, undefined); + + const graph = new Graph(canvas); + graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined}); + callbacks.push((status, values) => { + last_info = {status: status, info: values}; + + if(!values) { + graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined}); + } else { + graph.insert_entry({ + timestamp: Date.now(), + download: values[fields.download], //values.connection_bandwidth_received_last_second_total, + upload: values[fields.uplaod], //values.connection_bandwidth_sent_last_second_total + }); } - - modal.htmlTag.find(".button-close").on('click', event => modal.close()); - modal.htmlTag.find(".modal-body").addClass("modal-server-info-bandwidth"); - modal.open(); - return modal; - } - - function initialize_graph(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[], fields: {uplaod: string, download: string}) { - const canvas = tag.find("canvas")[0] as HTMLCanvasElement; - const label_upload = tag.find(".upload"); - const label_download = tag.find(".download"); - let last_info: { status: RequestInfoStatus, info: ServerConnectionInfo }; - let custom_info = false; - - const show_info = (upload: number | undefined, download: number | undefined) => { - let fallback_text = last_info && last_info.status === RequestInfoStatus.NO_PERMISSION ? tr("No permission") : tr("receiving..."); - - if(typeof upload !== "number") - upload = last_info ? last_info[fields.uplaod] : undefined; - - if(typeof download !== "number") - download = last_info ? last_info[fields.download] : undefined; - - if(typeof upload !== "number") - label_upload.text(fallback_text); - else - label_upload.text(MessageHelper.network.format_bytes(upload, {unit: "Bytes", time: "s", exact: false})); - - if(typeof download !== "number") - label_download.text(fallback_text); - else - label_download.text(MessageHelper.network.format_bytes(download, {unit: "Bytes", time: "s", exact: false})); + /* set set that we want to show the entry within one second */ + graph._time_span.origin = Object.assign(graph.calculate_time_span(), { time: Date.now() }); + graph._time_span.target = { + begin: Date.now() - 120 * 1000, + end: Date.now(), + time: Date.now() + 200 }; - show_info(undefined, undefined); - const graph = new net.graph.Graph(canvas); - graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined}); - callbacks.push((status, values) => { - last_info = {status: status, info: values}; - - if(!values) { - graph.insert_entry({ timestamp: Date.now(), upload: undefined, download: undefined}); - } else { - graph.insert_entry({ - timestamp: Date.now(), - download: values[fields.download], //values.connection_bandwidth_received_last_second_total, - upload: values[fields.uplaod], //values.connection_bandwidth_sent_last_second_total - }); - } - - /* set set that we want to show the entry within one second */ - graph._time_span.origin = Object.assign(graph.calculate_time_span(), { time: Date.now() }); - graph._time_span.target = { - begin: Date.now() - 120 * 1000, - end: Date.now(), - time: Date.now() + 200 - }; - - graph.cleanup(); - if(!custom_info) { - show_info(undefined, undefined); - graph.resize(); /* just to ensure (we have to rethink this maybe; cause it causes a recalculates the style */ - } - }); - - graph.max_gap_size(0); - graph.initialize(); - - graph.callback_detailed_hide = () => { - custom_info = false; + graph.cleanup(); + if(!custom_info) { show_info(undefined, undefined); - }; + graph.resize(); /* just to ensure (we have to rethink this maybe; cause it causes a recalculates the style */ + } + }); - graph.callback_detailed_info = (upload, download, timestamp, event) => { - custom_info = true; - show_info(upload, download); - }; + graph.max_gap_size(0); + graph.initialize(); - modal.close_listener.push(() => graph.terminate()); - modal.open_listener.push(() => graph.resize()); + graph.callback_detailed_hide = () => { + custom_info = false; + show_info(undefined, undefined); + }; - tag.addClass("window-resize-listener").on('resize', event => graph.resize()); - } + graph.callback_detailed_info = (upload, download, timestamp, event) => { + custom_info = true; + show_info(upload, download); + }; - function initialize_current_bandwidth(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { - initialize_graph(modal, tag, callbacks, { - uplaod: "connection_bandwidth_sent_last_second_total", - download: "connection_bandwidth_received_last_second_total" - }); - } + modal.close_listener.push(() => graph.terminate()); + modal.open_listener.push(() => graph.resize()); - function initialize_ft_bandwidth(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { - initialize_graph(modal, tag, callbacks, { - uplaod: "connection_filetransfer_bandwidth_sent", - download: "connection_filetransfer_bandwidth_received" - }); - } + tag.addClass("window-resize-listener").on('resize', event => graph.resize()); +} - function initialize_general(tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { - const tag_packets_upload = tag.find(".statistic-packets .upload"); - const tag_packets_download = tag.find(".statistic-packets .download"); +function initialize_current_bandwidth(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { + initialize_graph(modal, tag, callbacks, { + uplaod: "connection_bandwidth_sent_last_second_total", + download: "connection_bandwidth_received_last_second_total" + }); +} - const tag_bytes_upload = tag.find(".statistic-bytes .upload"); - const tag_bytes_download = tag.find(".statistic-bytes .download"); +function initialize_ft_bandwidth(modal: Modal, tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { + initialize_graph(modal, tag, callbacks, { + uplaod: "connection_filetransfer_bandwidth_sent", + download: "connection_filetransfer_bandwidth_received" + }); +} - const tag_ft_bytes_upload = tag.find(".statistic-ft-bytes .upload"); - const tag_ft_bytes_download = tag.find(".statistic-ft-bytes .download"); +function initialize_general(tag: JQuery, callbacks: ServerBandwidthInfoUpdateCallback[]) { + const tag_packets_upload = tag.find(".statistic-packets .upload"); + const tag_packets_download = tag.find(".statistic-packets .download"); - const update = (tag, value: undefined | null | number) => { - if(typeof value === "undefined") - tag.text(tr("receiving...")); - else if(value === null) - tag.text(tr("no permissions")); - else - tag.text(MessageHelper.network.format_bytes(value, {unit: "Bytes", exact: false})); - }; + const tag_bytes_upload = tag.find(".statistic-bytes .upload"); + const tag_bytes_download = tag.find(".statistic-bytes .download"); - const props = [ - {tag: tag_packets_download, property: "connection_packets_received_total"}, - {tag: tag_packets_upload, property: "connection_packets_sent_total"}, + const tag_ft_bytes_upload = tag.find(".statistic-ft-bytes .upload"); + const tag_ft_bytes_download = tag.find(".statistic-ft-bytes .download"); - {tag: tag_bytes_download, property: "connection_bytes_received_total"}, - {tag: tag_bytes_upload, property: "connection_bytes_sent_total"}, + const update = (tag, value: undefined | null | number) => { + if(typeof value === "undefined") + tag.text(tr("receiving...")); + else if(value === null) + tag.text(tr("no permissions")); + else + tag.text(network.format_bytes(value, {unit: "Bytes", exact: false})); + }; - {tag: tag_ft_bytes_upload, property: "connection_filetransfer_bytes_received_total"}, - {tag: tag_ft_bytes_download, property: "connection_filetransfer_bytes_sent_total"}, - ]; + const props = [ + {tag: tag_packets_download, property: "connection_packets_received_total"}, + {tag: tag_packets_upload, property: "connection_packets_sent_total"}, - callbacks.push((status, info) => { - if(status === RequestInfoStatus.SUCCESS) { - for(const entry of props) - update(entry.tag, info[entry.property]); - } else if(status === RequestInfoStatus.NO_PERMISSION) { - for(const entry of props) - update(entry.tag, null); - } else { - for(const entry of props) - update(entry.tag, undefined); - } - }); - } + {tag: tag_bytes_download, property: "connection_bytes_received_total"}, + {tag: tag_bytes_upload, property: "connection_bytes_sent_total"}, + + {tag: tag_ft_bytes_upload, property: "connection_filetransfer_bytes_received_total"}, + {tag: tag_ft_bytes_download, property: "connection_filetransfer_bytes_sent_total"}, + ]; + + callbacks.push((status, info) => { + if(status === RequestInfoStatus.SUCCESS) { + for(const entry of props) + update(entry.tag, info[entry.property]); + } else if(status === RequestInfoStatus.NO_PERMISSION) { + for(const entry of props) + update(entry.tag, null); + } else { + for(const entry of props) + update(entry.tag, undefined); + } + }); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts index 155e3076..6e3c6c58 100644 --- a/shared/js/ui/modal/ModalSettings.ts +++ b/shared/js/ui/modal/ModalSettings.ts @@ -1,442 +1,1882 @@ -namespace Modals { - export function spawnSettingsModal(default_page?: string) : Modal { - let modal: Modal; - modal = createModal({ - header: tr("Settings"), - body: () => { - const tag = $("#tmpl_settings").renderTag().dividerfy(); +import {createErrorModal, createInfoModal, createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {sliderfy} from "tc-shared/ui/elements/Slider"; +import {settings, Settings} from "tc-shared/settings"; +import {manager, set_master_volume, Sound} from "tc-shared/sound/Sounds"; +import {ConnectionProfile} from "tc-shared/profiles/ConnectionProfile"; +import {IdentitifyType} from "tc-shared/profiles/Identity"; +import {TeaForumIdentity} from "tc-shared/profiles/identities/TeaForumIdentity"; +import {TeaSpeakIdentity} from "tc-shared/profiles/identities/TeamSpeakIdentity"; +import {NameIdentity} from "tc-shared/profiles/identities/NameIdentity"; +import * as log from "tc-shared/log"; +import {LogCategory} from "tc-shared/log"; +import * as profiles from "tc-shared/profiles/ConnectionProfile"; +import {RepositoryTranslation, TranslationRepository} from "tc-shared/i18n/localize"; +import {Registry} from "tc-shared/events"; +import {key_description} from "tc-shared/PPTListener"; +import {default_recorder} from "tc-shared/voice/RecorderProfile"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import * as i18n from "tc-shared/i18n/localize"; +import * as i18nc from "tc-shared/i18n/country"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import * as events from "tc-shared/events"; +import * as sound from "tc-shared/sound/Sounds"; +import * as forum from "tc-shared/profiles/identities/teaspeak-forum"; +import {formatMessage, set_icon_size} from "tc-shared/ui/frames/chat"; +import {spawnKeySelect} from "tc-shared/ui/modal/ModalKeySelect"; +import {spawnTeamSpeakIdentityImport, spawnTeamSpeakIdentityImprove} from "tc-shared/ui/modal/ModalIdentity"; +import {Device} from "tc-shared/audio/player"; +import {LevelMeter} from "tc-shared/voice/RecorderBase"; +import * as loader from "tc-loader"; +import * as aplayer from "tc-backend/audio/player"; +import * as arecorder from "tc-backend/audio/recorder"; +import * as ppt from "tc-backend/ppt"; - /* general "tab" mechanic */ - const left = tag.find("> .left"); - const right = tag.find("> .right"); - { - left.find(".entry:not(.group)").on('click', event => { - const entry = $(event.target); - right.find("> .container").addClass("hidden"); - left.find(".selected").removeClass("selected"); +export function spawnSettingsModal(default_page?: string) : Modal { + let modal: Modal; + modal = createModal({ + header: tr("Settings"), + body: () => { + const tag = $("#tmpl_settings").renderTag().dividerfy(); - const target = entry.attr("container"); - if(!target) return; - - right.find("> .container." + target).removeClass("hidden"); - entry.addClass("selected"); - }) - } - - /* initialize all tabs */ - - /* enable one tab */ - { - left.find(".entry[container" + (default_page ? ("='" + default_page + "'") : "") + "]").first().trigger('click'); - } - - return tag; - }, - footer: null - }); - modal.htmlTag.find(".modal-body").addClass("modal-settings"); - - settings_general_application(modal.htmlTag.find(".right .container.general-application"), modal); - settings_general_language(modal.htmlTag.find(".right .container.general-language"), modal); - settings_general_chat(modal.htmlTag.find(".right .container.general-chat"), modal); - settings_audio_microphone(modal.htmlTag.find(".right .container.audio-microphone"), modal); - settings_audio_speaker(modal.htmlTag.find(".right .container.audio-speaker"), modal); - settings_audio_sounds(modal.htmlTag.find(".right .container.audio-sounds"), modal); - const update_profiles = settings_identity_profiles(modal.htmlTag.find(".right .container.identity-profiles"), modal); - settings_identity_forum(modal.htmlTag.find(".right .container.identity-forum"), modal, update_profiles as any); - - modal.close_listener.push(() => { - if(profiles.requires_save()) - profiles.save(); - }); - - modal.open(); - return modal; - } - - function settings_general_application(container: JQuery, modal: Modal) { - /* hostbanner */ - { - const option = container.find(".option-hostbanner-background") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); - for(const sc of server_connections.server_connection_handlers()) - sc.hostbanner.update(); - }).prop("checked", settings.static_global(Settings.KEY_HOSTBANNER_BACKGROUND)); - } - - /* font size */ - { - const current_size = parseInt(getComputedStyle(document.body).fontSize); //settings.static_global(Settings.KEY_FONT_SIZE, 12); - const select = container.find(".option-font-size"); - - if(select.find("option[value='" + current_size + "']").length) - select.find("option[value='" + current_size + "']").prop("selected", true); - else - select.find("option[value='-1']").prop("selected", true); - - select.on('change', event => { - const value = parseInt(select.val() as string); - settings.changeGlobal(Settings.KEY_FONT_SIZE, value); - console.log("Changed font size to %dpx", value); - - $(document.body).css("font-size", value + "px"); - }); - } - - /* all permissions */ - { - const option = container.find(".option-all-permissions") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); - }).prop("checked", settings.global(Settings.KEY_PERMISSIONS_SHOW_ALL)); - } - } - - function settings_general_language(container: JQuery, modal: Modal) { - - const container_entries = container.find(".container-list .entries"); - - const tag_loading = container.find(".cover-loading"); - const template = $("#settings-translations-list-entry"); - - const restart_hint = container.find(".restart-note").hide(); - - const display_repository_info = (repository: i18n.TranslationRepository) => { - const info_modal = createModal({ - header: tr("Repository info"), - body: () => { - return $("#settings-translations-list-entry-info").renderTag({ - type: "repository", - name: repository.name, - url: repository.url, - contact: repository.contact, - translations: repository.translations || [] - }); - }, - footer: () => { - let footer = $.spawn("div"); - footer.addClass("modal-button-group"); - footer.css("margin-top", "5px"); - footer.css("margin-bottom", "5px"); - footer.css("text-align", "right"); - - let buttonOk = $.spawn("button"); - buttonOk.text(tr("Close")); - buttonOk.click(() => info_modal.close()); - footer.append(buttonOk); - - return footer; - } - }); - info_modal.open() - }; - const display_translation_info = (translation: i18n.RepositoryTranslation, repository: i18n.TranslationRepository) => { - const info_modal = createModal({ - header: tr("Translation info"), - body: () => { - const tag = $("#settings-translations-list-entry-info").renderTag({ - type: "translation", - name: translation.name, - url: translation.path, - repository_name: repository.name, - contributors: translation.contributors || [] - }); - - tag.find(".button-info").on('click', () => display_repository_info(repository)); - - return tag; - }, - footer: () => { - let footer = $.spawn("div"); - footer.addClass("modal-button-group"); - footer.css("margin-top", "5px"); - footer.css("margin-bottom", "5px"); - footer.css("text-align", "right"); - - let buttonOk = $.spawn("button"); - buttonOk.text(tr("Close")); - buttonOk.click(() => info_modal.close()); - footer.append(buttonOk); - - return footer; - } - }); - info_modal.open() - }; - - const update_current_selected = () => { - const container_current = container.find(".selected-language6"); - container_current.empty().text(tr("Loading")); - - let current_translation: i18n.RepositoryTranslation; - i18n.iterate_repositories(repository => { - if(current_translation) return; - for(const entry of repository.translations) - if(i18n.config.translation_config().current_translation_path == entry.path) { - current_translation = entry; - return; - } - }).then(() => { - container_current.empty(); - - const language = current_translation ? current_translation.country_code : "gb"; - $.spawn("div").addClass("country flag-" + language.toLowerCase()).attr('title', i18n.country_name(language, tr("Unknown language"))).appendTo(container_current); - $.spawn("a").text(current_translation ? current_translation.name : tr("English (Default)")).appendTo(container_current); - }).catch(error => { - /* This shall never happen */ - }); - }; - - const initially_selected = i18n.config.translation_config().current_translation_url; - const update_list = () => { - container_entries.empty(); - - const currently_selected = i18n.config.translation_config().current_translation_url; - //Default translation + /* general "tab" mechanic */ + const left = tag.find("> .left"); + const right = tag.find("> .right"); { - const tag = template.renderTag({ - type: "default", - selected: !currently_selected || currently_selected == "default" - }); - tag.on('click', () => { - i18n.select_translation(undefined, undefined); - container_entries.find(".selected").removeClass("selected"); - tag.addClass("selected"); + left.find(".entry:not(.group)").on('click', event => { + const entry = $(event.target); + right.find("> .container").addClass("hidden"); + left.find(".selected").removeClass("selected"); - update_current_selected(); - restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); - }); - tag.appendTo(container_entries); - } + const target = entry.attr("container"); + if(!target) return; - { - tag_loading.show(); - i18n.iterate_repositories(repo => { - let repo_tag = container_entries.find("[repository=\"" + repo.unique_id + "\"]"); - if (repo_tag.length == 0) { - repo_tag = template.renderTag({ - type: "repository", - name: repo.name || repo.url, - id: repo.unique_id - }); - - repo_tag.find(".button-delete").on('click', e => { - e.preventDefault(); - - Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this repository?"), answer => { - if (answer) { - i18n.delete_repository(repo); - update_list(); - } - }); - }); - repo_tag.find(".button-info").on('click', e => { - e.preventDefault(); - display_repository_info(repo); - }); - - container_entries.append(repo_tag); - } - - for(const translation of repo.translations) { - const tag = template.renderTag({ - type: "translation", - name: translation.name || translation.path, - id: repo.unique_id, - country_code: translation.country_code, - selected: i18n.config.translation_config().current_translation_path == translation.path - }); - tag.find(".button-info").on('click', e => { - e.preventDefault(); - display_translation_info(translation, repo); - }); - tag.on('click', e => { - if (e.isDefaultPrevented()) return; - i18n.select_translation(repo, translation); - container_entries.find(".selected").removeClass("selected"); - tag.addClass("selected"); - - update_current_selected(); - restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); - }); - tag.insertAfter(repo_tag); - } - }).then(() => tag_loading.hide()).catch(error => { - console.error(error); - /* this should NEVER happen */ + right.find("> .container." + target).removeClass("hidden"); + entry.addClass("selected"); }) } - }; + /* initialize all tabs */ - /* button add repository */ - { - container.find(".button-add-repository").on('click', () => { - createInputModal(tr("Enter repository URL"), tr("Enter repository URL:"), text => { - try { - new URL(text); - return true; - } catch(error) { - return false; - } - }, url => { - if (!url) return; + /* enable one tab */ + { + left.find(".entry[container" + (default_page ? ("='" + default_page + "'") : "") + "]").first().trigger('click'); + } - tag_loading.show(); - i18n.load_repository(url as string).then(repository => { - i18n.register_repository(repository); - update_list(); - }).catch(error => { - tag_loading.hide(); - createErrorModal("Failed to load repository", tr("Failed to query repository.
Ensure that this repository is valid and reachable.
Error: ") + error).open(); - }) - }).open(); - }); - } + return tag; + }, + footer: null + }); + modal.htmlTag.find(".modal-body").addClass("modal-settings"); - container.find(".button-restart").on('click', () => { - if(app.is_web()) { - location.reload(); - } else { - createErrorModal(tr("Not implemented"), tr("Client restart isn't implemented.
Please do it manually!")).open(); + settings_general_application(modal.htmlTag.find(".right .container.general-application"), modal); + settings_general_language(modal.htmlTag.find(".right .container.general-language"), modal); + settings_general_chat(modal.htmlTag.find(".right .container.general-chat"), modal); + settings_audio_microphone(modal.htmlTag.find(".right .container.audio-microphone"), modal); + settings_audio_speaker(modal.htmlTag.find(".right .container.audio-speaker"), modal); + settings_audio_sounds(modal.htmlTag.find(".right .container.audio-sounds"), modal); + const update_profiles = settings_identity_profiles(modal.htmlTag.find(".right .container.identity-profiles"), modal); + settings_identity_forum(modal.htmlTag.find(".right .container.identity-forum"), modal, update_profiles as any); + + modal.close_listener.push(() => { + if(profiles.requires_save()) + profiles.save(); + }); + + modal.open(); + return modal; +} + +function settings_general_application(container: JQuery, modal: Modal) { + /* hostbanner */ + { + const option = container.find(".option-hostbanner-background") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); + for(const sc of server_connections.server_connection_handlers()) + sc.hostbanner.update(); + }).prop("checked", settings.static_global(Settings.KEY_HOSTBANNER_BACKGROUND)); + } + + /* font size */ + { + const current_size = parseInt(getComputedStyle(document.body).fontSize); //settings.static_global(Settings.KEY_FONT_SIZE, 12); + const select = container.find(".option-font-size"); + + if(select.find("option[value='" + current_size + "']").length) + select.find("option[value='" + current_size + "']").prop("selected", true); + else + select.find("option[value='-1']").prop("selected", true); + + select.on('change', event => { + const value = parseInt(select.val() as string); + settings.changeGlobal(Settings.KEY_FONT_SIZE, value); + console.log("Changed font size to %dpx", value); + + $(document.body).css("font-size", value + "px"); + }); + } + + /* all permissions */ + { + const option = container.find(".option-all-permissions") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_HOSTBANNER_BACKGROUND, option[0].checked); + }).prop("checked", settings.global(Settings.KEY_PERMISSIONS_SHOW_ALL)); + } +} + +function settings_general_language(container: JQuery, modal: Modal) { + + const container_entries = container.find(".container-list .entries"); + + const tag_loading = container.find(".cover-loading"); + const template = $("#settings-translations-list-entry"); + + const restart_hint = container.find(".restart-note").hide(); + + const display_repository_info = (repository: TranslationRepository) => { + const info_modal = createModal({ + header: tr("Repository info"), + body: () => { + return $("#settings-translations-list-entry-info").renderTag({ + type: "repository", + name: repository.name, + url: repository.url, + contact: repository.contact, + translations: repository.translations || [], + }); + }, + footer: () => { + let footer = $.spawn("div"); + footer.addClass("modal-button-group"); + footer.css("margin-top", "5px"); + footer.css("margin-bottom", "5px"); + footer.css("text-align", "right"); + + let buttonOk = $.spawn("button"); + buttonOk.text(tr("Close")); + buttonOk.click(() => info_modal.close()); + footer.append(buttonOk); + + return footer; } }); + info_modal.open() + }; + const display_translation_info = (translation: RepositoryTranslation, repository: TranslationRepository) => { + const info_modal = createModal({ + header: tr("Translation info"), + body: () => { + const tag = $("#settings-translations-list-entry-info").renderTag({ + type: "translation", + name: translation.name, + url: translation.path, + repository_name: repository.name, + contributors: translation.contributors || [] + }); - update_list(); - update_current_selected(); - } + tag.find(".button-info").on('click', () => display_repository_info(repository)); - function settings_general_chat(container: JQuery, modal: Modal) { - /* timestamp format */ - { - const option_fixed = container.find(".option-fixed-timestamps") as JQuery; - const option_colloquial = container.find(".option-colloquial-timestamps") as JQuery; + return tag; + }, + footer: () => { + let footer = $.spawn("div"); + footer.addClass("modal-button-group"); + footer.css("margin-top", "5px"); + footer.css("margin-bottom", "5px"); + footer.css("text-align", "right"); - option_colloquial.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS, option_colloquial[0].checked); - }); + let buttonOk = $.spawn("button"); + buttonOk.text(tr("Close")); + buttonOk.click(() => info_modal.close()); + footer.append(buttonOk); - option_fixed.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_FIXED_TIMESTAMPS, option_fixed[0].checked); - option_colloquial - .prop("disabled", option_fixed[0].checked) - .parents("label").toggleClass("disabled", option_fixed[0].checked); - if(option_fixed[0].checked) { - option_colloquial.prop("checked", false); - } else { - option_colloquial.prop("checked", settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)); + return footer; + } + }); + info_modal.open() + }; + + const update_current_selected = () => { + const container_current = container.find(".selected-language6"); + container_current.empty().text(tr("Loading")); + + let current_translation: RepositoryTranslation; + i18n.iterate_repositories(repository => { + if(current_translation) return; + for(const entry of repository.translations) + if(i18n.config.translation_config().current_translation_path == entry.path) { + current_translation = entry; + return; } - }).prop("checked", settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)).trigger('change'); - } + }).then(() => { + container_current.empty(); - { - const option = container.find(".option-instant-channel-switch") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CHAT, option[0].checked); - }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)); - } - { - const option = container.find(".option-instant-client-switch") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CLIENT, option[0].checked); - }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)); - } - { - const option = container.find(".option-colored-emojies") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_COLORED_EMOJIES, option[0].checked); - }).prop("checked", settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)); - } - - const update_format_helper = () => server_connections.server_connection_handlers().map(e => e.side_bar).forEach(e => { - e.private_conversations().update_input_format_helper(); - e.channel_conversations().update_input_format_helper(); + const language = current_translation ? current_translation.country_code : "gb"; + $.spawn("div").addClass("country flag-" + language.toLowerCase()).attr('title', i18nc.country_name(language, tr("Unknown language"))).appendTo(container_current); + $.spawn("a").text(current_translation ? current_translation.name : tr("English (Default)")).appendTo(container_current); + }).catch(error => { + /* This shall never happen */ }); - { - const option = container.find(".option-support-markdown") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_ENABLE_MARKDOWN, option[0].checked); - update_format_helper(); - }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)); - } - { - const option = container.find(".option-support-bbcode") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_ENABLE_BBCODE, option[0].checked); - update_format_helper(); - }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE)); - } - { - const option = container.find(".option-url-tagging") as JQuery; - option.on('change', event => { - settings.changeGlobal(Settings.KEY_CHAT_TAG_URLS, option[0].checked); - }).prop("checked", settings.static_global(Settings.KEY_CHAT_TAG_URLS)); - } - /* Icon size */ - { - const container_slider = container.find(".container-icon-size .container-slider"); - const container_value = container.find(".container-icon-size .value"); + }; - sliderfy(container_slider, { - unit: '%', - min_value: 25, - max_value: 300, - step: 5, - initial_value: settings.static_global(Settings.KEY_ICON_SIZE), - value_field: container_value + const initially_selected = i18n.config.translation_config().current_translation_url; + const update_list = () => { + container_entries.empty(); + + const currently_selected = i18n.config.translation_config().current_translation_url; + //Default translation + { + const tag = template.renderTag({ + type: "default", + selected: !currently_selected || currently_selected == "default", + fallback_country_name: i18nc.country_name('gb'), }); + tag.on('click', () => { + i18n.select_translation(undefined, undefined); + container_entries.find(".selected").removeClass("selected"); + tag.addClass("selected"); - container_slider.on('change', event => { - const value = parseInt(container_slider.attr("value") as string); - settings.changeGlobal(Settings.KEY_ICON_SIZE, value); - console.log("Changed icon size to %sem", (value / 100).toFixed(2)); + update_current_selected(); + restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); + }); + tag.appendTo(container_entries); + } - MessageHelper.set_icon_size((value / 100).toFixed(2) + "em"); + { + tag_loading.show(); + i18n.iterate_repositories(repo => { + let repo_tag = container_entries.find("[repository=\"" + repo.unique_id + "\"]"); + if (repo_tag.length == 0) { + repo_tag = template.renderTag({ + type: "repository", + name: repo.name || repo.url, + id: repo.unique_id + }); + + repo_tag.find(".button-delete").on('click', e => { + e.preventDefault(); + + spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this repository?"), answer => { + if (answer) { + i18n.delete_repository(repo); + update_list(); + } + }); + }); + repo_tag.find(".button-info").on('click', e => { + e.preventDefault(); + display_repository_info(repo); + }); + + container_entries.append(repo_tag); + } + + for(const translation of repo.translations) { + const tag = template.renderTag({ + type: "translation", + name: translation.name || translation.path, + id: repo.unique_id, + country_code: translation.country_code, + selected: i18n.config.translation_config().current_translation_path == translation.path, + fallback_country_name: i18nc.country_name('gb'), + country_name: i18nc.country_name((translation.country_code || "XX").toLowerCase()), + }); + tag.find(".button-info").on('click', e => { + e.preventDefault(); + display_translation_info(translation, repo); + }); + tag.on('click', e => { + if (e.isDefaultPrevented()) return; + i18n.select_translation(repo, translation); + container_entries.find(".selected").removeClass("selected"); + tag.addClass("selected"); + + update_current_selected(); + restart_hint.toggle(initially_selected !== i18n.config.translation_config().current_translation_url); + }); + tag.insertAfter(repo_tag); + } + }).then(() => tag_loading.hide()).catch(error => { + console.error(error); + /* this should NEVER happen */ + }) + } + + }; + + /* button add repository */ + { + container.find(".button-add-repository").on('click', () => { + createInputModal(tr("Enter repository URL"), tr("Enter repository URL:"), text => { + try { + new URL(text); + return true; + } catch(error) { + return false; + } + }, url => { + if (!url) return; + + tag_loading.show(); + i18n.load_repository(url as string).then(repository => { + i18n.register_repository(repository); + update_list(); + }).catch(error => { + tag_loading.hide(); + createErrorModal("Failed to load repository", tr("Failed to query repository.
Ensure that this repository is valid and reachable.
Error: ") + error).open(); + }) + }).open(); + }); + } + + container.find(".button-restart").on('click', () => { + if(loader.version().type === "web") { + location.reload(); + } else { + createErrorModal(tr("Not implemented"), tr("Client restart isn't implemented.
Please do it manually!")).open(); + } + }); + + update_list(); + update_current_selected(); +} + +function settings_general_chat(container: JQuery, modal: Modal) { + /* timestamp format */ + { + const option_fixed = container.find(".option-fixed-timestamps") as JQuery; + const option_colloquial = container.find(".option-colloquial-timestamps") as JQuery; + + option_colloquial.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS, option_colloquial[0].checked); + }); + + option_fixed.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_FIXED_TIMESTAMPS, option_fixed[0].checked); + option_colloquial + .prop("disabled", option_fixed[0].checked) + .parents("label").toggleClass("disabled", option_fixed[0].checked); + if(option_fixed[0].checked) { + option_colloquial.prop("checked", false); + } else { + option_colloquial.prop("checked", settings.static_global(Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS)); + } + }).prop("checked", settings.static_global(Settings.KEY_CHAT_FIXED_TIMESTAMPS)).trigger('change'); + } + + { + const option = container.find(".option-instant-channel-switch") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CHAT, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CHAT)); + } + { + const option = container.find(".option-instant-client-switch") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_SWITCH_INSTANT_CLIENT, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_SWITCH_INSTANT_CLIENT)); + } + { + const option = container.find(".option-colored-emojies") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_COLORED_EMOJIES, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_COLORED_EMOJIES)); + } + + const update_format_helper = () => server_connections.server_connection_handlers().map(e => e.side_bar).forEach(e => { + e.private_conversations().update_input_format_helper(); + e.channel_conversations().update_input_format_helper(); + }); + { + const option = container.find(".option-support-markdown") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_ENABLE_MARKDOWN, option[0].checked); + update_format_helper(); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_MARKDOWN)); + } + { + const option = container.find(".option-support-bbcode") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_ENABLE_BBCODE, option[0].checked); + update_format_helper(); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_ENABLE_BBCODE)); + } + { + const option = container.find(".option-url-tagging") as JQuery; + option.on('change', event => { + settings.changeGlobal(Settings.KEY_CHAT_TAG_URLS, option[0].checked); + }).prop("checked", settings.static_global(Settings.KEY_CHAT_TAG_URLS)); + } + /* Icon size */ + { + const container_slider = container.find(".container-icon-size .container-slider"); + const container_value = container.find(".container-icon-size .value"); + + sliderfy(container_slider, { + unit: '%', + min_value: 25, + max_value: 300, + step: 5, + initial_value: settings.static_global(Settings.KEY_ICON_SIZE), + value_field: container_value + }); + + container_slider.on('change', event => { + const value = parseInt(container_slider.attr("value") as string); + settings.changeGlobal(Settings.KEY_ICON_SIZE, value); + console.log("Changed icon size to %sem", (value / 100).toFixed(2)); + + set_icon_size((value / 100).toFixed(2) + "em"); + }); + } +} + +function settings_audio_microphone(container: JQuery, modal: Modal) { + const registry = new Registry(); + registry.enable_debug("settings-microphone"); + modal_settings.initialize_audio_microphone_controller(registry); + modal_settings.initialize_audio_microphone_view(container, registry); + + modal.close_listener.push(() => registry.fire_async("deinitialize")); + return; +} + +function settings_identity_profiles(container: JQuery, modal: Modal) { + const registry = new Registry(); + //registry.enable_debug("settings-identity"); + modal_settings.initialize_identity_profiles_controller(registry); + modal_settings.initialize_identity_profiles_view(container, registry, { + forum_setuppable: true + }); + + registry.on("setup-forum-connection", event => { + modal.htmlTag.find('.entry[container="identity-forum"]').trigger('click'); + }); + return () => registry.fire("reload-profile"); +} + +function settings_audio_speaker(container: JQuery, modal: Modal) { + /* devices */ + { + const container_devices = container.find(".left .container-devices"); + const contianer_error = container.find(".left .container-error"); + + const update_devices = () => { + container_devices.children().remove(); + + const current_selected = aplayer.current_device(); + const generate_device = (device: Device | undefined) => { + const selected = device === current_selected || (typeof(current_selected) !== "undefined" && typeof(device) !== "undefined" && current_selected.device_id == device.device_id); + + const tag = $.spawn("div").addClass("device").toggleClass("selected", selected).append( + $.spawn("div").addClass("container-selected").append( + $.spawn("div").addClass("icon_em client-apply") + ), + $.spawn("div").addClass("container-name").append( + $.spawn("div").addClass("device-driver").text( + device ? (device.driver || "Unknown driver") : "No device" + ), + $.spawn("div").addClass("device-name").text( + device ? (device.name || "Unknown name") : "No device" + ) + ) + ); + + tag.on('click', event => { + if(tag.hasClass("selected")) + return; + + const _old = container_devices.find(".selected"); + _old.removeClass("selected"); + tag.addClass("selected"); + + aplayer.set_device(device ? device.device_id : null).then(() => { + console.debug(tr("Changed default speaker device")); + }).catch((error) => { + _old.addClass("selected"); + tag.removeClass("selected"); + + console.error(tr("Failed to change speaker to device %o: %o"), device, error); + createErrorModal(tr("Failed to change speaker"), formatMessage(tr("Failed to change the speaker device to the target speaker{:br:}{}"), error)).open(); + }); + }); + + return tag; + }; + + generate_device(undefined).appendTo(container_devices); + aplayer.available_devices().then(result => { + contianer_error.text("").hide(); + result.forEach(e => generate_device(e).appendTo(container_devices)); + }).catch(error => { + if(typeof(error) === "string") + contianer_error.text(error).show(); + + console.log(tr("Failed to query available speaker devices: %o"), error); + contianer_error.text(tr("Errors occurred (View console)")).show(); + }); + }; + update_devices(); + + const button_update = container.find(".button-update"); + button_update.on('click', async event => { + button_update.prop("disabled", true); + try { + update_devices(); + } catch(error) { + console.error(tr("Failed to build new speaker device list: %o"), error); + } + button_update.prop("disabled", false); + }); + } + + /* slider */ + { + + { + const container_master = container.find(".container-volume-master"); + const slider = container_master.find(".container-slider"); + sliderfy(slider, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: settings.static_global(Settings.KEY_SOUND_MASTER, 100), + value_field: [container_master.find(".container-value")] + }); + slider.on('change', event => { + const volume = parseInt(slider.attr('value')); + + if(aplayer.set_master_volume) + aplayer.set_master_volume(volume / 100); + settings.changeGlobal(Settings.KEY_SOUND_MASTER, volume); + }); + } + + { + const container_soundpack = container.find(".container-volume-soundpack"); + const slider = container_soundpack.find(".container-slider"); + sliderfy(slider, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: settings.static_global(Settings.KEY_SOUND_MASTER_SOUNDS, 100), + value_field: [container_soundpack.find(".container-value")] + }); + slider.on('change', event => { + const volume = parseInt(slider.attr('value')); + set_master_volume(volume / 100); + settings.changeGlobal(Settings.KEY_SOUND_MASTER_SOUNDS, volume); }); } } - function settings_audio_microphone(container: JQuery, modal: Modal) { - const registry = new events.Registry(); - registry.enable_debug("settings-microphone"); - modal_settings.initialize_audio_microphone_controller(registry); - modal_settings.initialize_audio_microphone_view(container, registry); + /* button test sound */ + { + container.find(".button-test-sound").on('click', event => { + manager.play(Sound.SOUND_TEST, { + default_volume: 1, + ignore_muted: true, + ignore_overlap: true + }) + }); + } +} - modal.close_listener.push(() => registry.fire_async("deinitialize")); - return; +function settings_audio_sounds(contianer: JQuery, modal: Modal) { + /* initialize sound list */ + { + const container_sounds = contianer.find(".container-sounds"); + + const generate_sound = (_sound: Sound) => { + let tag_play_pause: JQuery, tag_play: JQuery, tag_pause: JQuery, tag_input_muted: JQuery; + let tag = $.spawn("div").addClass("sound").append( + tag_play_pause = $.spawn("div").addClass("container-button-play_pause").append( + tag_play = $.spawn("img").attr("src", "img/icon_sound_play.svg"), + tag_pause = $.spawn("img").attr("src", "img/icon_sound_pause.svg") + ), + $.spawn("div").addClass("container-name").text(_sound), + $.spawn("label").addClass("container-button-toggle").append( + $.spawn("div").addClass("switch").append( + tag_input_muted = $.spawn("input").attr("type", "checkbox"), + $.spawn("span").addClass("slider").append( + $.spawn("div").addClass("dot") + ) + ) + ) + ); + + tag_play_pause.on('click', event => { + if(tag_pause.is(":visible")) + return; + tag_play.hide(); + tag_pause.show(); + + const _done = flag => { + tag_pause.hide(); + tag_play.show(); + }; + const _timeout = setTimeout(() => _done(false), 10 * 1000); /* the sounds are not longer than 10 seconds */ + + sound.manager.play(_sound, { + ignore_overlap: true, + ignore_muted: true, + default_volume: 1, + + callback: flag => { + clearTimeout(_timeout); + _done(flag); + } + }); + }); + tag_pause.hide(); + + tag_input_muted.prop("checked", sound.get_sound_volume(_sound, 1) > 0); + tag_input_muted.on('change', event => { + const volume = tag_input_muted.prop("checked") ? 1 : 0; + sound.set_sound_volume(_sound, volume); + console.log(tr("Changed sound volume to %o for sound %o"), volume, _sound); + }); + + return tag; + }; + + //container-sounds + for(const sound_key in Sound) + generate_sound(Sound[sound_key as any] as any).appendTo(container_sounds); + + /* the filter */ + const input_filter = contianer.find(".input-sounds-filter"); + input_filter.on('change keyup', event => { + const filter = input_filter.val() as string; + + container_sounds.find(".sound").each((_, _element) => { + const element = $(_element); + element.toggle(filter.length == 0 || element.text().toLowerCase().indexOf(filter) !== -1); + }) + }); } - function settings_identity_profiles(container: JQuery, modal: Modal) { - const registry = new events.Registry(); - //registry.enable_debug("settings-identity"); - modal_settings.initialize_identity_profiles_controller(registry); - modal_settings.initialize_identity_profiles_view(container, registry, { - forum_setuppable: true - }); + const overlap_tag = contianer.find(".option-overlap-same"); + overlap_tag.on('change', event => { + const activated = (event.target).checked; + sound.set_overlap_activated(activated); + }).prop("checked", sound.overlap_activated()); - registry.on("setup-forum-connection", event => { - modal.htmlTag.find('.entry[container="identity-forum"]').trigger('click'); - }); - return () => registry.fire("reload-profile"); + const mute_tag = contianer.find(".option-mute-output"); + mute_tag.on('change', event => { + const activated = (event.target).checked; + sound.set_ignore_output_muted(!activated); + }).prop("checked", !sound.ignore_output_muted()); + + modal.close_listener.push(sound.save); +} + +export namespace modal_settings { + export interface ProfileViewSettings { + forum_setuppable: boolean } + export function initialize_identity_profiles_controller(event_registry: Registry) { + const send_error = (event, profile, text) => event_registry.fire_async(event, { status: "error", profile_id: profile, error: text }); + event_registry.on("create-profile", event => { + const profile = profiles.create_new_profile(event.name); + profiles.mark_need_save(); + event_registry.fire_async("create-profile-result", { + status: "success", + name: event.name, + profile_id: profile.id + }); + }); - function settings_audio_speaker(container: JQuery, modal: Modal) { - /* devices */ + event_registry.on("delete-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("delete-profile-result", event.profile_id, tr("Unknown profile")); + return; + } + + profiles.delete_profile(profile); + event_registry.fire_async("delete-profile-result", { status: "success", profile_id: event.profile_id }); + }); + + const build_profile_info = (profile: ConnectionProfile) => { + const forum_data = profile.selected_identity(IdentitifyType.TEAFORO) as TeaForumIdentity; + const teamspeak_data = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity; + const nickname_data = profile.selected_identity(IdentitifyType.NICKNAME) as NameIdentity; + + return { + id: profile.id, + name: profile.profile_name, + nickname: profile.default_username, + identity_type: profile.selected_identity_type as any, + identity_forum: !forum_data ? undefined : { + valid: forum_data.valid(), + fallback_name: forum_data.fallback_name() + }, + identity_nickname: !nickname_data ? undefined : { + name: nickname_data.name(), + fallback_name: nickname_data.fallback_name() + }, + identity_teamspeak: !teamspeak_data ? undefined : { + unique_id: teamspeak_data.uid(), + fallback_name: teamspeak_data.fallback_name() + } + } + }; + event_registry.on("query-profile-list", event => { + event_registry.fire_async("query-profile-list-result", { status: "success", profiles: profiles.profiles().map(e => build_profile_info(e)) }); + }); + + event_registry.on("query-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("query-profile-result", event.profile_id, tr("Unknown profile")); + return; + } + + event_registry.fire_async("query-profile-result", { status: "success", profile_id: event.profile_id, info: build_profile_info(profile)}); + }); + + event_registry.on("set-default-profile", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("set-default-profile-result", event.profile_id, tr("Unknown profile")); + return; + } + + const old = profiles.set_default_profile(profile); + event_registry.fire_async("set-default-profile-result", { status: "success", old_profile_id: event.profile_id, new_profile_id: old.id }); + }); + + event_registry.on("set-profile-name", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("set-profile-name-result", event.profile_id, tr("Unknown profile")); + return; + } + + profile.profile_name = event.name; + profiles.mark_need_save(); + event_registry.fire_async("set-profile-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + + event_registry.on("set-default-name", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("set-default-name-result", event.profile_id, tr("Unknown profile")); + return; + } + + profile.default_username = event.name; + profiles.mark_need_save(); + event_registry.fire_async("set-default-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + + event_registry.on("set-identity-name-name", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("set-identity-name-name-result", event.profile_id, tr("Unknown profile")); + return; + } + + let identity = profile.selected_identity(IdentitifyType.NICKNAME) as NameIdentity; + if(!identity) + profile.set_identity(IdentitifyType.NICKNAME, identity = new NameIdentity()); + identity.set_name(event.name); + profiles.mark_need_save(); + + event_registry.fire_async("set-identity-name-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); + }); + + event_registry.on("query-profile-validity", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("query-profile-validity-result", event.profile_id, tr("Unknown profile")); + return; + } + + event_registry.fire_async("query-profile-validity-result", { status: "success", profile_id: event.profile_id, valid: profile.valid() }); + }); + + event_registry.on("query-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("query-identity-teamspeak-result", event.profile_id, tr("Unknown profile")); + return; + } + + const ts = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity; + if(!ts) { + event_registry.fire_async("query-identity-teamspeak-result", { status: "error", profile_id: event.profile_id, error: tr("Missing identity") }); + return; + } + + ts.level().then(level => { + event_registry.fire_async("query-identity-teamspeak-result", { status: "success", level: level, profile_id: event.profile_id }); + }).catch(error => { + send_error("query-identity-teamspeak-result", event.profile_id, tr("failed to calculate level")); + }) + }); + + event_registry.on("select-identity-type", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + return; + } + + profile.selected_identity_type = event.identity_type; + profiles.mark_need_save(); + }); + + event_registry.on("generate-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + send_error("generate-identity-teamspeak-result", event.profile_id, tr("Unknown profile")); + return; + } + + TeaSpeakIdentity.generate_new().then(identity => { + profile.set_identity(IdentitifyType.TEAMSPEAK, identity); + profiles.mark_need_save(); + + identity.level().then(level => { + event_registry.fire_async("generate-identity-teamspeak-result", { + status: "success", + profile_id: event.profile_id, + unique_id: identity.uid(), + level: level + }); + }).catch(error => { + console.error(tr("Failed to calculate level for a new identity. Error object: %o"), error); + send_error("generate-identity-teamspeak-result", event.profile_id, tr("failed to calculate level: ") + error); + }) + }).catch(error => { + console.error(tr("Failed to generate a new identity. Error object: %o"), error); + send_error("generate-identity-teamspeak-result", event.profile_id, tr("failed to generate identity: ") + error); + }); + }); + + event_registry.on("import-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + return; + } + + spawnTeamSpeakIdentityImport(identity => { + profile.set_identity(IdentitifyType.TEAMSPEAK, identity); + profiles.mark_need_save(); + + identity.level().catch(error => { + console.error(tr("Failed to calculate level for a new imported identity. Error object: %o"), error); + return Promise.resolve(undefined); + }).then(level => { + event_registry.fire_async("import-identity-teamspeak-result", { + profile_id: event.profile_id, + unique_id: identity.uid(), + level: level + }); + }); + }); + }); + + event_registry.on("improve-identity-teamspeak-level", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + return; + } + + const identity = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity; + if (!identity) return; + + spawnTeamSpeakIdentityImprove(identity, profile.profile_name).close_listener.push(() => { + profiles.mark_need_save(); + + identity.level().then(level => { + event_registry.fire_async("improve-identity-teamspeak-level-update", { profile_id: event.profile_id, new_level: level }); + }).catch(error => { + log.error(LogCategory.CLIENT, tr("Failed to calculate identity level after improvement (%o)"), error); + }); + }); + }); + + event_registry.on("export-identity-teamspeak", event => { + const profile = profiles.find_profile(event.profile_id); + if(!profile) { + log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); + return; + } + + const identity = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity; + if (!identity) return; + + identity.export_ts(true).then(data => { + const element = $.spawn("a") + .text("donwload") + .attr("href", "data:test/plain;charset=utf-8," + encodeURIComponent(data)) + .attr("download", name + ".ini") + .css("display", "none") + .appendTo($("body")); + element[0].click(); + element.remove(); + }).catch(error => { + console.error(error); + createErrorModal(tr("Failed to export identity"), tr("Failed to export and save identity.
Error: ") + error).open(); + }); + }); + } + export function initialize_identity_profiles_view(container: JQuery, event_registry: Registry, settings: ProfileViewSettings) { + /* profile list */ { - const container_devices = container.find(".left .container-devices"); - const contianer_error = container.find(".left .container-error"); + const container_profiles = container.find(".container-profiles"); + let selected_profile; - const update_devices = () => { - container_devices.children().remove(); + const overlay_error = container_profiles.find(".overlay-error"); + const overlay_timeout = container_profiles.find(".overlay-timeout"); + const overlay_empty = container_profiles.find(".overlay-empty"); - const current_selected = audio.player.current_device(); - const generate_device = (device: audio.player.Device | undefined) => { - const selected = device === current_selected || (typeof(current_selected) !== "undefined" && typeof(device) !== "undefined" && current_selected.device_id == device.device_id); + const build_profile = (profile: events.modal.settings.ProfileInfo, selected: boolean) => { + let tag_avatar: JQuery, tag_default: JQuery; + let tag = $.spawn("div").addClass("profile").attr("profile-id", profile.id).append( + tag_avatar = $.spawn("div").addClass("container-avatar"), + $.spawn("div").addClass("container-info").append( + $.spawn("div").addClass("container-type").append( + $.spawn("div").addClass("identity-type").text(profile.identity_type || tr("Type unset")), + tag_default = $.spawn("div").addClass("tag-default").text(tr("(Default)")), + $.spawn("div").addClass("icon_em icon-status").hide() + ), + $.spawn("div").addClass("profile-name").text(profile.name || tr("Unnamed")) + ) + ); + tag_avatar.hide(); /* no avatars yet */ - const tag = $.spawn("div").addClass("device").toggleClass("selected", selected).append( + tag.on('click', event => event_registry.fire("select-profile", { profile_id: profile.id })); + tag.toggleClass("selected", selected); + tag_default.toggle(profile.id === "default"); + + event_registry.fire("query-profile-validity", { profile_id: profile.id }); + return tag; + }; + + event_registry.on("select-profile", event => { + container_profiles.find(".profile").removeClass("selected"); + container_profiles.find(".profile[profile-id='" + event.profile_id + "']").addClass("selected"); + selected_profile = event.profile_id; + }); + + + event_registry.on("query-profile-list", event => { + container_profiles.find(".profile").remove(); + }); + + event_registry.on("query-profile-list-result", event => { + container_profiles.find(".overlay").hide(); + if(event.status === "error") { + overlay_error.show().find(".error").text(event.error || tr("unknown error")); + return; + } else if(event.status === "timeout") { + overlay_timeout.show(); + return; + } + if(!event.profiles.length) { + overlay_empty.show(); + return; + } + + container_profiles.find(".overlay").hide(); + container_profiles.find(".profile").remove(); + event.profiles.forEach(e => build_profile(e, e.id == selected_profile).appendTo(container_profiles)); + }); + + event_registry.on("delete-profile-result", event => { + if(event.status !== "success") return; + + //TODO: Animate removal? + container_profiles.find(".profile[profile-id='" + event.profile_id + "']").remove(); + }); + + event_registry.on('create-profile-result', event => { + if(event.status !== "success") return; + + event_registry.fire("query-profile-list"); + event_registry.one("query-profile-list-result", e => event_registry.fire("select-profile", { profile_id: event.profile_id })); + }); + + event_registry.on("set-profile-name-result", event => { + if(event.status !== "success") return; + + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".profile-name").text(event.name || tr("Unnamed")); + }); + + event_registry.on("set-default-profile-result", event => { + if(event.status !== "success") return; + + const old_profile = container_profiles.find(".profile[profile-id='default']"); + const new_profile = container_profiles.find(".profile[profile-id='" + event.old_profile_id + "']"); + old_profile.attr("profile-id", event.new_profile_id).find(".tag-default").hide(); + new_profile.attr("profile-id", "default").find(".tag-default").show(); + }); + + event_registry.on("select-identity-type", event => { + if(!event.identity_type) return; + + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".identity-type").text(event.identity_type.toUpperCase() || tr("Type unset")); + }); + + event_registry.on("query-profile-validity-result", event => { + const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); + profile.find(".icon-status") + .show() + .toggleClass("client-apply", event.status === "success" && event.valid) + .toggleClass("client-delete", event.status !== "success" || !event.valid) + .attr("title", event.status === "success" ? event.valid ? tr("Profile is valid") : tr("Provile is invalid") : event.error || tr("failed to query status")); + }); + + /* status indicator updaters */ + event_registry.on("select-identity-type", event => { + if(!event.profile_id) return; + + /* we need a short delay so everything could apply*/ + setTimeout(() => { + event_registry.fire("query-profile-validity", { profile_id: event.profile_id }); + }, 100); + }); + event_registry.on(["set-default-name-result", "set-profile-name-result", "set-identity-name-name-result", "generate-identity-teamspeak-result"], event => { + if(!('status' in event) ||!('profile_id' in event)) { + log.warn(LogCategory.CLIENT, tr("Profile status watcher encountered an unuseal event!")); + return; + } + if((event as any).status !== "success") return; + event_registry.fire("query-profile-validity", { profile_id: (event as any).profile_id }); + }) + } + + /* list buttons */ + { + /* reload */ + { + const button = container.find(".button-reload-list"); + + button.on('click', event => event_registry.fire("query-profile-list")); + + event_registry.on("query-profile-list", event => button.prop("disabled", true)); + event_registry.on("query-profile-list-result", event => button.prop("disabled", false)); + } + + /* set default */ + { + const button = container.find(".button-set-default"); + let current_profile; + + button.on('click', event => event_registry.fire("set-default-profile", { profile_id: current_profile })); + event_registry.on("select-profile", event => { + current_profile = event.profile_id; + button.prop("disabled", !event.profile_id || event.profile_id === "default"); + }); + + event_registry.on("set-default-profile-result", event => { + if(event.status === "success") return; + + createErrorModal(tr("Failed to set default profile"), tr("Failed to set default profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); + }); + button.prop("disabled", true); + } + + /* delete button */ + { + const button = container.find(".button-delete"); + let current_profile; + + button.on('click', event => { + if(!current_profile || current_profile === "default") return; + + spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this profile?"), result => { + if (result) + event_registry.fire("delete-profile", { profile_id: current_profile }); + }); + }); + + event_registry.on("delete-profile-result", event => { + if(event.status === "success") return; + + createErrorModal(tr("Failed to delete profile"), tr("Failed to delete profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); + }); + + event_registry.on("select-profile", event => { + current_profile = event.profile_id; + + button.prop("disabled", !event.profile_id || event.profile_id === "default"); + }); + } + + /* create button */ + { + const button = container.find(".button-create"); + button.on('click', event => { + createInputModal(tr("Please enter a name"), tr("Please enter a name for the new profile:"), text => text.length >= 3 && !profiles.find_profile_by_name(text), value => { + if (value) + event_registry.fire("create-profile", { name: value as string }); + }).open(); + }); + + event_registry.on('create-profile', event => button.prop("disabled", true)); + event_registry.on("create-profile-result", event => { + button.prop("disabled", false); + if(event.status === "success") { + event_registry.fire("select-profile", { profile_id: event.profile_id }); + return; + } + + createErrorModal(tr("Failed to create profile"), tr("Failed to create new profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); + }) + } + } + + + /* profile info */ + { + let current_profile; + const error_text = event => event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")); + + /* general info */ + { + /* profile name */ + { + const input = container.find(".profile-name"); + let last_name; + + const update_name = () => input.prop("disabled", false) + .val(last_name) + .attr("placeholder", tr("Profile name")) + .parent().removeClass("is-invalid"); + + const info_name = text => input.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + + event_registry.on("query-profile", event => { + if(event.profile_id !== current_profile) return; + + info_name(tr("loading")); + }); + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status === "success") { + last_name = event.info.name; + update_name(); + } else { + info_name(error_text(event)); + } + }); + + event_registry.on("set-profile-name", event => { + if(event.profile_id !== current_profile) return; + + info_name(tr("saving")); + }); + + event_registry.on("set-profile-name-result", event => { + if(event.status !== "success") { + createErrorModal(tr("Failed to change profile name"), tr("Failed to create apply new name:") + "
" + error_text(event)).open(); + } else { + last_name = event.name; + } + update_name(); + }); + + input.on('keyup', event => { + const text = input.val() as string; + const profile = profiles.find_profile_by_name(text); + input.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); + }).on('change', event => { + const text = input.val() as string; + const profile = profiles.find_profile_by_name(text); + if(text.length < 3 || (profile && profile.id != current_profile)) return; + + event_registry.fire("set-profile-name", { profile_id: current_profile, name: text }); + }); + } + + /* nickname name */ + { + const input = container.find(".profile-default-name"); + let last_name = null, fallback_names = {}, current_identity_type = ""; + + const update_name = () => input.prop("disabled", false) + .val(last_name) + .attr("placeholder", fallback_names[current_identity_type] || tr("Another TeaSpeak user")) + .parent().removeClass("is-invalid"); + + const info_name = text => input.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + + event_registry.on("query-profile", event => { + if(event.profile_id !== current_profile) return; + + input.prop("disabled", true).val(null).attr("placeholder", tr("loading")); + }); + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + if(event.status === "success") { + current_identity_type = event.info.identity_type; + fallback_names["nickname"] = event.info.identity_nickname ? event.info.identity_nickname.fallback_name : undefined; + fallback_names["teaforo"] = event.info.identity_forum ? event.info.identity_forum.fallback_name : undefined; + fallback_names["teamspeak"] = event.info.identity_teamspeak ? event.info.identity_teamspeak.fallback_name : undefined; + + last_name = event.info.nickname; + update_name(); + } else { + info_name(error_text(event)); + } + }); + + event_registry.on("select-identity-type", event => { + if (current_identity_type === event.identity_type) return; + + current_identity_type = event.identity_type; + update_name(); + }); + + event_registry.on("set-default-name", event => { + if(event.profile_id !== current_profile) return; + + info_name(tr("saving")); + }); + + event_registry.on("set-default-name-result", event => { + if(event.status !== "success") { + createErrorModal(tr("Failed to change nickname"), tr("Failed to create apply new nickname:") + "
" + error_text(event)).open(); + } else { + last_name = event.name; + } + update_name(); + }); + + input.on('keyup', event => { + const text = input.val() as string; + input.parent().toggleClass("is-invalid", text.length != 0 && text.length < 3); + }).on('change', event => { + const text = input.val() as string; + if(text.length != 0 && text.length < 3) return; + + event_registry.fire("set-default-name", { profile_id: current_profile, name: text }); + }); + } + + /* identity type */ + { + const select_identity_type = container.find(".profile-identity-type"); + + const show_message = (text, is_invalid) => select_identity_type + .toggleClass("is-invalid", is_invalid) + .prop("disabled", true) + .find("option[value=error]") + .text(text) + .prop("selected", true); + + const set_type = type => select_identity_type + .toggleClass("is-invalid", type === "unset") + .prop("disabled", false) + .find("option[value=" + type + "]") + .prop("selected", true); + + event_registry.on("query-profile", event => show_message(tr("loading"), false)); + + event_registry.on("select-identity-type", event => { + if(event.profile_id !== current_profile) return; + + set_type(event.identity_type || "unset"); + }); + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status === "success") + event_registry.fire("select-identity-type", { profile_id: event.profile_id, identity_type: event.info.identity_type }); + else + show_message(error_text(event), false); + }); + + select_identity_type.on('change', event => { + const type = (select_identity_type.val() as string).toLowerCase(); + if(type === "error" || type == "unset") return; + + event_registry.fire("select-identity-type", { profile_id: current_profile, identity_type: type as any }); + }); + } + + /* avatar */ + { + container.find(".button-change-avatar").hide(); + } + } + + /* special info TeamSpeak */ + { + const container_settings = container.find(".container-teamspeak"); + const container_valid = container_settings.find(".container-valid"); + const container_invalid = container_settings.find(".container-invalid"); + + const input_current_level = container_settings.find(".current-level"); + const input_unique_id = container_settings.find(".unique-id"); + + const button_new = container_settings.find(".button-new"); + const button_improve = container_settings.find(".button-improve"); + + const button_import = container_settings.find(".button-import"); + const button_export = container_settings.find(".button-export"); + + let is_profile_generated = false; + + event_registry.on("select-identity-type", event => { + if(event.profile_id !== current_profile) return; + + container_settings.toggle(event.identity_type === "teamspeak"); + }); + + event_registry.on("query-profile", event => { + input_unique_id.val(null).attr("placeholder", tr("loading")); + input_current_level.val(null).attr("placeholder", tr("loading")); + + button_new.prop("disabled", true); + button_improve.prop("disabled", true); + button_import.prop("disabled", true); + button_export.prop("disabled", true); + }); + + const update_identity = (state: "not-created" | "created", unique_id?: string, level?: number) => { + if(state === "not-created") { + container_invalid.show(); + container_valid.hide(); + + button_improve.prop("disabled", true); + button_export.prop("disabled", true); + } else { + container_invalid.hide(); + container_valid.show(); + + input_unique_id.val(unique_id).attr("placeholder", null); + if(typeof level !== "number") + event_registry.fire("query-identity-teamspeak", { profile_id: current_profile }); + else + input_current_level.val(level).attr("placeholder", null); + + button_improve.prop("disabled", false); + button_export.prop("disabled", false); + } + + is_profile_generated = state === "created"; + button_new.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); + button_import.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); + + button_new.prop("disabled", false); + button_import.prop("disabled", false); + }; + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status !== "success") { + input_unique_id.val(null).attr("placeholder", error_text(event)); + return; + } + + if(!event.info.identity_teamspeak) + update_identity("not-created"); + else + update_identity("created", event.info.identity_teamspeak.unique_id); + }); + + event_registry.on("query-identity-teamspeak-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status === "success") { + input_current_level.val(event.level).attr("placeholder", null); + } else { + input_current_level.val(null).attr("placeholder", error_text(event)); + } + }); + + /* the new button */ + { + button_new.on('click', event => { + if(is_profile_generated) { + spawnYesNo(tr("Are you sure"), tr("Do you really want to generate a new identity and override the old identity?"), result => { + if (result) event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); + }); + } else { + event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); + } + }); + + event_registry.on("generate-identity-teamspeak-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status !== "success") { + createErrorModal(tr("Failed to generate a new identity"), tr("Failed to create a new identity:") + "
" + error_text(event)).open(); + return; + } + + update_identity("created", event.unique_id, event.level); + createInfoModal(tr("Identity generated"), tr("A new identity had been successfully generated")).open(); + }); + } + + /* the import identity */ + { + button_import.on('click', event => { + if(is_profile_generated) { + spawnYesNo(tr("Are you sure"), tr("Do you really want to import a new identity and override the old identity?"), result => { + if (result) event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); + }); + } else { + event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); + } + }); + + event_registry.on("improve-identity-teamspeak-level-update", event => { + if(event.profile_id !== current_profile) return; + + input_current_level.val(event.new_level).attr("placeholder", null); + }); + + event_registry.on("import-identity-teamspeak-result", event => { + if(event.profile_id !== current_profile) return; + + event_registry.fire_async("query-profile", { profile_id: event.profile_id }); /* we do it like this so the default nickname changes as well */ + createInfoModal(tr("Identity imported"), tr("Your identity had been successfully imported generated")).open(); + }); + } + + /* identity export */ + { + button_export.on('click', event => { + createInputModal(tr("File name"), tr("Please enter the file name"), text => !!text, name => { + if (name) + event_registry.fire("export-identity-teamspeak", { profile_id: current_profile, filename: name as string }); + }).open(); + }); + } + + /* the improve button */ + button_improve.on('click', event => event_registry.fire("improve-identity-teamspeak-level", { profile_id: current_profile })); + } + + /* special info TeaSpeak - Forum */ + { + const container_settings = container.find(".container-teaforo"); + const container_valid = container_settings.find(".container-valid"); + const container_invalid = container_settings.find(".container-invalid"); + + const button_setup = container_settings.find(".button-setup"); + + event_registry.on("select-identity-type", event => { + if(event.profile_id !== current_profile) return; + + container_settings.toggle(event.identity_type === "teaforo"); + }); + + event_registry.on("query-profile", event => { + container_valid.toggle(false); + container_invalid.toggle(false); + }); + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + + const valid = event.status === "success" && event.info.identity_forum && event.info.identity_forum.valid; + container_valid.toggle(!!valid); + container_invalid.toggle(!valid); + }); + + button_setup.on('click', event => event_registry.fire_async("setup-forum-connection")); + button_setup.toggle(settings.forum_setuppable); + } + + /* special info nickname */ + { + const container_settings = container.find(".container-nickname"); + const input_nickname = container_settings.find(".nickname"); + let last_name; + + const update_name = () => input_nickname.prop("disabled", false) + .val(last_name) + .attr("placeholder", tr("Identity base name")) + .parent().removeClass("is-invalid"); + + const show_info = text => input_nickname.prop("disabled", true) + .val(null) + .attr("placeholder", text) + .parent().removeClass("is-invalid"); + + event_registry.on("select-identity-type", event => event.profile_id === current_profile && container_settings.toggle(event.identity_type === "nickname")); + + event_registry.on("query-profile", event => { + if(event.profile_id !== current_profile) return; + + show_info(tr("loading")); + }); + + event_registry.on("query-profile-result", event => { + if(event.profile_id !== current_profile) return; + + if(event.status === "success") { + last_name = event.info.identity_nickname ? event.info.identity_nickname.name : null; + update_name(); + } else { + show_info(error_text(event)); + } + }); + + event_registry.on("set-identity-name-name", event => { + if(event.profile_id !== current_profile) return; + show_info(tr("saving")); + }); + + event_registry.on("set-identity-name-name-result", event => { + if(event.status !== "success") { + createErrorModal(tr("Failed to change name"), tr("Failed to create new name:") + "
" + error_text(event)).open(); + } else { + last_name = event.name; + } + update_name(); + }); + + input_nickname.on('keyup', event => { + const text = input_nickname.val() as string; + const profile = profiles.find_profile_by_name(text); + input_nickname.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); + }).on('change', event => { + const text = input_nickname.val() as string; + const profile = profiles.find_profile_by_name(text); + if(text.length < 3 || (profile && profile.id != current_profile)) return; + + event_registry.fire("set-identity-name-name", { profile_id: current_profile, name: text }); + }); + } + event_registry.on("select-profile", e => current_profile = e.profile_id); + } + + /* timeouts */ + { + /* profile list */ + { + let timeout; + event_registry.on("query-profile-list", event => timeout = setTimeout(() => event_registry.fire("query-profile-list-result", { status: "timeout" }), 5000)); + event_registry.on("query-profile-list-result", event => { + clearTimeout(timeout); + timeout = undefined; + }); + } + + /* profile create */ + { + const timeouts = {}; + event_registry.on("create-profile", event => { + clearTimeout(timeouts[event.name]); + timeouts[event.name] = setTimeout(() => { + event_registry.fire("create-profile-result", { name: event.name, status: "timeout" }); + }, 5000); + }); + + event_registry.on("create-profile-result", event => { + clearTimeout(timeouts[event.name]); + delete timeouts[event.name]; + }); + } + + /* profile set default create */ + { + const timeouts = {}; + event_registry.on("set-default-profile", event => { + clearTimeout(timeouts[event.profile_id]); + timeouts[event.profile_id] = setTimeout(() => { + event_registry.fire("set-default-profile-result", { old_profile_id: event.profile_id, status: "timeout" }); + }, 5000); + }); + + event_registry.on("set-default-profile-result", event => { + clearTimeout(timeouts[event.old_profile_id]); + delete timeouts[event.old_profile_id]; + }); + } + + const create_standard_timeout = (event: keyof events.modal.settings.profiles, response_event: keyof events.modal.settings.profiles, key: string) => { + const timeouts = {}; + event_registry.on(event, event => { + clearTimeout(timeouts[event[key]]); + timeouts[event[key]] = setTimeout(() => { + const timeout_event = { status: "timeout" }; + timeout_event[key] = event[key]; + event_registry.fire(response_event, timeout_event as any); + }, 5000); + }); + + event_registry.on(response_event, event => { + clearTimeout(timeouts[event[key]]); + delete timeouts[event[key]]; + }); + }; + + create_standard_timeout("query-profile", "query-profile-result", "profile_id"); + create_standard_timeout("query-identity-teamspeak", "query-identity-teamspeak-result", "profile_id"); + create_standard_timeout("delete-profile", "delete-profile-result", "profile_id"); + create_standard_timeout("set-profile-name", "set-profile-name-result", "profile_id"); + create_standard_timeout("set-default-name", "set-default-name-result", "profile_id"); + create_standard_timeout("query-profile-validity", "query-profile-validity-result", "profile_id"); + create_standard_timeout("set-identity-name-name", "set-identity-name-name-result", "profile_id"); + create_standard_timeout("generate-identity-teamspeak", "generate-identity-teamspeak-result", "profile_id"); + } + + /* some view semantics */ + { + let selected_profile; + event_registry.on("delete-profile-result", event => { + if(event.status !== "success") return; + if(event.profile_id !== selected_profile) return; + + /* the selected profile has been deleted, so we need to select another one */ + event_registry.fire("select-profile", { profile_id: "default" }); + }); + + /* reselect the default profile or the new default profile */ + event_registry.on("set-default-profile-result", event => { + if(event.status !== "success") return; + if(selected_profile === "default") + event_registry.fire("select-profile", { profile_id: event.new_profile_id }); + else if(selected_profile === event.old_profile_id) + event_registry.fire("select-profile", { profile_id: "default" }); + }); + + event_registry.on("select-profile", event => { + selected_profile = event.profile_id; + event_registry.fire("query-profile", { profile_id: event.profile_id }); + }); + + event_registry.on("reload-profile", event => { + event_registry.fire("query-profile-list"); + event_registry.fire("select-profile", event.profile_id || selected_profile); + }); + } + + event_registry.fire("query-profile-list"); + event_registry.fire("select-profile", { profile_id: "default" }); + event_registry.fire("select-identity-type", { profile_id: "default", identity_type: undefined }); + } + + export function initialize_audio_microphone_controller(event_registry: Registry) { + /* level meters */ + { + const level_meters: {[key: string]:Promise} = {}; + const level_info: {[key: string]:any} = {}; + let level_update_task; + + const destroy_meters = () => { + Object.keys(level_meters).forEach(e => { + const meter = level_meters[e]; + delete level_meters[e]; + + meter.then(e => e.destory()); + }); + Object.keys(level_info).forEach(e => delete level_info[e]); + }; + + const update_level_meter = () => { + destroy_meters(); + + for(const device of arecorder.devices()) { + let promise = arecorder.create_levelmeter(device).then(meter => { + meter.set_observer(level => { + if(level_meters[device.unique_id] !== promise) return; /* old level meter */ + + level_info[device.unique_id] = { + device_id: device.unique_id, + status: "success", + level: level + }; + }); + return Promise.resolve(meter); + }).catch(error => { + if(level_meters[device.unique_id] !== promise) return; /* old level meter */ + level_info[device.unique_id] = { + device_id: device.unique_id, + status: "error", + + error: error + }; + + log.warn(LogCategory.AUDIO, tr("Failed to initialize a level meter for device %s (%s): %o"), device.unique_id, device.driver + ":" + device.name, error); + return Promise.reject(error); + }); + level_meters[device.unique_id] = promise; + } + }; + + level_update_task = setInterval(() => { + event_registry.fire("update-device-level", { + devices: Object.keys(level_info).map(e => level_info[e]) + }); + }, 50); + + event_registry.on("query-device-result", event => { + if(event.status !== "success") return; + + update_level_meter(); + }); + + event_registry.on("deinitialize", event => { + destroy_meters(); + clearInterval(level_update_task); + }); + } + + /* device list */ + { + event_registry.on("query-devices", event => { + Promise.resolve().then(() => { + return arecorder.device_refresh_available() && event.refresh_list ? arecorder.refresh_devices() : Promise.resolve(); + }).catch(error => { + log.warn(LogCategory.AUDIO, tr("Failed to refresh device list: %o"), error); + return Promise.resolve(); + }).then(() => { + const devices = arecorder.devices(); + + event_registry.fire_async("query-device-result", { + status: "success", + active_device: default_recorder.current_device() ? default_recorder.current_device().unique_id : "none", + devices: devices.map(e => { return { id: e.unique_id, name: e.name, driver: e.driver }}) + }); + }); + }); + + event_registry.on("set-device", event => { + const device = arecorder.devices().find(e => e.unique_id === event.device_id); + if(!device && event.device_id !== "none") { + event_registry.fire_async("set-device-result", { status: "error", error: tr("Invalid device id"), device_id: event.device_id }); + return; + } + + default_recorder.set_device(device).then(() => { + console.debug(tr("Changed default microphone device")); + event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); + }).catch((error) => { + log.warn(LogCategory.AUDIO, tr("Failed to change microphone to device %s: %o"), device ? device.unique_id : "none", error) + event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); + }); + }); + } + + /* settings */ + { + event_registry.on("query-settings", event => { + event_registry.fire_async("query-settings-result", { + status: "success", + info: { + volume: default_recorder.get_volume(), + vad_type: default_recorder.get_vad_type(), + vad_ppt: { + key: default_recorder.get_vad_ppt_key(), + release_delay: Math.abs(default_recorder.get_vad_ppt_delay()), + release_delay_active: default_recorder.get_vad_ppt_delay() >= 0 + }, + vad_threshold: { + threshold: default_recorder.get_vad_threshold() + } + } + }); + }); + + event_registry.on("set-setting", event => { + const ensure_type = (type: "object" | "string" | "boolean" | "number" | "undefined") => { + if(typeof event.value !== type) { + event_registry.fire_async("set-setting-result", { status: "error", error: tr("Invalid value type for key") + " (expected: " + type + ", received: " + typeof event.value + ")", setting: event.setting }); + return false; + } + return true; + }; + + switch (event.setting) { + case "volume": + if(!ensure_type("number")) return; + default_recorder.set_volume(event.value); + break; + + case "threshold-threshold": + if(!ensure_type("number")) return; + default_recorder.set_vad_threshold(event.value); + break; + + case "vad-type": + if(!ensure_type("string")) return; + if(!default_recorder.set_vad_type(event.value)) { + event_registry.fire_async("set-setting-result", { status: "error", error: tr("Unknown VAD type"), setting: event.setting }); + return; + } + break; + + case "ppt-key": + if(!ensure_type("object")) return; + default_recorder.set_vad_ppt_key(event.value); + break; + + case "ppt-release-delay": + if(!ensure_type("number")) return; + const sign = default_recorder.get_vad_ppt_delay() >= 0 ? 1 : -1; + default_recorder.set_vad_ppt_delay(sign * event.value); + break; + + case "ppt-release-delay-active": + if(!ensure_type("boolean")) return; + default_recorder.set_vad_ppt_delay(Math.abs(default_recorder.get_vad_ppt_delay()) * (event.value ? 1 : -1)); + break; + + default: + event_registry.fire_async("set-setting-result", { status: "error", error: tr("Invalid setting key"), setting: event.setting }); + return; + } + event_registry.fire_async("set-setting-result", { status: "success", setting: event.setting, value: event.value }); + }); + } + + aplayer.on_ready(() => event_registry.fire_async("audio-initialized", {})); + } + export function initialize_audio_microphone_view(container: JQuery, event_registry: Registry) { + /* device list */ + { + /* actual list */ + { + const container_devices = container.find(".container-devices"); + const volume_bar_tags: {[key: string]:{ volume: JQuery, error: JQuery }} = {}; + let pending_changes = 0; + let default_device_id; + + const build_device = (device: { id: string, name: string, driver: string }, selected: boolean) => { + let tag_volume: JQuery, tag_volume_error: JQuery; + const tag = $.spawn("div").attr("device-id", device ? device.id : "none").addClass("device").toggleClass("selected", selected).append( $.spawn("div").addClass("container-selected").append( - $.spawn("div").addClass("icon_em client-apply") + $.spawn("div").addClass("icon_em client-apply"), + $.spawn("div").addClass("icon-loading").append( + $.spawn("img").attr("src", "img/icon_settings_loading.svg") + ) ), $.spawn("div").addClass("container-name").append( $.spawn("div").addClass("device-driver").text( @@ -444,2015 +1884,609 @@ namespace Modals { ), $.spawn("div").addClass("device-name").text( device ? (device.name || "Unknown name") : "No device" + ), + ), + $.spawn("div").addClass("container-activity").append( + $.spawn("div").addClass("container-activity-bar").append( + tag_volume = $.spawn("div").addClass("bar-hider"), + tag_volume_error = $.spawn("div").addClass("bar-error") ) ) ); + tag_volume.css('width', '100%'); /* initially hide the bar */ + if(device) + volume_bar_tags[device.id] = { volume: tag_volume, error: tag_volume_error }; tag.on('click', event => { - if(tag.hasClass("selected")) - return; + if(tag.hasClass("selected") || pending_changes > 0) return; - const _old = container_devices.find(".selected"); - _old.removeClass("selected"); - tag.addClass("selected"); - - audio.player.set_device(device ? device.device_id : null).then(() => { - console.debug(tr("Changed default speaker device")); - }).catch((error) => { - _old.addClass("selected"); - tag.removeClass("selected"); - - console.error(tr("Failed to change speaker to device %o: %o"), device, error); - createErrorModal(tr("Failed to change speaker"), MessageHelper.formatMessage(tr("Failed to change the speaker device to the target speaker{:br:}{}"), error)).open(); - }); + event_registry.fire("set-device", { device_id: device ? device.id : "none" }); }); return tag; }; - generate_device(undefined).appendTo(container_devices); - audio.player.available_devices().then(result => { - contianer_error.text("").hide(); - result.forEach(e => generate_device(e).appendTo(container_devices)); - }).catch(error => { - if(typeof(error) === "string") - contianer_error.text(error).show(); - - console.log(tr("Failed to query available speaker devices: %o"), error); - contianer_error.text(tr("Errors occurred (View console)")).show(); - }); - }; - update_devices(); - - const button_update = container.find(".button-update"); - button_update.on('click', async event => { - button_update.prop("disabled", true); - try { - update_devices(); - } catch(error) { - console.error(tr("Failed to build new speaker device list: %o"), error); - } - button_update.prop("disabled", false); - }); - } - - /* slider */ - { - - { - const container_master = container.find(".container-volume-master"); - const slider = container_master.find(".container-slider"); - sliderfy(slider, { - min_value: 0, - max_value: 100, - step: 1, - initial_value: settings.static_global(Settings.KEY_SOUND_MASTER, 100), - value_field: [container_master.find(".container-value")] - }); - slider.on('change', event => { - const volume = parseInt(slider.attr('value')); - - if(audio.player.set_master_volume) - audio.player.set_master_volume(volume / 100); - settings.changeGlobal(Settings.KEY_SOUND_MASTER, volume); - }); - } - - { - const container_soundpack = container.find(".container-volume-soundpack"); - const slider = container_soundpack.find(".container-slider"); - sliderfy(slider, { - min_value: 0, - max_value: 100, - step: 1, - initial_value: settings.static_global(Settings.KEY_SOUND_MASTER_SOUNDS, 100), - value_field: [container_soundpack.find(".container-value")] - }); - slider.on('change', event => { - const volume = parseInt(slider.attr('value')); - sound.set_master_volume(volume / 100); - settings.changeGlobal(Settings.KEY_SOUND_MASTER_SOUNDS, volume); - }); - } - } - - /* button test sound */ - { - container.find(".button-test-sound").on('click', event => { - sound.manager.play(Sound.SOUND_TEST, { - default_volume: 1, - ignore_muted: true, - ignore_overlap: true - }) - }); - } - } - - function settings_audio_sounds(contianer: JQuery, modal: Modal) { - /* initialize sound list */ - { - const container_sounds = contianer.find(".container-sounds"); - - const generate_sound = (_sound: Sound) => { - let tag_play_pause: JQuery, tag_play: JQuery, tag_pause: JQuery, tag_input_muted: JQuery; - let tag = $.spawn("div").addClass("sound").append( - tag_play_pause = $.spawn("div").addClass("container-button-play_pause").append( - tag_play = $.spawn("img").attr("src", "img/icon_sound_play.svg"), - tag_pause = $.spawn("img").attr("src", "img/icon_sound_pause.svg") - ), - $.spawn("div").addClass("container-name").text(_sound), - $.spawn("label").addClass("container-button-toggle").append( - $.spawn("div").addClass("switch").append( - tag_input_muted = $.spawn("input").attr("type", "checkbox"), - $.spawn("span").addClass("slider").append( - $.spawn("div").addClass("dot") - ) - ) - ) - ); - - tag_play_pause.on('click', event => { - if(tag_pause.is(":visible")) - return; - tag_play.hide(); - tag_pause.show(); - - const _done = flag => { - tag_pause.hide(); - tag_play.show(); - }; - const _timeout = setTimeout(() => _done(false), 10 * 1000); /* the sounds are not longer than 10 seconds */ - - sound.manager.play(_sound, { - ignore_overlap: true, - ignore_muted: true, - default_volume: 1, - - callback: flag => { - clearTimeout(_timeout); - _done(flag); - } - }); - }); - tag_pause.hide(); - - tag_input_muted.prop("checked", sound.get_sound_volume(_sound, 1) > 0); - tag_input_muted.on('change', event => { - const volume = tag_input_muted.prop("checked") ? 1 : 0; - sound.set_sound_volume(_sound, volume); - console.log(tr("Changed sound volume to %o for sound %o"), volume, _sound); - }); - - return tag; - }; - - //container-sounds - for(const sound_key in Sound) - generate_sound(Sound[sound_key as any] as any).appendTo(container_sounds); - - /* the filter */ - const input_filter = contianer.find(".input-sounds-filter"); - input_filter.on('change keyup', event => { - const filter = input_filter.val() as string; - - container_sounds.find(".sound").each((_, _element) => { - const element = $(_element); - element.toggle(filter.length == 0 || element.text().toLowerCase().indexOf(filter) !== -1); - }) - }); - } - - const overlap_tag = contianer.find(".option-overlap-same"); - overlap_tag.on('change', event => { - const activated = (event.target).checked; - sound.set_overlap_activated(activated); - }).prop("checked", sound.overlap_activated()); - - const mute_tag = contianer.find(".option-mute-output"); - mute_tag.on('change', event => { - const activated = (event.target).checked; - sound.set_ignore_output_muted(!activated); - }).prop("checked", !sound.ignore_output_muted()); - - modal.close_listener.push(sound.save); - } - - export namespace modal_settings { - export interface ProfileViewSettings { - forum_setuppable: boolean - } - export function initialize_identity_profiles_controller(event_registry: events.Registry) { - const send_error = (event, profile, text) => event_registry.fire_async(event, { status: "error", profile_id: profile, error: text }); - event_registry.on("create-profile", event => { - const profile = profiles.create_new_profile(event.name); - profiles.mark_need_save(); - event_registry.fire_async("create-profile-result", { - status: "success", - name: event.name, - profile_id: profile.id - }); - }); - - event_registry.on("delete-profile", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("delete-profile-result", event.profile_id, tr("Unknown profile")); - return; - } - - profiles.delete_profile(profile); - event_registry.fire_async("delete-profile-result", { status: "success", profile_id: event.profile_id }); - }); - - const build_profile_info = (profile: profiles.ConnectionProfile) => { - const forum_data = profile.selected_identity(profiles.identities.IdentitifyType.TEAFORO) as profiles.identities.TeaForumIdentity; - const teamspeak_data = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity; - const nickname_data = profile.selected_identity(profiles.identities.IdentitifyType.NICKNAME) as profiles.identities.NameIdentity; - - return { - id: profile.id, - name: profile.profile_name, - nickname: profile.default_username, - identity_type: profile.selected_identity_type as any, - identity_forum: !forum_data ? undefined : { - valid: forum_data.valid(), - fallback_name: forum_data.fallback_name() - }, - identity_nickname: !nickname_data ? undefined : { - name: nickname_data.name(), - fallback_name: nickname_data.fallback_name() - }, - identity_teamspeak: !teamspeak_data ? undefined : { - unique_id: teamspeak_data.uid(), - fallback_name: teamspeak_data.fallback_name() - } - } - }; - event_registry.on("query-profile-list", event => { - event_registry.fire_async("query-profile-list-result", { status: "success", profiles: profiles.profiles().map(e => build_profile_info(e)) }); - }); - - event_registry.on("query-profile", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("query-profile-result", event.profile_id, tr("Unknown profile")); - return; - } - - event_registry.fire_async("query-profile-result", { status: "success", profile_id: event.profile_id, info: build_profile_info(profile)}); - }); - - event_registry.on("set-default-profile", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("set-default-profile-result", event.profile_id, tr("Unknown profile")); - return; - } - - const old = profiles.set_default_profile(profile); - event_registry.fire_async("set-default-profile-result", { status: "success", old_profile_id: event.profile_id, new_profile_id: old.id }); - }); - - event_registry.on("set-profile-name", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("set-profile-name-result", event.profile_id, tr("Unknown profile")); - return; - } - - profile.profile_name = event.name; - profiles.mark_need_save(); - event_registry.fire_async("set-profile-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); - }); - - event_registry.on("set-default-name", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("set-default-name-result", event.profile_id, tr("Unknown profile")); - return; - } - - profile.default_username = event.name; - profiles.mark_need_save(); - event_registry.fire_async("set-default-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); - }); - - event_registry.on("set-identity-name-name", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("set-identity-name-name-result", event.profile_id, tr("Unknown profile")); - return; - } - - let identity = profile.selected_identity(profiles.identities.IdentitifyType.NICKNAME) as profiles.identities.NameIdentity; - if(!identity) - profile.set_identity(profiles.identities.IdentitifyType.NICKNAME, identity = new profiles.identities.NameIdentity()); - identity.set_name(event.name); - profiles.mark_need_save(); - - event_registry.fire_async("set-identity-name-name-result", { name: event.name, profile_id: event.profile_id, status: "success" }); - }); - - event_registry.on("query-profile-validity", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("query-profile-validity-result", event.profile_id, tr("Unknown profile")); - return; - } - - event_registry.fire_async("query-profile-validity-result", { status: "success", profile_id: event.profile_id, valid: profile.valid() }); - }); - - event_registry.on("query-identity-teamspeak", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("query-identity-teamspeak-result", event.profile_id, tr("Unknown profile")); - return; - } - - const ts = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity; - if(!ts) { - event_registry.fire_async("query-identity-teamspeak-result", { status: "error", profile_id: event.profile_id, error: tr("Missing identity") }); - return; - } - - ts.level().then(level => { - event_registry.fire_async("query-identity-teamspeak-result", { status: "success", level: level, profile_id: event.profile_id }); - }).catch(error => { - send_error("query-identity-teamspeak-result", event.profile_id, tr("failed to calculate level")); - }) - }); - - event_registry.on("select-identity-type", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - return; - } - - profile.selected_identity_type = event.identity_type; - profiles.mark_need_save(); - }); - - event_registry.on("generate-identity-teamspeak", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - send_error("generate-identity-teamspeak-result", event.profile_id, tr("Unknown profile")); - return; - } - - profiles.identities.TeaSpeakIdentity.generate_new().then(identity => { - profile.set_identity(profiles.identities.IdentitifyType.TEAMSPEAK, identity); - profiles.mark_need_save(); - - identity.level().then(level => { - event_registry.fire_async("generate-identity-teamspeak-result", { - status: "success", - profile_id: event.profile_id, - unique_id: identity.uid(), - level: level - }); - }).catch(error => { - console.error(tr("Failed to calculate level for a new identity. Error object: %o"), error); - send_error("generate-identity-teamspeak-result", event.profile_id, tr("failed to calculate level: ") + error); - }) - }).catch(error => { - console.error(tr("Failed to generate a new identity. Error object: %o"), error); - send_error("generate-identity-teamspeak-result", event.profile_id, tr("failed to generate identity: ") + error); - }); - }); - - event_registry.on("import-identity-teamspeak", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - return; - } - - spawnTeamSpeakIdentityImport(identity => { - profile.set_identity(profiles.identities.IdentitifyType.TEAMSPEAK, identity); - profiles.mark_need_save(); - - identity.level().catch(error => { - console.error(tr("Failed to calculate level for a new imported identity. Error object: %o"), error); - return Promise.resolve(undefined); - }).then(level => { - event_registry.fire_async("import-identity-teamspeak-result", { - profile_id: event.profile_id, - unique_id: identity.uid(), - level: level - }); - }); - }); - }); - - event_registry.on("improve-identity-teamspeak-level", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - return; - } - - const identity = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity; - if (!identity) return; - - Modals.spawnTeamSpeakIdentityImprove(identity, profile.profile_name).close_listener.push(() => { - profiles.mark_need_save(); - - identity.level().then(level => { - event_registry.fire_async("improve-identity-teamspeak-level-update", { profile_id: event.profile_id, new_level: level }); - }).catch(error => { - log.error(LogCategory.CLIENT, tr("Failed to calculate identity level after improvement (%o)"), error); - }); - }); - }); - - event_registry.on("export-identity-teamspeak", event => { - const profile = profiles.find_profile(event.profile_id); - if(!profile) { - log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id); - return; - } - - const identity = profile.selected_identity(profiles.identities.IdentitifyType.TEAMSPEAK) as profiles.identities.TeaSpeakIdentity; - if (!identity) return; - - identity.export_ts(true).then(data => { - const element = $.spawn("a") - .text("donwload") - .attr("href", "data:test/plain;charset=utf-8," + encodeURIComponent(data)) - .attr("download", name + ".ini") - .css("display", "none") - .appendTo($("body")); - element[0].click(); - element.remove(); - }).catch(error => { - console.error(error); - createErrorModal(tr("Failed to export identity"), tr("Failed to export and save identity.
Error: ") + error).open(); - }); - }); - } - export function initialize_identity_profiles_view(container: JQuery, event_registry: events.Registry, settings: ProfileViewSettings) { - /* profile list */ - { - const container_profiles = container.find(".container-profiles"); - let selected_profile; - - const overlay_error = container_profiles.find(".overlay-error"); - const overlay_timeout = container_profiles.find(".overlay-timeout"); - const overlay_empty = container_profiles.find(".overlay-empty"); - - const build_profile = (profile: events.modal.settings.ProfileInfo, selected: boolean) => { - let tag_avatar: JQuery, tag_default: JQuery; - let tag = $.spawn("div").addClass("profile").attr("profile-id", profile.id).append( - tag_avatar = $.spawn("div").addClass("container-avatar"), - $.spawn("div").addClass("container-info").append( - $.spawn("div").addClass("container-type").append( - $.spawn("div").addClass("identity-type").text(profile.identity_type || tr("Type unset")), - tag_default = $.spawn("div").addClass("tag-default").text(tr("(Default)")), - $.spawn("div").addClass("icon_em icon-status").hide() - ), - $.spawn("div").addClass("profile-name").text(profile.name || tr("Unnamed")) - ) - ); - tag_avatar.hide(); /* no avatars yet */ - - tag.on('click', event => event_registry.fire("select-profile", { profile_id: profile.id })); - tag.toggleClass("selected", selected); - tag_default.toggle(profile.id === "default"); - - event_registry.fire("query-profile-validity", { profile_id: profile.id }); - return tag; - }; - - event_registry.on("select-profile", event => { - container_profiles.find(".profile").removeClass("selected"); - container_profiles.find(".profile[profile-id='" + event.profile_id + "']").addClass("selected"); - selected_profile = event.profile_id; - }); - - - event_registry.on("query-profile-list", event => { - container_profiles.find(".profile").remove(); - }); - - event_registry.on("query-profile-list-result", event => { - container_profiles.find(".overlay").hide(); - if(event.status === "error") { - overlay_error.show().find(".error").text(event.error || tr("unknown error")); - return; - } else if(event.status === "timeout") { - overlay_timeout.show(); - return; - } - if(!event.profiles.length) { - overlay_empty.show(); - return; - } - - container_profiles.find(".overlay").hide(); - container_profiles.find(".profile").remove(); - event.profiles.forEach(e => build_profile(e, e.id == selected_profile).appendTo(container_profiles)); - }); - - event_registry.on("delete-profile-result", event => { - if(event.status !== "success") return; - - //TODO: Animate removal? - container_profiles.find(".profile[profile-id='" + event.profile_id + "']").remove(); - }); - - event_registry.on('create-profile-result', event => { - if(event.status !== "success") return; - - event_registry.fire("query-profile-list"); - event_registry.one("query-profile-list-result", e => event_registry.fire("select-profile", { profile_id: event.profile_id })); - }); - - event_registry.on("set-profile-name-result", event => { - if(event.status !== "success") return; - - const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); - profile.find(".profile-name").text(event.name || tr("Unnamed")); - }); - - event_registry.on("set-default-profile-result", event => { - if(event.status !== "success") return; - - const old_profile = container_profiles.find(".profile[profile-id='default']"); - const new_profile = container_profiles.find(".profile[profile-id='" + event.old_profile_id + "']"); - old_profile.attr("profile-id", event.new_profile_id).find(".tag-default").hide(); - new_profile.attr("profile-id", "default").find(".tag-default").show(); - }); - - event_registry.on("select-identity-type", event => { - if(!event.identity_type) return; - - const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); - profile.find(".identity-type").text(event.identity_type.toUpperCase() || tr("Type unset")); - }); - - event_registry.on("query-profile-validity-result", event => { - const profile = container_profiles.find(".profile[profile-id='" + event.profile_id + "']"); - profile.find(".icon-status") - .show() - .toggleClass("client-apply", event.status === "success" && event.valid) - .toggleClass("client-delete", event.status !== "success" || !event.valid) - .attr("title", event.status === "success" ? event.valid ? tr("Profile is valid") : tr("Provile is invalid") : event.error || tr("failed to query status")); - }); - - /* status indicator updaters */ - event_registry.on("select-identity-type", event => { - if(!event.profile_id) return; - - /* we need a short delay so everything could apply*/ - setTimeout(() => { - event_registry.fire("query-profile-validity", { profile_id: event.profile_id }); - }, 100); - }); - event_registry.on(["set-default-name-result", "set-profile-name-result", "set-identity-name-name-result", "generate-identity-teamspeak-result"], event => { - if(!('status' in event) ||!('profile_id' in event)) { - log.warn(LogCategory.CLIENT, tr("Profile status watcher encountered an unuseal event!")); - return; - } - if((event as any).status !== "success") return; - event_registry.fire("query-profile-validity", { profile_id: (event as any).profile_id }); - }) - } - - /* list buttons */ - { - /* reload */ - { - const button = container.find(".button-reload-list"); - - button.on('click', event => event_registry.fire("query-profile-list")); - - event_registry.on("query-profile-list", event => button.prop("disabled", true)); - event_registry.on("query-profile-list-result", event => button.prop("disabled", false)); - } - - /* set default */ - { - const button = container.find(".button-set-default"); - let current_profile; - - button.on('click', event => event_registry.fire("set-default-profile", { profile_id: current_profile })); - event_registry.on("select-profile", event => { - current_profile = event.profile_id; - button.prop("disabled", !event.profile_id || event.profile_id === "default"); - }); - - event_registry.on("set-default-profile-result", event => { - if(event.status === "success") return; - - createErrorModal(tr("Failed to set default profile"), tr("Failed to set default profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); - }); - button.prop("disabled", true); - } - - /* delete button */ - { - const button = container.find(".button-delete"); - let current_profile; - - button.on('click', event => { - if(!current_profile || current_profile === "default") return; - - spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this profile?"), result => { - if (result) - event_registry.fire("delete-profile", { profile_id: current_profile }); - }); - }); - - event_registry.on("delete-profile-result", event => { - if(event.status === "success") return; - - createErrorModal(tr("Failed to delete profile"), tr("Failed to delete profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); - }); - - event_registry.on("select-profile", event => { - current_profile = event.profile_id; - - button.prop("disabled", !event.profile_id || event.profile_id === "default"); - }); - } - - /* create button */ - { - const button = container.find(".button-create"); - button.on('click', event => { - createInputModal(tr("Please enter a name"), tr("Please enter a name for the new profile:"), text => text.length >= 3 && !profiles.find_profile_by_name(text), value => { - if (value) - event_registry.fire("create-profile", { name: value as string }); - }).open(); - }); - - event_registry.on('create-profile', event => button.prop("disabled", true)); - event_registry.on("create-profile-result", event => { - button.prop("disabled", false); - if(event.status === "success") { - event_registry.fire("select-profile", { profile_id: event.profile_id }); - return; - } - - createErrorModal(tr("Failed to create profile"), tr("Failed to create new profile:") + "
" + (event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")))).open(); - }) - } - } - - - /* profile info */ - { - let current_profile; - const error_text = event => event.status === "timeout" ? tr("request timeout") : (event.error || tr("unknown error")); - - /* general info */ - { - /* profile name */ - { - const input = container.find(".profile-name"); - let last_name; - - const update_name = () => input.prop("disabled", false) - .val(last_name) - .attr("placeholder", tr("Profile name")) - .parent().removeClass("is-invalid"); - - const info_name = text => input.prop("disabled", true) - .val(null) - .attr("placeholder", text) - .parent().removeClass("is-invalid"); - - event_registry.on("query-profile", event => { - if(event.profile_id !== current_profile) return; - - info_name(tr("loading")); - }); - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status === "success") { - last_name = event.info.name; - update_name(); - } else { - info_name(error_text(event)); - } - }); - - event_registry.on("set-profile-name", event => { - if(event.profile_id !== current_profile) return; - - info_name(tr("saving")); - }); - - event_registry.on("set-profile-name-result", event => { - if(event.status !== "success") { - createErrorModal(tr("Failed to change profile name"), tr("Failed to create apply new name:") + "
" + error_text(event)).open(); - } else { - last_name = event.name; - } - update_name(); - }); - - input.on('keyup', event => { - const text = input.val() as string; - const profile = profiles.find_profile_by_name(text); - input.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); - }).on('change', event => { - const text = input.val() as string; - const profile = profiles.find_profile_by_name(text); - if(text.length < 3 || (profile && profile.id != current_profile)) return; - - event_registry.fire("set-profile-name", { profile_id: current_profile, name: text }); - }); - } - - /* nickname name */ - { - const input = container.find(".profile-default-name"); - let last_name = null, fallback_names = {}, current_identity_type = ""; - - const update_name = () => input.prop("disabled", false) - .val(last_name) - .attr("placeholder", fallback_names[current_identity_type] || tr("Another TeaSpeak user")) - .parent().removeClass("is-invalid"); - - const info_name = text => input.prop("disabled", true) - .val(null) - .attr("placeholder", text) - .parent().removeClass("is-invalid"); - - event_registry.on("query-profile", event => { - if(event.profile_id !== current_profile) return; - - input.prop("disabled", true).val(null).attr("placeholder", tr("loading")); - }); - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - if(event.status === "success") { - current_identity_type = event.info.identity_type; - fallback_names["nickname"] = event.info.identity_nickname ? event.info.identity_nickname.fallback_name : undefined; - fallback_names["teaforo"] = event.info.identity_forum ? event.info.identity_forum.fallback_name : undefined; - fallback_names["teamspeak"] = event.info.identity_teamspeak ? event.info.identity_teamspeak.fallback_name : undefined; - - last_name = event.info.nickname; - update_name(); - } else { - info_name(error_text(event)); - } - }); - - event_registry.on("select-identity-type", event => { - if (current_identity_type === event.identity_type) return; - - current_identity_type = event.identity_type; - update_name(); - }); - - event_registry.on("set-default-name", event => { - if(event.profile_id !== current_profile) return; - - info_name(tr("saving")); - }); - - event_registry.on("set-default-name-result", event => { - if(event.status !== "success") { - createErrorModal(tr("Failed to change nickname"), tr("Failed to create apply new nickname:") + "
" + error_text(event)).open(); - } else { - last_name = event.name; - } - update_name(); - }); - - input.on('keyup', event => { - const text = input.val() as string; - input.parent().toggleClass("is-invalid", text.length != 0 && text.length < 3); - }).on('change', event => { - const text = input.val() as string; - if(text.length != 0 && text.length < 3) return; - - event_registry.fire("set-default-name", { profile_id: current_profile, name: text }); - }); - } - - /* identity type */ - { - const select_identity_type = container.find(".profile-identity-type"); - - const show_message = (text, is_invalid) => select_identity_type - .toggleClass("is-invalid", is_invalid) - .prop("disabled", true) - .find("option[value=error]") - .text(text) - .prop("selected", true); - - const set_type = type => select_identity_type - .toggleClass("is-invalid", type === "unset") - .prop("disabled", false) - .find("option[value=" + type + "]") - .prop("selected", true); - - event_registry.on("query-profile", event => show_message(tr("loading"), false)); - - event_registry.on("select-identity-type", event => { - if(event.profile_id !== current_profile) return; - - set_type(event.identity_type || "unset"); - }); - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status === "success") - event_registry.fire("select-identity-type", { profile_id: event.profile_id, identity_type: event.info.identity_type }); - else - show_message(error_text(event), false); - }); - - select_identity_type.on('change', event => { - const type = (select_identity_type.val() as string).toLowerCase(); - if(type === "error" || type == "unset") return; - - event_registry.fire("select-identity-type", { profile_id: current_profile, identity_type: type as any }); - }); - } - - /* avatar */ - { - container.find(".button-change-avatar").hide(); - } - } - - /* special info TeamSpeak */ - { - const container_settings = container.find(".container-teamspeak"); - const container_valid = container_settings.find(".container-valid"); - const container_invalid = container_settings.find(".container-invalid"); - - const input_current_level = container_settings.find(".current-level"); - const input_unique_id = container_settings.find(".unique-id"); - - const button_new = container_settings.find(".button-new"); - const button_improve = container_settings.find(".button-improve"); - - const button_import = container_settings.find(".button-import"); - const button_export = container_settings.find(".button-export"); - - let is_profile_generated = false; - - event_registry.on("select-identity-type", event => { - if(event.profile_id !== current_profile) return; - - container_settings.toggle(event.identity_type === "teamspeak"); - }); - - event_registry.on("query-profile", event => { - input_unique_id.val(null).attr("placeholder", tr("loading")); - input_current_level.val(null).attr("placeholder", tr("loading")); - - button_new.prop("disabled", true); - button_improve.prop("disabled", true); - button_import.prop("disabled", true); - button_export.prop("disabled", true); - }); - - const update_identity = (state: "not-created" | "created", unique_id?: string, level?: number) => { - if(state === "not-created") { - container_invalid.show(); - container_valid.hide(); - - button_improve.prop("disabled", true); - button_export.prop("disabled", true); - } else { - container_invalid.hide(); - container_valid.show(); - - input_unique_id.val(unique_id).attr("placeholder", null); - if(typeof level !== "number") - event_registry.fire("query-identity-teamspeak", { profile_id: current_profile }); - else - input_current_level.val(level).attr("placeholder", null); - - button_improve.prop("disabled", false); - button_export.prop("disabled", false); - } - - is_profile_generated = state === "created"; - button_new.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); - button_import.toggleClass("btn-blue", !is_profile_generated).toggleClass("btn-red", is_profile_generated); - - button_new.prop("disabled", false); - button_import.prop("disabled", false); - }; - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status !== "success") { - input_unique_id.val(null).attr("placeholder", error_text(event)); - return; - } - - if(!event.info.identity_teamspeak) - update_identity("not-created"); - else - update_identity("created", event.info.identity_teamspeak.unique_id); - }); - - event_registry.on("query-identity-teamspeak-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status === "success") { - input_current_level.val(event.level).attr("placeholder", null); - } else { - input_current_level.val(null).attr("placeholder", error_text(event)); - } - }); - - /* the new button */ - { - button_new.on('click', event => { - if(is_profile_generated) { - spawnYesNo(tr("Are you sure"), tr("Do you really want to generate a new identity and override the old identity?"), result => { - if (result) event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); - }); - } else { - event_registry.fire("generate-identity-teamspeak", { profile_id: current_profile }); - } - }); - - event_registry.on("generate-identity-teamspeak-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status !== "success") { - createErrorModal(tr("Failed to generate a new identity"), tr("Failed to create a new identity:") + "
" + error_text(event)).open(); - return; - } - - update_identity("created", event.unique_id, event.level); - createInfoModal(tr("Identity generated"), tr("A new identity had been successfully generated")).open(); - }); - } - - /* the import identity */ - { - button_import.on('click', event => { - if(is_profile_generated) { - spawnYesNo(tr("Are you sure"), tr("Do you really want to import a new identity and override the old identity?"), result => { - if (result) event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); - }); - } else { - event_registry.fire("import-identity-teamspeak", { profile_id: current_profile }); - } - }); - - event_registry.on("improve-identity-teamspeak-level-update", event => { - if(event.profile_id !== current_profile) return; - - input_current_level.val(event.new_level).attr("placeholder", null); - }); - - event_registry.on("import-identity-teamspeak-result", event => { - if(event.profile_id !== current_profile) return; - - event_registry.fire_async("query-profile", { profile_id: event.profile_id }); /* we do it like this so the default nickname changes as well */ - createInfoModal(tr("Identity imported"), tr("Your identity had been successfully imported generated")).open(); - }); - } - - /* identity export */ - { - button_export.on('click', event => { - createInputModal(tr("File name"), tr("Please enter the file name"), text => !!text, name => { - if (name) - event_registry.fire("export-identity-teamspeak", { profile_id: current_profile, filename: name as string }); - }).open(); - }); - } - - /* the improve button */ - button_improve.on('click', event => event_registry.fire("improve-identity-teamspeak-level", { profile_id: current_profile })); - } - - /* special info TeaSpeak - Forum */ - { - const container_settings = container.find(".container-teaforo"); - const container_valid = container_settings.find(".container-valid"); - const container_invalid = container_settings.find(".container-invalid"); - - const button_setup = container_settings.find(".button-setup"); - - event_registry.on("select-identity-type", event => { - if(event.profile_id !== current_profile) return; - - container_settings.toggle(event.identity_type === "teaforo"); - }); - - event_registry.on("query-profile", event => { - container_valid.toggle(false); - container_invalid.toggle(false); - }); - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - - const valid = event.status === "success" && event.info.identity_forum && event.info.identity_forum.valid; - container_valid.toggle(!!valid); - container_invalid.toggle(!valid); - }); - - button_setup.on('click', event => event_registry.fire_async("setup-forum-connection")); - button_setup.toggle(settings.forum_setuppable); - } - - /* special info nickname */ - { - const container_settings = container.find(".container-nickname"); - const input_nickname = container_settings.find(".nickname"); - let last_name; - - const update_name = () => input_nickname.prop("disabled", false) - .val(last_name) - .attr("placeholder", tr("Identity base name")) - .parent().removeClass("is-invalid"); - - const show_info = text => input_nickname.prop("disabled", true) - .val(null) - .attr("placeholder", text) - .parent().removeClass("is-invalid"); - - event_registry.on("select-identity-type", event => event.profile_id === current_profile && container_settings.toggle(event.identity_type === "nickname")); - - event_registry.on("query-profile", event => { - if(event.profile_id !== current_profile) return; - - show_info(tr("loading")); - }); - - event_registry.on("query-profile-result", event => { - if(event.profile_id !== current_profile) return; - - if(event.status === "success") { - last_name = event.info.identity_nickname ? event.info.identity_nickname.name : null; - update_name(); - } else { - show_info(error_text(event)); - } - }); - - event_registry.on("set-identity-name-name", event => { - if(event.profile_id !== current_profile) return; - show_info(tr("saving")); - }); - - event_registry.on("set-identity-name-name-result", event => { - if(event.status !== "success") { - createErrorModal(tr("Failed to change name"), tr("Failed to create new name:") + "
" + error_text(event)).open(); - } else { - last_name = event.name; - } - update_name(); - }); - - input_nickname.on('keyup', event => { - const text = input_nickname.val() as string; - const profile = profiles.find_profile_by_name(text); - input_nickname.parent().toggleClass("is-invalid", text.length < 3 || (profile && profile.id != current_profile)); - }).on('change', event => { - const text = input_nickname.val() as string; - const profile = profiles.find_profile_by_name(text); - if(text.length < 3 || (profile && profile.id != current_profile)) return; - - event_registry.fire("set-identity-name-name", { profile_id: current_profile, name: text }); - }); - } - event_registry.on("select-profile", e => current_profile = e.profile_id); - } - - /* timeouts */ - { - /* profile list */ - { - let timeout; - event_registry.on("query-profile-list", event => timeout = setTimeout(() => event_registry.fire("query-profile-list-result", { status: "timeout" }), 5000)); - event_registry.on("query-profile-list-result", event => { - clearTimeout(timeout); - timeout = undefined; - }); - } - - /* profile create */ - { - const timeouts = {}; - event_registry.on("create-profile", event => { - clearTimeout(timeouts[event.name]); - timeouts[event.name] = setTimeout(() => { - event_registry.fire("create-profile-result", { name: event.name, status: "timeout" }); - }, 5000); - }); - - event_registry.on("create-profile-result", event => { - clearTimeout(timeouts[event.name]); - delete timeouts[event.name]; - }); - } - - /* profile set default create */ - { - const timeouts = {}; - event_registry.on("set-default-profile", event => { - clearTimeout(timeouts[event.profile_id]); - timeouts[event.profile_id] = setTimeout(() => { - event_registry.fire("set-default-profile-result", { old_profile_id: event.profile_id, status: "timeout" }); - }, 5000); - }); - - event_registry.on("set-default-profile-result", event => { - clearTimeout(timeouts[event.old_profile_id]); - delete timeouts[event.old_profile_id]; - }); - } - - const create_standard_timeout = (event: keyof events.modal.settings.profiles, response_event: keyof events.modal.settings.profiles, key: string) => { - const timeouts = {}; - event_registry.on(event, event => { - clearTimeout(timeouts[event[key]]); - timeouts[event[key]] = setTimeout(() => { - const timeout_event = { status: "timeout" }; - timeout_event[key] = event[key]; - event_registry.fire(response_event, timeout_event as any); - }, 5000); - }); - - event_registry.on(response_event, event => { - clearTimeout(timeouts[event[key]]); - delete timeouts[event[key]]; - }); - }; - - create_standard_timeout("query-profile", "query-profile-result", "profile_id"); - create_standard_timeout("query-identity-teamspeak", "query-identity-teamspeak-result", "profile_id"); - create_standard_timeout("delete-profile", "delete-profile-result", "profile_id"); - create_standard_timeout("set-profile-name", "set-profile-name-result", "profile_id"); - create_standard_timeout("set-default-name", "set-default-name-result", "profile_id"); - create_standard_timeout("query-profile-validity", "query-profile-validity-result", "profile_id"); - create_standard_timeout("set-identity-name-name", "set-identity-name-name-result", "profile_id"); - create_standard_timeout("generate-identity-teamspeak", "generate-identity-teamspeak-result", "profile_id"); - } - - /* some view semantics */ - { - let selected_profile; - event_registry.on("delete-profile-result", event => { - if(event.status !== "success") return; - if(event.profile_id !== selected_profile) return; - - /* the selected profile has been deleted, so we need to select another one */ - event_registry.fire("select-profile", { profile_id: "default" }); - }); - - /* reselect the default profile or the new default profile */ - event_registry.on("set-default-profile-result", event => { - if(event.status !== "success") return; - if(selected_profile === "default") - event_registry.fire("select-profile", { profile_id: event.new_profile_id }); - else if(selected_profile === event.old_profile_id) - event_registry.fire("select-profile", { profile_id: "default" }); - }); - - event_registry.on("select-profile", event => { - selected_profile = event.profile_id; - event_registry.fire("query-profile", { profile_id: event.profile_id }); - }); - - event_registry.on("reload-profile", event => { - event_registry.fire("query-profile-list"); - event_registry.fire("select-profile", event.profile_id || selected_profile); - }); - } - - event_registry.fire("query-profile-list"); - event_registry.fire("select-profile", { profile_id: "default" }); - event_registry.fire("select-identity-type", { profile_id: "default", identity_type: undefined }); - } - - export function initialize_audio_microphone_controller(event_registry: events.Registry) { - /* level meters */ - { - const level_meters: {[key: string]:Promise} = {}; - const level_info: {[key: string]:any} = {}; - let level_update_task; - - const destroy_meters = () => { - Object.keys(level_meters).forEach(e => { - const meter = level_meters[e]; - delete level_meters[e]; - - meter.then(e => e.destory()); - }); - Object.keys(level_info).forEach(e => delete level_info[e]); - }; - - const update_level_meter = () => { - destroy_meters(); - - for(const device of audio.recorder.devices()) { - let promise = audio.recorder.create_levelmeter(device).then(meter => { - meter.set_observer(level => { - if(level_meters[device.unique_id] !== promise) return; /* old level meter */ - - level_info[device.unique_id] = { - device_id: device.unique_id, - status: "success", - level: level - }; - }); - return Promise.resolve(meter); - }).catch(error => { - if(level_meters[device.unique_id] !== promise) return; /* old level meter */ - level_info[device.unique_id] = { - device_id: device.unique_id, - status: "error", - - error: error - }; - - log.warn(LogCategory.AUDIO, tr("Failed to initialize a level meter for device %s (%s): %o"), device.unique_id, device.driver + ":" + device.name, error); - return Promise.reject(error); - }); - level_meters[device.unique_id] = promise; - } - }; - - level_update_task = setInterval(() => { - event_registry.fire("update-device-level", { - devices: Object.keys(level_info).map(e => level_info[e]) - }); - }, 50); - - event_registry.on("query-device-result", event => { - if(event.status !== "success") return; - - update_level_meter(); - }); - - event_registry.on("deinitialize", event => { - destroy_meters(); - clearInterval(level_update_task); - }); - } - - /* device list */ - { - event_registry.on("query-devices", event => { - Promise.resolve().then(() => { - return audio.recorder.device_refresh_available() && event.refresh_list ? audio.recorder.refresh_devices() : Promise.resolve(); - }).catch(error => { - log.warn(LogCategory.AUDIO, tr("Failed to refresh device list: %o"), error); - return Promise.resolve(); - }).then(() => { - const devices = audio.recorder.devices(); - - event_registry.fire_async("query-device-result", { - status: "success", - active_device: default_recorder.current_device() ? default_recorder.current_device().unique_id : "none", - devices: devices.map(e => { return { id: e.unique_id, name: e.name, driver: e.driver }}) - }); - }); - }); - event_registry.on("set-device", event => { - const device = audio.recorder.devices().find(e => e.unique_id === event.device_id); - if(!device && event.device_id !== "none") { - event_registry.fire_async("set-device-result", { status: "error", error: tr("Invalid device id"), device_id: event.device_id }); + pending_changes++; + + const default_device = container_devices.find(".selected"); + default_device_id = default_device.attr("device-id"); + default_device.removeClass("selected"); + + const new_device = container_devices.find(".device[device-id='" + event.device_id + "']"); + new_device.addClass("loading"); + }); + event_registry.on("set-device-result", event => { + pending_changes--; + container_devices.find(".loading").removeClass("loading"); + + if(event.status !== "success") { + createErrorModal(tr("Failed to change microphone"), formatMessage(tr("Failed to change the microphone to the target microphone{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); + } else { + default_device_id = event.device_id; + } + + container_devices.find(".device[device-id='" + default_device_id + "']").addClass("selected"); + }); + + event_registry.on('query-devices', event => { + Object.keys(volume_bar_tags).forEach(e => delete volume_bar_tags[e]); + container_devices.find(".device").remove(); + container_devices.find(".overlay").hide(); + container_devices.find(".overlay.overlay-loading").show(); + }); + + event_registry.on("query-device-result", event => { + container_devices.find(".device").remove(); + container_devices.find(".overlay").hide(); + + if(event.status !== "success") { + const container_text = container_devices.find(".overlay.overlay-error").show().find(".error-text"); + container_text.text(event.status === "timeout" ? tr("Timeout while loading") : event.error || tr("An unknown error happened")); return; } - default_recorder.set_device(device).then(() => { - console.debug(tr("Changed default microphone device")); - event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); - }).catch((error) => { - log.warn(LogCategory.AUDIO, tr("Failed to change microphone to device %s: %o"), device ? device.unique_id : "none", error) - event_registry.fire_async("set-device-result", { status: "success", device_id: event.device_id }); - }); - }); - } - - /* settings */ - { - event_registry.on("query-settings", event => { - event_registry.fire_async("query-settings-result", { - status: "success", - info: { - volume: default_recorder.get_volume(), - vad_type: default_recorder.get_vad_type(), - vad_ppt: { - key: default_recorder.get_vad_ppt_key(), - release_delay: Math.abs(default_recorder.get_vad_ppt_delay()), - release_delay_active: default_recorder.get_vad_ppt_delay() >= 0 - }, - vad_threshold: { - threshold: default_recorder.get_vad_threshold() - } - } - }); + build_device(undefined, event.active_device === "none").appendTo(container_devices); + for(const device of event.devices) + build_device(device, event.active_device === device.id).appendTo(container_devices); }); - event_registry.on("set-setting", event => { - const ensure_type = (type: "object" | "string" | "boolean" | "number" | "undefined") => { - if(typeof event.value !== type) { - event_registry.fire_async("set-setting-result", { status: "error", error: tr("Invalid value type for key") + " (expected: " + type + ", received: " + typeof event.value + ")", setting: event.setting }); - return false; - } - return true; - }; + event_registry.on("update-device-level", event => { + for(const device of event.devices) { + const tags = volume_bar_tags[device.device_id]; + if(!tags) continue; - switch (event.setting) { - case "volume": - if(!ensure_type("number")) return; - default_recorder.set_volume(event.value); - break; - - case "threshold-threshold": - if(!ensure_type("number")) return; - default_recorder.set_vad_threshold(event.value); - break; - - case "vad-type": - if(!ensure_type("string")) return; - if(!default_recorder.set_vad_type(event.value)) { - event_registry.fire_async("set-setting-result", { status: "error", error: tr("Unknown VAD type"), setting: event.setting }); - return; - } - break; - - case "ppt-key": - if(!ensure_type("object")) return; - default_recorder.set_vad_ppt_key(event.value); - break; - - case "ppt-release-delay": - if(!ensure_type("number")) return; - const sign = default_recorder.get_vad_ppt_delay() >= 0 ? 1 : -1; - default_recorder.set_vad_ppt_delay(sign * event.value); - break; - - case "ppt-release-delay-active": - if(!ensure_type("boolean")) return; - default_recorder.set_vad_ppt_delay(Math.abs(default_recorder.get_vad_ppt_delay()) * (event.value ? 1 : -1)); - break; - - default: - event_registry.fire_async("set-setting-result", { status: "error", error: tr("Invalid setting key"), setting: event.setting }); - return; + let level = typeof device.level === "number" ? device.level : 100; + if(level > 100) level = 100; + else if(level < 0) level = 0; + tags.error.attr('title', device.error || null).text(device.error || null); + tags.volume.css('width', (100 - level) + '%'); } - event_registry.fire_async("set-setting-result", { status: "success", setting: event.setting, value: event.value }); }); } - audio.player.on_ready(() => event_registry.fire_async("audio-initialized", {})); - } - export function initialize_audio_microphone_view(container: JQuery, event_registry: events.Registry) { - /* device list */ + /* device list update button */ { - /* actual list */ - { - const container_devices = container.find(".container-devices"); - const volume_bar_tags: {[key: string]:{ volume: JQuery, error: JQuery }} = {}; - let pending_changes = 0; - let default_device_id; - const build_device = (device: { id: string, name: string, driver: string }, selected: boolean) => { - let tag_volume: JQuery, tag_volume_error: JQuery; - const tag = $.spawn("div").attr("device-id", device ? device.id : "none").addClass("device").toggleClass("selected", selected).append( - $.spawn("div").addClass("container-selected").append( - $.spawn("div").addClass("icon_em client-apply"), - $.spawn("div").addClass("icon-loading").append( - $.spawn("img").attr("src", "img/icon_settings_loading.svg") - ) - ), - $.spawn("div").addClass("container-name").append( - $.spawn("div").addClass("device-driver").text( - device ? (device.driver || "Unknown driver") : "No device" - ), - $.spawn("div").addClass("device-name").text( - device ? (device.name || "Unknown name") : "No device" - ), - ), - $.spawn("div").addClass("container-activity").append( - $.spawn("div").addClass("container-activity-bar").append( - tag_volume = $.spawn("div").addClass("bar-hider"), - tag_volume_error = $.spawn("div").addClass("bar-error") - ) - ) - ); - tag_volume.css('width', '100%'); /* initially hide the bar */ - if(device) - volume_bar_tags[device.id] = { volume: tag_volume, error: tag_volume_error }; + const button_update = container.find(".button-update"); + event_registry.on(["query-devices", "set-device"], event => button_update.prop("disabled", true)); + event_registry.on(["query-device-result", "set-device-result"], event => button_update.prop("disabled", false)); - tag.on('click', event => { - if(tag.hasClass("selected") || pending_changes > 0) return; + button_update.on("click", event => event_registry.fire("query-devices", { refresh_list: true })); + } + } - event_registry.fire("set-device", { device_id: device ? device.id : "none" }); - }); + /* settings */ + { + /* TODO: Query settings error handling */ - return tag; - }; + /* volume */ + { + const container_volume = container.find(".container-volume"); + const slider_tag = container_volume.find(".container-slider"); + let triggered_events = 0; + let last_value = -1; - event_registry.on("set-device", event => { - pending_changes++; + const slider = sliderfy(slider_tag, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: 0 + }); - const default_device = container_devices.find(".selected"); - default_device_id = default_device.attr("device-id"); - default_device.removeClass("selected"); + slider_tag.on('change', event => { + const value = parseInt(slider_tag.attr("value")); + if(last_value === value) return; - const new_device = container_devices.find(".device[device-id='" + event.device_id + "']"); - new_device.addClass("loading"); - }); - event_registry.on("set-device-result", event => { - pending_changes--; - container_devices.find(".loading").removeClass("loading"); + triggered_events++; + event_registry.fire("set-setting", { setting: "volume", value: value }); + }); - if(event.status !== "success") { - createErrorModal(tr("Failed to change microphone"), MessageHelper.formatMessage(tr("Failed to change the microphone to the target microphone{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); - } else { - default_device_id = event.device_id; - } + event_registry.on("query-settings-result", event => { + if(event.status !== "success") return; - container_devices.find(".device[device-id='" + default_device_id + "']").addClass("selected"); - }); + last_value = event.info.volume; + slider.value(event.info.volume); + }); - event_registry.on('query-devices', event => { - Object.keys(volume_bar_tags).forEach(e => delete volume_bar_tags[e]); - container_devices.find(".device").remove(); - container_devices.find(".overlay").hide(); - container_devices.find(".overlay.overlay-loading").show(); - }); + event_registry.on("set-setting-result", event => { + if(event.setting !== "volume") return; + if(triggered_events > 0) { + triggered_events--; + return; + } + if(event.status !== "success") return; - event_registry.on("query-device-result", event => { - container_devices.find(".device").remove(); - container_devices.find(".overlay").hide(); - - if(event.status !== "success") { - const container_text = container_devices.find(".overlay.overlay-error").show().find(".error-text"); - container_text.text(event.status === "timeout" ? tr("Timeout while loading") : event.error || tr("An unknown error happened")); - return; - } - - build_device(undefined, event.active_device === "none").appendTo(container_devices); - for(const device of event.devices) - build_device(device, event.active_device === device.id).appendTo(container_devices); - }); - - event_registry.on("update-device-level", event => { - for(const device of event.devices) { - const tags = volume_bar_tags[device.device_id]; - if(!tags) continue; - - let level = typeof device.level === "number" ? device.level : 100; - if(level > 100) level = 100; - else if(level < 0) level = 0; - tags.error.attr('title', device.error || null).text(device.error || null); - tags.volume.css('width', (100 - level) + '%'); - } - }); - } - - /* device list update button */ - { - - const button_update = container.find(".button-update"); - event_registry.on(["query-devices", "set-device"], event => button_update.prop("disabled", true)); - event_registry.on(["query-device-result", "set-device-result"], event => button_update.prop("disabled", false)); - - button_update.on("click", event => event_registry.fire("query-devices", { refresh_list: true })); - } + last_value = event.value; + slider.value(event.value); + }); } - /* settings */ + /* vad type */ { - /* TODO: Query settings error handling */ + const container_select = container.find(".container-select-vad"); + let last_value; - /* volume */ - { - const container_volume = container.find(".container-volume"); - const slider_tag = container_volume.find(".container-slider"); - let triggered_events = 0; - let last_value = -1; + container_select.find("input").on('change', event => { + if(!(event.target).checked) + return; - const slider = sliderfy(slider_tag, { - min_value: 0, - max_value: 100, - step: 1, - initial_value: 0 - }); + const mode = (event.target).value; + if(mode === last_value) return; - slider_tag.on('change', event => { - const value = parseInt(slider_tag.attr("value")); - if(last_value === value) return; + event_registry.fire("set-setting", { setting: "vad-type", value: mode }); + }); - triggered_events++; - event_registry.fire("set-setting", { setting: "volume", value: value }); - }); + const select_vad_type = type => { + let elements = container_select.find('input[value="' + type + '"]'); + if(elements.length < 1) + elements = container_select.find('input[value]'); + elements.first().trigger('click'); + }; - event_registry.on("query-settings-result", event => { + event_registry.on("query-settings-result", event => { + if(event.status !== "success") return; + + last_value = event.info.vad_type; + select_vad_type(event.info.vad_type); + }); + + event_registry.on("set-setting-result", event => { + if(event.setting !== "vad-type") return; + if(event.status !== "success") { + createErrorModal(tr("Failed to change setting"), formatMessage(tr("Failed to change vad type{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); + } else { + last_value = event.value; + } + + select_vad_type(last_value); + }); + } + + /* Sensitivity */ + { + const container_sensitivity = container.find(".container-sensitivity"); + + const container_bar = container_sensitivity.find(".container-activity-bar"); + const bar_hider = container_bar.find(".bar-hider"); + + let last_value; + let triggered_events = 0; + let enabled; + + const slider = sliderfy(container_bar, { + min_value: 0, + max_value: 100, + step: 1, + initial_value: 0 + }); + + const set_enabled = value => { + if(enabled === value) return; + + enabled = value; + container_sensitivity.toggleClass("disabled", !value); + }; + + container_bar.on('change', event => { + const value = parseInt(container_bar.attr("value")); + if(last_value === value) return; + + triggered_events++; + event_registry.fire("set-setting", { setting: "threshold-threshold", value: value }); + }); + + event_registry.on("query-settings", event => set_enabled(false)); + event_registry.on("query-settings-result", event => { + if(event.status !== "success") return; + + last_value = event.info.vad_threshold.threshold; + slider.value(event.info.vad_threshold.threshold); + set_enabled(event.info.vad_type === "threshold"); + }); + + event_registry.on("set-setting-result", event => { + if(event.setting === "threshold-threshold") { if(event.status !== "success") return; - last_value = event.info.volume; - slider.value(event.info.volume); - }); - - event_registry.on("set-setting-result", event => { - if(event.setting !== "volume") return; if(triggered_events > 0) { triggered_events--; return; } - if(event.status !== "success") return; last_value = event.value; slider.value(event.value); - }); - } + } else if(event.setting === "vad-type") { + if(event.status !== "success") return; - /* vad type */ + set_enabled(event.value === "threshold"); + } + }); + + let selected_device; + event_registry.on("query-device-result", event => { + if(event.status !== "success") return; + + selected_device = event.active_device; + }); + event_registry.on("set-device-result", event => { + if(event.status !== "success") return; + + selected_device = event.device_id; + }); + + + bar_hider.css("width", "100%"); + event_registry.on("update-device-level", event => { + if(!enabled) return; + + const data = event.devices.find(e => e.device_id === selected_device); + let level = data && typeof data.level === "number" ? data.level : 0; + if(level > 100) level = 100; + else if(level < 0) level = 0; + + bar_hider.css("width", (100 - level) + "%"); + }); + + set_enabled(false); + } + + /* ppt settings */ + { + /* PPT Key */ { - const container_select = container.find(".container-select-vad"); + const button_key = container.find(".container-ppt button"); + event_registry.on("query-settings", event => button_key.prop("disabled", true).text(tr("loading"))); let last_value; - container_select.find("input").on('change', event => { - if(!(event.target).checked) - return; - - const mode = (event.target).value; - if(mode === last_value) return; - - event_registry.fire("set-setting", { setting: "vad-type", value: mode }); - }); - - const select_vad_type = type => { - let elements = container_select.find('input[value="' + type + '"]'); - if(elements.length < 1) - elements = container_select.find('input[value]'); - elements.first().trigger('click'); - }; - event_registry.on("query-settings-result", event => { if(event.status !== "success") return; - last_value = event.info.vad_type; - select_vad_type(event.info.vad_type); + button_key.prop('disabled', event.info.vad_type !== "push_to_talk"); + button_key.text(last_value = key_description(event.info.vad_ppt.key)); + }); + + + event_registry.on("set-setting", event => { + if(event.setting !== "ppt-key") return; + + button_key.prop("enabled", false); + button_key.text(tr("applying")); }); event_registry.on("set-setting-result", event => { - if(event.setting !== "vad-type") return; - if(event.status !== "success") { - createErrorModal(tr("Failed to change setting"), MessageHelper.formatMessage(tr("Failed to change vad type{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); - } else { - last_value = event.value; - } - - select_vad_type(last_value); - }); - } - - /* Sensitivity */ - { - const container_sensitivity = container.find(".container-sensitivity"); - - const container_bar = container_sensitivity.find(".container-activity-bar"); - const bar_hider = container_bar.find(".bar-hider"); - - let last_value; - let triggered_events = 0; - let enabled; - - const slider = sliderfy(container_bar, { - min_value: 0, - max_value: 100, - step: 1, - initial_value: 0 - }); - - const set_enabled = value => { - if(enabled === value) return; - - enabled = value; - container_sensitivity.toggleClass("disabled", !value); - }; - - container_bar.on('change', event => { - const value = parseInt(container_bar.attr("value")); - if(last_value === value) return; - - triggered_events++; - event_registry.fire("set-setting", { setting: "threshold-threshold", value: value }); - }); - - event_registry.on("query-settings", event => set_enabled(false)); - event_registry.on("query-settings-result", event => { - if(event.status !== "success") return; - - last_value = event.info.vad_threshold.threshold; - slider.value(event.info.vad_threshold.threshold); - set_enabled(event.info.vad_type === "threshold"); - }); - - event_registry.on("set-setting-result", event => { - if(event.setting === "threshold-threshold") { + if(event.setting === "vad-type") { if(event.status !== "success") return; - if(triggered_events > 0) { - triggered_events--; - return; + button_key.prop('disabled', event.value !== "push_to_talk"); + } else if(event.setting === "ppt-key") { + if(event.status !== "success") { + createErrorModal(tr("Failed to change PPT key"), formatMessage(tr("Failed to change PPT key:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); + } else { + last_value = key_description(event.value); } - - last_value = event.value; - slider.value(event.value); - } else if(event.setting === "vad-type") { - if(event.status !== "success") return; - - set_enabled(event.value === "threshold"); + button_key.text(last_value); } }); - let selected_device; - event_registry.on("query-device-result", event => { - if(event.status !== "success") return; + button_key.on('click', event => { + spawnKeySelect(key => { + if(!key) return; - selected_device = event.active_device; + event_registry.fire("set-setting", { setting: "ppt-key", value: key }); + }); }); - event_registry.on("set-device-result", event => { - if(event.status !== "success") return; - - selected_device = event.device_id; - }); - - - bar_hider.css("width", "100%"); - event_registry.on("update-device-level", event => { - if(!enabled) return; - - const data = event.devices.find(e => e.device_id === selected_device); - let level = data && typeof data.level === "number" ? data.level : 0; - if(level > 100) level = 100; - else if(level < 0) level = 0; - - bar_hider.css("width", (100 - level) + "%"); - }); - - set_enabled(false); } - /* ppt settings */ + /* delay */ { - /* PPT Key */ - { - const button_key = container.find(".container-ppt button"); - event_registry.on("query-settings", event => button_key.prop("disabled", true).text(tr("loading"))); - let last_value; + const container_delay = container.find(".container-ppt-delay"); + /* toggle button */ + { + const input_enabled = container_delay.find("input.delay-enabled"); + const update_enabled_state = () => { + const value = !loading && !applying && ppt_selected; + input_enabled.prop("disabled", !value).parent().toggleClass("disabled", !value); + }; + + let last_state; + let loading = true, applying = false, ppt_selected = false; + + event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); event_registry.on("query-settings-result", event => { if(event.status !== "success") return; - button_key.prop('disabled', event.info.vad_type !== "push_to_talk"); - button_key.text(last_value = ppt.key_description(event.info.vad_ppt.key)); + loading = false; + ppt_selected = event.info.vad_type === "push_to_talk"; + update_enabled_state(); + input_enabled.prop("checked", last_state = event.info.vad_ppt.release_delay_active); }); - event_registry.on("set-setting", event => { - if(event.setting !== "ppt-key") return; + if(event.setting !== "ppt-release-delay-active") return; - button_key.prop("enabled", false); - button_key.text(tr("applying")); + applying = true; + update_enabled_state(); }); event_registry.on("set-setting-result", event => { if(event.setting === "vad-type") { if(event.status !== "success") return; - button_key.prop('disabled', event.value !== "push_to_talk"); - } else if(event.setting === "ppt-key") { + ppt_selected = event.value === "push_to_talk"; + update_enabled_state(); + } else if(event.setting === "ppt-release-delay-active") { + applying = false; + update_enabled_state(); + if(event.status !== "success") { - createErrorModal(tr("Failed to change PPT key"), MessageHelper.formatMessage(tr("Failed to change PPT key:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); + createErrorModal(tr("Failed to change PPT delay state"), formatMessage(tr("Failed to change PPT delay state:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); } else { - last_value = ppt.key_description(event.value); + last_state = event.value; } - button_key.text(last_value); + input_enabled.prop("checked", last_state); } }); - button_key.on('click', event => { - Modals.spawnKeySelect(key => { - if(!key) return; - - event_registry.fire("set-setting", { setting: "ppt-key", value: key }); - }); + input_enabled.on('change', event => { + event_registry.fire("set-setting", { setting: "ppt-release-delay-active", value: input_enabled.prop("checked") }); }); } - /* delay */ + /* delay input */ { - const container_delay = container.find(".container-ppt-delay"); + const input_time = container_delay.find("input.delay-time"); + const update_enabled_state = () => { + const value = !loading && !applying && ppt_selected && delay_active; + input_time.prop("disabled", !value).parent().toggleClass("disabled", !value); + }; - /* toggle button */ - { - const input_enabled = container_delay.find("input.delay-enabled"); - const update_enabled_state = () => { - const value = !loading && !applying && ppt_selected; - input_enabled.prop("disabled", !value).parent().toggleClass("disabled", !value); - }; + let last_state; + let loading = true, applying = false, ppt_selected = false, delay_active = false; - let last_state; - let loading = true, applying = false, ppt_selected = false; + event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); + event_registry.on("query-settings-result", event => { + if(event.status !== "success") return; - event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); - event_registry.on("query-settings-result", event => { + loading = false; + ppt_selected = event.info.vad_type === "push_to_talk"; + delay_active = event.info.vad_ppt.release_delay_active; + update_enabled_state(); + input_time.val(last_state = event.info.vad_ppt.release_delay); + }); + + event_registry.on("set-setting", event => { + if(event.setting !== "ppt-release-delay") return; + + applying = true; + update_enabled_state(); + }); + + event_registry.on("set-setting-result", event => { + if(event.setting === "vad-type") { if(event.status !== "success") return; - loading = false; - ppt_selected = event.info.vad_type === "push_to_talk"; + ppt_selected = event.value === "push_to_talk"; update_enabled_state(); - input_enabled.prop("checked", last_state = event.info.vad_ppt.release_delay_active); - }); - - event_registry.on("set-setting", event => { - if(event.setting !== "ppt-release-delay-active") return; - - applying = true; - update_enabled_state(); - }); - - event_registry.on("set-setting-result", event => { - if(event.setting === "vad-type") { - if(event.status !== "success") return; - - ppt_selected = event.value === "push_to_talk"; - update_enabled_state(); - } else if(event.setting === "ppt-release-delay-active") { - applying = false; - update_enabled_state(); - - if(event.status !== "success") { - createErrorModal(tr("Failed to change PPT delay state"), MessageHelper.formatMessage(tr("Failed to change PPT delay state:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); - } else { - last_state = event.value; - } - input_enabled.prop("checked", last_state); - } - }); - - input_enabled.on('change', event => { - event_registry.fire("set-setting", { setting: "ppt-release-delay-active", value: input_enabled.prop("checked") }); - }); - } - - /* delay input */ - { - const input_time = container_delay.find("input.delay-time"); - const update_enabled_state = () => { - const value = !loading && !applying && ppt_selected && delay_active; - input_time.prop("disabled", !value).parent().toggleClass("disabled", !value); - }; - - let last_state; - let loading = true, applying = false, ppt_selected = false, delay_active = false; - - event_registry.on("query-settings", event => { loading = true; update_enabled_state(); }); - event_registry.on("query-settings-result", event => { + } else if(event.setting === "ppt-release-delay-active") { if(event.status !== "success") return; - loading = false; - ppt_selected = event.info.vad_type === "push_to_talk"; - delay_active = event.info.vad_ppt.release_delay_active; + delay_active = event.value; update_enabled_state(); - input_time.val(last_state = event.info.vad_ppt.release_delay); - }); - - event_registry.on("set-setting", event => { - if(event.setting !== "ppt-release-delay") return; - - applying = true; + } else if(event.setting === "ppt-release-delay") { + applying = false; update_enabled_state(); - }); - event_registry.on("set-setting-result", event => { - if(event.setting === "vad-type") { - if(event.status !== "success") return; - - ppt_selected = event.value === "push_to_talk"; - update_enabled_state(); - } else if(event.setting === "ppt-release-delay-active") { - if(event.status !== "success") return; - - delay_active = event.value; - update_enabled_state(); - } else if(event.setting === "ppt-release-delay") { - applying = false; - update_enabled_state(); - - if(event.status !== "success") { - createErrorModal(tr("Failed to change PPT delay"), MessageHelper.formatMessage(tr("Failed to change PPT delay:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); - } else { - last_state = event.value; - } - input_time.val(last_state); + if(event.status !== "success") { + createErrorModal(tr("Failed to change PPT delay"), formatMessage(tr("Failed to change PPT delay:{:br:}{}"), event.status === "timeout" ? tr("Timeout") : event.error || tr("Unknown error"))).open(); + } else { + last_state = event.value; } - }); + input_time.val(last_state); + } + }); - input_time.on('change', event => { - event_registry.fire("set-setting", { setting: "ppt-release-delay", value: parseInt(input_time.val() as any) }); - }); - } + input_time.on('change', event => { + event_registry.fire("set-setting", { setting: "ppt-release-delay", value: parseInt(input_time.val() as any) }); + }); } } } - - /* timeouts */ - { - /* device query */ - { - let timeout; - event_registry.on('query-devices', event => { - clearTimeout(timeout); - timeout = setTimeout(() => { - event_registry.fire("query-device-result", { status: "timeout" }); - }, 5000); - }); - - event_registry.on("query-device-result", event => clearTimeout(timeout)); - } - - /* device set */ - { - let timeouts = {}; - event_registry.on('set-device', event => { - clearTimeout(timeouts[event.device_id]); - timeouts[event.device_id] = setTimeout(() => { - event_registry.fire("set-device-result", { status: "timeout", device_id: event.device_id }); - }, 5000); - }); - - event_registry.on("set-device-result", event => clearTimeout(timeouts[event.device_id])); - } - - /* settings query */ - { - let timeout; - event_registry.on('query-settings', event => { - clearTimeout(timeout); - timeout = setTimeout(() => { - event_registry.fire("query-settings-result", { status: "timeout" }); - }, 5000); - }); - - event_registry.on("query-settings-result", event => clearTimeout(timeout)); - } - - /* settings change */ - { - let timeouts = {}; - event_registry.on('set-setting', event => { - clearTimeout(timeouts[event.setting]); - timeouts[event.setting] = setTimeout(() => { - event_registry.fire("set-setting-result", { status: "timeout", setting: event.setting }); - }, 5000); - }); - - event_registry.on("set-setting-result", event => clearTimeout(timeouts[event.setting])); - } - } - - event_registry.on("audio-initialized", () => { - event_registry.fire("query-settings"); - event_registry.fire("query-devices", { refresh_list: false }); - }); } - } - function settings_identity_forum(container: JQuery, modal: Modal, update_profiles: () => any) { - const containers_connected = container.find(".show-connected"); - const containers_disconnected = container.find(".show-disconnected"); + /* timeouts */ + { + /* device query */ + { + let timeout; + event_registry.on('query-devices', event => { + clearTimeout(timeout); + timeout = setTimeout(() => { + event_registry.fire("query-device-result", { status: "timeout" }); + }, 5000); + }); - const update_state = () => { - const logged_in = forum.logged_in(); - containers_connected.toggle(logged_in); - containers_disconnected.toggle(!logged_in); - - if(logged_in) { - container.find(".forum-username").text(forum.data().name()); - container.find(".forum-premium").text(forum.data().is_premium() ? tr("Yes") : tr("No")); + event_registry.on("query-device-result", event => clearTimeout(timeout)); } + + /* device set */ + { + let timeouts = {}; + event_registry.on('set-device', event => { + clearTimeout(timeouts[event.device_id]); + timeouts[event.device_id] = setTimeout(() => { + event_registry.fire("set-device-result", { status: "timeout", device_id: event.device_id }); + }, 5000); + }); + + event_registry.on("set-device-result", event => clearTimeout(timeouts[event.device_id])); + } + + /* settings query */ + { + let timeout; + event_registry.on('query-settings', event => { + clearTimeout(timeout); + timeout = setTimeout(() => { + event_registry.fire("query-settings-result", { status: "timeout" }); + }, 5000); + }); + + event_registry.on("query-settings-result", event => clearTimeout(timeout)); + } + + /* settings change */ + { + let timeouts = {}; + event_registry.on('set-setting', event => { + clearTimeout(timeouts[event.setting]); + timeouts[event.setting] = setTimeout(() => { + event_registry.fire("set-setting-result", { status: "timeout", setting: event.setting }); + }, 5000); + }); + + event_registry.on("set-setting-result", event => clearTimeout(timeouts[event.setting])); + } + } + + event_registry.on("audio-initialized", () => { + event_registry.fire("query-settings"); + event_registry.fire("query-devices", { refresh_list: false }); + }); + } +} + +function settings_identity_forum(container: JQuery, modal: Modal, update_profiles: () => any) { + const containers_connected = container.find(".show-connected"); + const containers_disconnected = container.find(".show-disconnected"); + + const update_state = () => { + const logged_in = forum.logged_in(); + containers_connected.toggle(logged_in); + containers_disconnected.toggle(!logged_in); + + if(logged_in) { + container.find(".forum-username").text(forum.data().name()); + container.find(".forum-premium").text(forum.data().is_premium() ? tr("Yes") : tr("No")); + } + }; + + /* login */ + { + const button_login = container.find(".button-login"); + const input_username = container.find(".input-username"); + const input_password = container.find(".input-password"); + const container_error = container.find(".container-login .container-error"); + + const container_captcha_g = container.find(".g-recaptcha"); + let captcha: boolean | string = false; + + const update_button_state = () => { + let enabled = true; + enabled = enabled && !!input_password.val(); + enabled = enabled && !!input_username.val(); + enabled = enabled && (typeof(captcha) === "boolean" ? !captcha : !!captcha); + button_login.prop("disabled", !enabled); }; - /* login */ - { - const button_login = container.find(".button-login"); - const input_username = container.find(".input-username"); - const input_password = container.find(".input-password"); - const container_error = container.find(".container-login .container-error"); + /* username */ + input_username.on('change keyup', update_button_state); - const container_captcha_g = container.find(".g-recaptcha"); - let captcha: boolean | string = false; + /* password */ + input_password.on('change keyup', update_button_state); - const update_button_state = () => { - let enabled = true; - enabled = enabled && !!input_password.val(); - enabled = enabled && !!input_username.val(); - enabled = enabled && (typeof(captcha) === "boolean" ? !captcha : !!captcha); - button_login.prop("disabled", !enabled); - }; + button_login.on('click', event => { + input_username.prop("disabled", true); + input_password.prop("disabled", true); + button_login.prop("disabled", true); + container_error.removeClass("shown"); - /* username */ - input_username.on('change keyup', update_button_state); + forum.login(input_username.val() as string, input_password.val() as string, typeof(captcha) === "string" ? captcha : undefined).then(state => { + captcha = false; - /* password */ - input_password.on('change keyup', update_button_state); - - button_login.on('click', event => { - input_username.prop("disabled", true); - input_password.prop("disabled", true); - button_login.prop("disabled", true); - container_error.removeClass("shown"); - - forum.login(input_username.val() as string, input_password.val() as string, typeof(captcha) === "string" ? captcha : undefined).then(state => { - captcha = false; - - console.debug(tr("Forum login result: %o"), state); - if(state.status === "success") { - update_state(); - update_profiles(); - return; - } - - setTimeout(() => { - if(!!state.error_message) /* clear password if we have an error */ - input_password.val(""); - input_password.focus(); - update_button_state(); - }, 0); - if(state.status === "captcha") { - //TODO Works currently only with localhost! - button_login.hide(); - container_error.text(state.error_message || tr("Captcha required")).addClass("shown"); - - captcha = ""; - - console.log(tr("Showing captcha for site-key: %o"), state.captcha.data); - forum.gcaptcha.spawn(container_captcha_g, state.captcha.data, token => { - captcha = token; - console.debug(tr("Got captcha token: %o"), token); - container_captcha_g.hide(); - button_login.show(); - update_button_state(); - }).catch(error => { - console.error(tr("Failed to initialize forum captcha: %o"), error); - container_error.text("Failed to initialize GReCaptcha! No authentication possible.").addClass("shown"); - container_captcha_g.hide(); - button_login.hide(); - }); - container_captcha_g.show(); - } else { - container_error.text(state.error_message || tr("Unknown error")).addClass("shown"); - } - }).catch(error => { - console.error(tr("Failed to login within the forum. Error: %o"), error); - createErrorModal(tr("Forum login failed."), tr("Forum login failed. Lookup the console for more information")).open(); - }).then(() => { - input_username.prop("disabled", false); - input_password.prop("disabled", false); - update_button_state(); - }); - }); - update_button_state(); - } - - /* logout */ - { - container.find(".button-logout").on('click', event => { - forum.logout().catch(error => { - console.error(tr("Failed to logout from forum: %o"), error); - createErrorModal(tr("Forum logout failed"), MessageHelper.formatMessage(tr("Failed to logout from forum account.{:br:}Error: {}"), error)).open(); - }).then(() => { - if (modal.shown) - update_state(); + console.debug(tr("Forum login result: %o"), state); + if(state.status === "success") { + update_state(); update_profiles(); - }); - }); - } + return; + } - update_state(); + setTimeout(() => { + if(!!state.error_message) /* clear password if we have an error */ + input_password.val(""); + input_password.focus(); + update_button_state(); + }, 0); + if(state.status === "captcha") { + //TODO Works currently only with localhost! + button_login.hide(); + container_error.text(state.error_message || tr("Captcha required")).addClass("shown"); + + captcha = ""; + + console.log(tr("Showing captcha for site-key: %o"), state.captcha.data); + forum.gcaptcha.spawn(container_captcha_g, state.captcha.data, token => { + captcha = token; + console.debug(tr("Got captcha token: %o"), token); + container_captcha_g.hide(); + button_login.show(); + update_button_state(); + }).catch(error => { + console.error(tr("Failed to initialize forum captcha: %o"), error); + container_error.text("Failed to initialize GReCaptcha! No authentication possible.").addClass("shown"); + container_captcha_g.hide(); + button_login.hide(); + }); + container_captcha_g.show(); + } else { + container_error.text(state.error_message || tr("Unknown error")).addClass("shown"); + } + }).catch(error => { + console.error(tr("Failed to login within the forum. Error: %o"), error); + createErrorModal(tr("Forum login failed."), tr("Forum login failed. Lookup the console for more information")).open(); + }).then(() => { + input_username.prop("disabled", false); + input_password.prop("disabled", false); + update_button_state(); + }); + }); + update_button_state(); } + + /* logout */ + { + container.find(".button-logout").on('click', event => { + forum.logout().catch(error => { + console.error(tr("Failed to logout from forum: %o"), error); + createErrorModal(tr("Forum logout failed"), formatMessage(tr("Failed to logout from forum account.{:br:}Error: {}"), error)).open(); + }).then(() => { + if (modal.shown) + update_state(); + update_profiles(); + }); + }); + } + + update_state(); } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalYesNo.ts b/shared/js/ui/modal/ModalYesNo.ts index 4562d4ac..51be7aef 100644 --- a/shared/js/ui/modal/ModalYesNo.ts +++ b/shared/js/ui/modal/ModalYesNo.ts @@ -1,47 +1,45 @@ -/// +import {BodyCreator, createModal, ModalFunctions} from "tc-shared/ui/elements/Modal"; -namespace Modals { - export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: { - text_yes?: string, - text_no?: string, +export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: { + text_yes?: string, + text_no?: string, - closeable?: boolean; - }) { - properties = properties || {}; + closeable?: boolean; +}) { + properties = properties || {}; - const props = ModalFunctions.warpProperties({}); - props.template_properties || (props.template_properties = {}); - props.template_properties.text_yes = properties.text_yes || tr("Yes"); - props.template_properties.text_no = properties.text_no || tr("No"); - props.template = "#tmpl_modal_yesno"; + const props = ModalFunctions.warpProperties({}); + props.template_properties || (props.template_properties = {}); + props.template_properties.text_yes = properties.text_yes || tr("Yes"); + props.template_properties.text_no = properties.text_no || tr("No"); + props.template = "#tmpl_modal_yesno"; - props.header = header; - props.template_properties.question = ModalFunctions.jqueriefy(body); + props.header = header; + props.template_properties.question = ModalFunctions.jqueriefy(body); - props.closeable = typeof(properties.closeable) !== "boolean" || properties.closeable; - const modal = createModal(props); - let submited = false; - const button_yes = modal.htmlTag.find(".button-yes"); - const button_no = modal.htmlTag.find(".button-no"); + props.closeable = typeof(properties.closeable) !== "boolean" || properties.closeable; + const modal = createModal(props); + let submited = false; + const button_yes = modal.htmlTag.find(".button-yes"); + const button_no = modal.htmlTag.find(".button-no"); - button_yes.on('click', event => { - if(!submited) { - submited = true; - callback(true); - } - modal.close(); - }); + button_yes.on('click', event => { + if(!submited) { + submited = true; + callback(true); + } + modal.close(); + }); - button_no.on('click', event => { - if(!submited) { - submited = true; - callback(false); - } - modal.close(); - }); + button_no.on('click', event => { + if(!submited) { + submited = true; + callback(false); + } + modal.close(); + }); - modal.close_listener.push(() => button_no.trigger('click')); - modal.open(); - return modal; - } + modal.close_listener.push(() => button_no.trigger('click')); + modal.open(); + return modal; } \ No newline at end of file diff --git a/shared/js/ui/modal/permission/AbstractPermissionEditor.ts b/shared/js/ui/modal/permission/AbstractPermissionEditor.ts new file mode 100644 index 00000000..63101b0d --- /dev/null +++ b/shared/js/ui/modal/permission/AbstractPermissionEditor.ts @@ -0,0 +1,61 @@ +import {GroupedPermissions, PermissionInfo, PermissionValue} from "tc-shared/permission/PermissionManager"; +import PermissionType from "tc-shared/permission/PermissionType"; + +export enum PermissionEditorMode { + VISIBLE, + NO_PERMISSION, + UNSET +} + +export abstract class AbstractPermissionEditor { + protected _permissions: GroupedPermissions[]; + protected _listener_update: () => any; + protected _listener_change: ChangeListener = () => Promise.resolve(); + protected _toggle_callback: () => string; + + icon_resolver: (id: number) => Promise; + icon_selector: (current_id: number) => Promise; + + protected constructor() {} + + abstract set_mode(mode: PermissionEditorMode); + + abstract initialize(permissions: GroupedPermissions[]); + abstract html_tag() : JQuery; + abstract set_permissions(permissions?: PermissionValue[]); + abstract set_hidden_permissions(permissions: PermissionType[]); + + set_listener(listener?: ChangeListener) { + this._listener_change = listener || (() => Promise.resolve()); + } + + set_listener_update(listener?: () => any) { this._listener_update = listener; } + trigger_update() { if(this._listener_update) this._listener_update(); } + + abstract set_toggle_button(callback: () => string, initial: string); +} + +export interface PermissionEntry { + tag: JQuery; + tag_value: JQuery; + tag_grant: JQuery; + tag_flag_negate: JQuery; + tag_flag_skip: JQuery; + + id: number; + filter: string; + is_bool: boolean; + +} + +export interface ChangedPermissionValue { + remove: boolean; /* if set remove the set permission (value or granted) */ + + granted?: number; + value?: number; + + flag_skip?: boolean; + flag_negate?: boolean; +} + +export type ChangeListener = (permission: PermissionInfo, value?: ChangedPermissionValue) => Promise; \ No newline at end of file diff --git a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts index f2c5ecfd..4afced0e 100644 --- a/shared/js/ui/modal/permission/CanvasPermissionEditor.ts +++ b/shared/js/ui/modal/permission/CanvasPermissionEditor.ts @@ -1,1586 +1,1453 @@ /// /* first needs the AbstractPermissionEdit */ /* Canvas Permission Editor */ -namespace pe { - namespace ui { - export namespace scheme { - export interface CheckBox { - border: string; - checkmark: string; - checkmark_font: string; +import PermissionType from "tc-shared/permission/PermissionType"; +import {KeyCode} from "tc-shared/PPTListener"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {createInfoModal} from "tc-shared/ui/elements/Modal"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import {AbstractPermissionEditor, PermissionEditorMode} from "tc-shared/ui/modal/permission/AbstractPermissionEditor"; +import {GroupedPermissions, PermissionInfo, PermissionValue} from "tc-shared/permission/PermissionManager"; - background_checked: string; - background_checked_hovered: string; +namespace ui { + export namespace scheme { + export interface CheckBox { + border: string; + checkmark: string; + checkmark_font: string; + background_checked: string; + background_checked_hovered: string; + + background: string; + background_hovered: string; + } + + export interface TextField { + color: string; + font: string; + + background: string; + background_hovered: string; + } + + export interface ColorScheme { + permission: { background: string; - background_hovered: string; + background_selected: string; + + name: string; + name_unset: string; + name_font: string; + + value: TextField; + value_b: CheckBox; + granted: TextField; + negate: CheckBox; + skip: CheckBox; } - export interface TextField { - color: string; - font: string; - - background: string; - background_hovered: string; - } - - export interface ColorScheme { - permission: { - background: string; - background_selected: string; - - name: string; - name_unset: string; - name_font: string; - - value: TextField; - value_b: CheckBox; - granted: TextField; - negate: CheckBox; - skip: CheckBox; - } - - group: { - name: string; - name_font: string; - } - } - } - - export enum RepaintMode { - NONE, - REPAINT, - REPAINT_OBJECT_FULL, - REPAINT_FULL - } - - export interface AxisAlignedBoundingBox { - x: number; - y: number; - - width: number; - height: number; - } - - export enum ClickEventType { - SIGNLE, - DOUBLE, - CONTEXT_MENU - } - - export interface InteractionClickEvent { - type: ClickEventType; - consumed: boolean; - offset_x: number; - offset_y: number; - } - - export interface InteractionListener { - region: AxisAlignedBoundingBox; - region_weight: number; - - /** - * @return true if a redraw is required - */ - on_mouse_enter?: () => RepaintMode; - - /** - * @return true if a redraw is required - */ - on_mouse_leave?: () => RepaintMode; - - /** - * @return true if a redraw is required - */ - on_click?: (event: InteractionClickEvent) => RepaintMode; - - mouse_cursor?: string; - - set_full_draw?: () => any; - disabled?: boolean; - } - - abstract class DrawableObject { - abstract draw(context: CanvasRenderingContext2D, full: boolean); - - private _object_full_draw = false; - private _width: number = 0; - - set_width(value: number) { - this._width = value; - } - - request_full_draw() { - this._object_full_draw = true; - } - - pop_full_draw() { - const result = this._object_full_draw; - this._object_full_draw = false; - return result; - } - - width() { - return this._width; - } - - abstract height(); - - private _transforms: DOMMatrix[] = []; - - protected push_transform(context: CanvasRenderingContext2D) { - this._transforms.push(context.getTransform()); - } - - protected pop_transform(context: CanvasRenderingContext2D) { - const transform = this._transforms.pop(); - context.setTransform( - transform.a, - transform.b, - transform.c, - transform.d, - transform.e, - transform.f - ); - } - - protected original_x(context: CanvasRenderingContext2D, x: number) { - return context.getTransform().e + x; - } - - protected original_y(context: CanvasRenderingContext2D, y: number) { - return context.getTransform().f + y; - } - - protected colors: scheme.ColorScheme = {} as any; - - set_color_scheme(scheme: scheme.ColorScheme) { - this.colors = scheme; - } - - protected manager: PermissionEditor; - - set_manager(manager: PermissionEditor) { - this.manager = manager; - } - - abstract initialize(); - - abstract finalize(); - } - - class PermissionGroup extends DrawableObject { - public static readonly HEIGHT = parseFloat(getComputedStyle(document.documentElement).fontSize) * (3 / 2); /* 24 */ - public static readonly ARROW_SIZE = 10; /* 12 */ - - group: GroupedPermissions; - _sub_elements: PermissionGroup[] = []; - _element_permissions: PermissionList; - - collapsed = false; - private _listener_colaps: InteractionListener; - - constructor(group: GroupedPermissions) { - super(); - - this.group = group; - - this._element_permissions = new PermissionList(this.group.permissions); - for (const sub of this.group.children) - this._sub_elements.push(new PermissionGroup(sub)); - } - - draw(context: CanvasRenderingContext2D, full: boolean) { - const _full = this.pop_full_draw() || full; - this.push_transform(context); - context.translate(PermissionGroup.ARROW_SIZE + 20, PermissionGroup.HEIGHT); - - let sum_height = 0; - /* let first draw the elements, because if the sum height is zero then we could hide ourselves */ - if (!this.collapsed) { /* draw the next groups */ - for (const group of this._sub_elements) { - group.draw(context, full); - - const height = group.height(); - sum_height += height; - context.translate(0, height); - } - - this._element_permissions.draw(context, full); - if (sum_height == 0) - sum_height += this._element_permissions.height(); - } else { - const process_group = (group: PermissionGroup) => { - for (const g of group._sub_elements) - process_group(g); - group._element_permissions.handle_hide(); - if (sum_height == 0 && group._element_permissions.height() > 0) { - sum_height = 1; - } - }; - process_group(this); - } - this.pop_transform(context); - - if (_full && sum_height > 0) { - const arrow_stretch = 2 / 3; - if (!full) { - context.clearRect(0, 0, this.width(), PermissionGroup.HEIGHT); - } - context.fillStyle = this.colors.group.name; - - /* arrow */ - { - const x1 = this.collapsed ? PermissionGroup.ARROW_SIZE * arrow_stretch / 2 : 0; - const y1 = (PermissionGroup.HEIGHT - PermissionGroup.ARROW_SIZE) / 2 + (this.collapsed ? 0 : PermissionGroup.ARROW_SIZE * arrow_stretch / 2); /* center arrow */ - - const x2 = this.collapsed ? x1 + PermissionGroup.ARROW_SIZE * arrow_stretch : x1 + PermissionGroup.ARROW_SIZE / 2; - const y2 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE / 2 : y1 + PermissionGroup.ARROW_SIZE * arrow_stretch; - - const x3 = this.collapsed ? x1 : x1 + PermissionGroup.ARROW_SIZE; - const y3 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE : y1; - - context.beginPath(); - context.moveTo(x1, y1); - - context.lineTo(x2, y2); - context.lineTo(x3, y3); - - context.moveTo(x2, y2); - context.lineTo(x3, y3); - context.fill(); - - this._listener_colaps.region.x = this.original_x(context, 0); - this._listener_colaps.region.y = this.original_y(context, y1); - } - /* text */ - { - context.font = this.colors.group.name_font; - context.textBaseline = "middle"; - context.textAlign = "start"; - - context.fillText(this.group.group.name, PermissionGroup.ARROW_SIZE + 5, PermissionGroup.HEIGHT / 2); - } - } - } - - set_width(value: number) { - super.set_width(value); - for (const element of this._sub_elements) - element.set_width(value - PermissionGroup.ARROW_SIZE - 20); - this._element_permissions.set_width(value - PermissionGroup.ARROW_SIZE - 20); - } - - set_color_scheme(scheme: scheme.ColorScheme) { - super.set_color_scheme(scheme); - for (const child of this._sub_elements) - child.set_color_scheme(scheme); - this._element_permissions.set_color_scheme(scheme); - } - - set_manager(manager: PermissionEditor) { - super.set_manager(manager); - for (const child of this._sub_elements) - child.set_manager(manager); - this._element_permissions.set_manager(manager); - } - - height() { - let result = 0; - - if (!this.collapsed) { - for (const element of this._sub_elements) - result += element.height(); - - result += this._element_permissions.height(); - } else { - //We've to figure out if we have permissions - const process_group = (group: PermissionGroup) => { - if (result == 0 && group._element_permissions.height() > 0) { - result = 1; - } else { - for (const g of group._sub_elements) - process_group(g); - } - }; - process_group(this); - - if (result > 0) - return PermissionGroup.HEIGHT; - - return 0; - } - if (result > 0) { - result += PermissionGroup.HEIGHT; - return result; - } else { - return 0; - } - } - - initialize() { - for (const child of this._sub_elements) - child.initialize(); - this._element_permissions.initialize(); - - - this._listener_colaps = { - region: { - x: 0, - y: 0, - height: PermissionGroup.ARROW_SIZE, - width: PermissionGroup.ARROW_SIZE - }, - region_weight: 10, - /* - on_mouse_enter: () => { - this.collapsed_hovered = true; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - this.collapsed_hovered = false; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - */ - on_click: () => { - this.collapsed = !this.collapsed; - return RepaintMode.REPAINT_FULL; - }, - set_full_draw: () => this.request_full_draw(), - mouse_cursor: "pointer" - }; - - this.manager.intercept_manager().register_listener(this._listener_colaps); - } - - finalize() { - for (const child of this._sub_elements) - child.finalize(); - this._element_permissions.finalize(); - } - - collapse_group() { - for (const child of this._sub_elements) - child.collapse_group(); - - this.collapsed = true; - } - - expend_group() { - for (const child of this._sub_elements) - child.expend_group(); - - this.collapsed = false; - } - } - - class PermissionList extends DrawableObject { - permissions: PermissionEntry[] = []; - - constructor(permissions: PermissionInfo[]) { - super(); - - for (const permission of permissions) - this.permissions.push(new PermissionEntry(permission)); - } - - set_width(value: number) { - super.set_width(value); - for (const entry of this.permissions) - entry.set_width(value); - } - - - draw(context: CanvasRenderingContext2D, full: boolean) { - this.push_transform(context); - - for (const permission of this.permissions) { - permission.draw(context, full); - context.translate(0, permission.height()); - } - - this.pop_transform(context); - } - - height() { - let height = 0; - for (const permission of this.permissions) - height += permission.height(); - return height; - } - - - set_color_scheme(scheme: scheme.ColorScheme) { - super.set_color_scheme(scheme); - for (const entry of this.permissions) - entry.set_color_scheme(scheme); - } - - set_manager(manager: PermissionEditor) { - super.set_manager(manager); - - for (const entry of this.permissions) - entry.set_manager(manager); - } - - initialize() { - for (const entry of this.permissions) - entry.initialize(); - } - - finalize() { - for (const entry of this.permissions) - entry.finalize(); - } - - handle_hide() { - for (const entry of this.permissions) - entry.handle_hide(); - } - } - - class PermissionEntry extends DrawableObject { - public static readonly HEIGHT = PermissionGroup.HEIGHT; /* 24 */ - public static readonly HALF_HEIGHT = PermissionEntry.HEIGHT / 2; - public static readonly CHECKBOX_HEIGHT = PermissionEntry.HEIGHT - 2; - - public static readonly COLUMN_PADDING = 2; - public static readonly COLUMN_VALUE = 75; - public static readonly COLUMN_GRANTED = 75; - //public static readonly COLUMN_NEGATE = 25; - //public static readonly COLUMN_SKIP = 25; - public static readonly COLUMN_NEGATE = 75; - public static readonly COLUMN_SKIP = 75; - - private _permission: PermissionInfo; - - hidden: boolean; - - granted: number = 22; - value: number; - flag_skip: boolean = true; - flag_negate: boolean; - - private _prev_selected = false; - selected: boolean; - - flag_skip_hovered = false; - flag_negate_hovered = false; - flag_value_hovered = false; - flag_grant_hovered = false; - - private _listener_checkbox_skip: InteractionListener; - private _listener_checkbox_negate: InteractionListener; - private _listener_value: InteractionListener; - private _listener_grant: InteractionListener; - private _listener_general: InteractionListener; - private _icon_image: HTMLImageElement | undefined; - - on_icon_select?: (current_id: number) => Promise; - on_context_menu?: (x: number, y: number) => any; - on_grant_change?: () => any; - on_change?: () => any; - - constructor(permission: PermissionInfo) { - super(); - this._permission = permission; - } - - set_icon_id_image(image: HTMLImageElement | undefined) { - if (this._icon_image === image) - return; - this._icon_image = image; - if (image) { - image.height = 16; - image.width = 16; - } - } - - permission() { - return this._permission; - } - - draw(ctx: CanvasRenderingContext2D, full: boolean) { - if (!this.pop_full_draw() && !full) { /* Note: do not change this order! */ - /* test for update! */ - return; - } - if (this.hidden) { - this.handle_hide(); - return; - } - ctx.lineWidth = 1; - - /* debug box */ - if (false) { - ctx.fillStyle = "#FF0000"; - ctx.fillRect(0, 0, this.width(), PermissionEntry.HEIGHT); - ctx.fillStyle = "#000000"; - ctx.strokeRect(0, 0, this.width(), PermissionEntry.HEIGHT); - } - - if (!full) { - const off = this.selected || this._prev_selected ? ctx.getTransform().e : 0; - ctx.clearRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); - } - - if (this.selected) - ctx.fillStyle = this.colors.permission.background_selected; - else - ctx.fillStyle = this.colors.permission.background; - const off = this.selected ? ctx.getTransform().e : 0; - ctx.fillRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); - this._prev_selected = this.selected; - - /* permission name */ - { - ctx.fillStyle = typeof (this.value) !== "undefined" ? this.colors.permission.name : this.colors.permission.name_unset; - ctx.textBaseline = "middle"; - ctx.textAlign = "start"; - ctx.font = this.colors.permission.name_font; - - ctx.fillText(this._permission.name, 0, PermissionEntry.HALF_HEIGHT); - } - - const original_y = this.original_y(ctx, 0); - const original_x = this.original_x(ctx, 0); - const width = this.width(); - - /* draw granted */ - let w = width - PermissionEntry.COLUMN_GRANTED; - if (typeof (this.granted) === "number") { - this._listener_grant.region.x = original_x + w; - this._listener_grant.region.y = original_y; - - this._draw_number_field(ctx, this.colors.permission.granted, w, 0, PermissionEntry.COLUMN_VALUE, this.granted, this.flag_grant_hovered); - } else { - this._listener_grant.region.y = original_y; - this._listener_grant.region.x = - original_x - + width - - PermissionEntry.COLUMN_GRANTED; - } - - /* draw value and the skip stuff */ - if (typeof (this.value) === "number") { - w -= PermissionEntry.COLUMN_SKIP + PermissionEntry.COLUMN_PADDING; - { - const x = w + (PermissionEntry.COLUMN_SKIP - PermissionEntry.CHECKBOX_HEIGHT) / 2; - const y = 1; - - this._listener_checkbox_skip.region.x = original_x + x; - this._listener_checkbox_skip.region.y = original_y + y; - - this._draw_checkbox_field(ctx, this.colors.permission.skip, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_skip, this.flag_skip_hovered); - } - - w -= PermissionEntry.COLUMN_NEGATE + PermissionEntry.COLUMN_PADDING; - { - const x = w + (PermissionEntry.COLUMN_NEGATE - PermissionEntry.CHECKBOX_HEIGHT) / 2; - const y = 1; - - this._listener_checkbox_negate.region.x = original_x + x; - this._listener_checkbox_negate.region.y = original_y + y; - - this._draw_checkbox_field(ctx, this.colors.permission.negate, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_negate, this.flag_negate_hovered); - } - - w -= PermissionEntry.COLUMN_VALUE + PermissionEntry.COLUMN_PADDING; - if (this._permission.is_boolean()) { - const x = w + PermissionEntry.COLUMN_VALUE - PermissionEntry.CHECKBOX_HEIGHT; - const y = 1; - - this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; - this._listener_value.region.x = original_x + x; - this._listener_value.region.y = original_y + y; - - this._draw_checkbox_field(ctx, this.colors.permission.value_b, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.value > 0, this.flag_value_hovered); - } else if (this._permission.name === "i_icon_id" && this._icon_image) { - this._listener_value.region.x = original_x + w; - this._listener_value.region.y = original_y; - this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; - - this._draw_icon_field(ctx, this.colors.permission.value_b, w, 0, PermissionEntry.COLUMN_VALUE, this.flag_value_hovered, this._icon_image); - } else { - this._listener_value.region.width = PermissionEntry.COLUMN_VALUE; - this._listener_value.region.x = original_x + w; - this._listener_value.region.y = original_y; - - this._draw_number_field(ctx, this.colors.permission.value, w, 0, PermissionEntry.COLUMN_VALUE, this.value, this.flag_value_hovered); - } - this._listener_value.disabled = false; - } else { - this._listener_checkbox_skip.region.y = -1e8; - this._listener_checkbox_negate.region.y = -1e8; - - this._listener_value.region.y = original_y; - this._listener_value.region.x = - original_x - + width - - PermissionEntry.COLUMN_GRANTED - - PermissionEntry.COLUMN_NEGATE - - PermissionEntry.COLUMN_VALUE - - PermissionEntry.COLUMN_PADDING * 4; - this._listener_value.disabled = true; - } - - this._listener_general.region.y = original_y; - this._listener_general.region.x = original_x; - } - - handle_hide() { - /* so the listener wound get triggered */ - this._listener_value.region.x = -1e8; - this._listener_grant.region.x = -1e8; - this._listener_checkbox_negate.region.x = -1e8; - this._listener_checkbox_skip.region.x = -1e8; - this._listener_general.region.x = -1e8; - } - - private _draw_icon_field(ctx: CanvasRenderingContext2D, scheme: scheme.CheckBox, x: number, y: number, width: number, hovered: boolean, image: HTMLImageElement) { - const line = ctx.lineWidth; - ctx.lineWidth = 2; - ctx.fillStyle = scheme.border; - ctx.strokeRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); - ctx.lineWidth = line; - - ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; - ctx.fillRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); - - const center_y = y + PermissionEntry.HEIGHT / 2; - const center_x = x + PermissionEntry.HEIGHT / 2; - ctx.drawImage(image, center_x - image.width / 2, center_y - image.height / 2); - } - - private _draw_number_field(ctx: CanvasRenderingContext2D, scheme: scheme.TextField, x: number, y: number, width: number, value: number, hovered: boolean) { - ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; - ctx.fillRect(x, y, width, PermissionEntry.HEIGHT); - - ctx.fillStyle = scheme.color; - ctx.font = scheme.font; //Math.floor(2/3 * PermissionEntry.HEIGHT) + "px Arial"; - ctx.textAlign = "start"; - ctx.fillText(value + "", x, y + PermissionEntry.HALF_HEIGHT, width); - - ctx.strokeStyle = "#6e6e6e"; - const line = ctx.lineWidth; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(x, y + PermissionEntry.HEIGHT - 2); - ctx.lineTo(x + width, y + PermissionEntry.HEIGHT - 2); - ctx.stroke(); - ctx.lineWidth = line; - } - - private _draw_checkbox_field(ctx: CanvasRenderingContext2D, scheme: scheme.CheckBox, x: number, y: number, height: number, checked: boolean, hovered: boolean) { - ctx.fillStyle = scheme.border; - ctx.strokeRect(x, y, height, height); - - - ctx.fillStyle = checked ? - (hovered ? scheme.background_checked_hovered : scheme.background_checked) : - (hovered ? scheme.background_hovered : scheme.background); - ctx.fillRect(x + 1, y + 1, height - 2, height - 2); - - if (checked) { - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillStyle = scheme.checkmark; - ctx.font = scheme.checkmark_font; //Math.floor((5/4) * PermissionEntry.HEIGHT) + "px Arial"; - ctx.fillText("✓", x + height / 2, y + height / 2); - } - } - - height() { - return this.hidden ? 0 : PermissionEntry.HEIGHT; - } - - set_width(value: number) { - super.set_width(value); - this._listener_general.region.width = value; - } - - initialize() { - this._listener_checkbox_skip = { - region: { - x: -1e8, - y: -1e8, - height: PermissionEntry.CHECKBOX_HEIGHT, - width: PermissionEntry.CHECKBOX_HEIGHT - }, - region_weight: 10, - on_mouse_enter: () => { - this.flag_skip_hovered = true; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - this.flag_skip_hovered = false; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_click: () => { - this.flag_skip = !this.flag_skip; - if (this.on_change) - this.on_change(); - return RepaintMode.REPAINT_OBJECT_FULL; - }, - set_full_draw: () => this.request_full_draw(), - mouse_cursor: "pointer" - }; - this._listener_checkbox_negate = { - region: { - x: -1e8, - y: -1e8, - height: PermissionEntry.CHECKBOX_HEIGHT, - width: PermissionEntry.CHECKBOX_HEIGHT - }, - region_weight: 10, - on_mouse_enter: () => { - this.flag_negate_hovered = true; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - this.flag_negate_hovered = false; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_click: () => { - this.flag_negate = !this.flag_negate; - if (this.on_change) - this.on_change(); - return RepaintMode.REPAINT_OBJECT_FULL; - }, - set_full_draw: () => this.request_full_draw(), - mouse_cursor: "pointer" - }; - this._listener_value = { - region: { - x: -1e8, - y: -1e8, - height: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.HEIGHT, - width: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.COLUMN_VALUE - }, - region_weight: 10, - on_mouse_enter: () => { - this.flag_value_hovered = true; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - this.flag_value_hovered = false; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_click: () => { - if (this._permission.is_boolean()) { - this.value = this.value > 0 ? 0 : 1; - if (this.on_change) - this.on_change(); - return RepaintMode.REPAINT_OBJECT_FULL; - } else if (this._permission.name === "i_icon_id") { - this.on_icon_select(this.value).then(value => { - this.value = value; - if (this.on_change) - this.on_change(); - }).catch(error => { - console.warn(tr("Failed to select icon: %o"), error); - }) - } else { - this._spawn_number_edit( - this._listener_value.region.x, - this._listener_value.region.y, - this._listener_value.region.width, - this._listener_value.region.height, - this.colors.permission.value, - this.value || 0, - value => { - if (typeof (value) === "number") { - this.value = value; - this.request_full_draw(); - this.manager.request_draw(false); - if (this.on_change) - this.on_change(); - } - } - ) - } - return RepaintMode.REPAINT_OBJECT_FULL; - }, - set_full_draw: () => this.request_full_draw(), - mouse_cursor: "pointer" - }; - this._listener_grant = { - region: { - x: -1e8, - y: -1e8, - height: PermissionEntry.HEIGHT, - width: PermissionEntry.COLUMN_VALUE - }, - region_weight: 10, - on_mouse_enter: () => { - this.flag_grant_hovered = true; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - this.flag_grant_hovered = false; - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_click: () => { - this._spawn_number_edit( - this._listener_grant.region.x, - this._listener_grant.region.y, - this._listener_grant.region.width, - this._listener_grant.region.height, - this.colors.permission.granted, - this.granted || 0, //TODO use max assignable value? - value => { - if (typeof (value) === "number") { - this.granted = value; - this.request_full_draw(); - this.manager.request_draw(false); - - if (this.on_grant_change) - this.on_grant_change(); - } - } - ); - return RepaintMode.REPAINT_OBJECT_FULL; - }, - set_full_draw: () => this.request_full_draw(), - mouse_cursor: "pointer" - }; - - this._listener_general = { - region: { - x: -1e8, - y: -1e8, - height: PermissionEntry.HEIGHT, - width: 0 - }, - region_weight: 0, - /* - on_mouse_enter: () => { - return RepaintMode.REPAINT_OBJECT_FULL; - }, - on_mouse_leave: () => { - return RepaintMode.REPAINT_OBJECT_FULL; - }, - */ - on_click: (event: InteractionClickEvent) => { - this.manager.set_selected_entry(this); - - if (event.type == ClickEventType.DOUBLE && typeof (this.value) === "undefined") - return this._listener_value.on_click(event); - else if (event.type == ClickEventType.CONTEXT_MENU) { - const mouse = this.manager.mouse; - if (this.on_context_menu) { - this.on_context_menu(mouse.x, mouse.y); - event.consumed = true; - } - } - return RepaintMode.NONE; - }, - set_full_draw: () => this.request_full_draw(), - }; - - this.manager.intercept_manager().register_listener(this._listener_checkbox_negate); - this.manager.intercept_manager().register_listener(this._listener_checkbox_skip); - this.manager.intercept_manager().register_listener(this._listener_value); - this.manager.intercept_manager().register_listener(this._listener_grant); - this.manager.intercept_manager().register_listener(this._listener_general); - } - - finalize() { - } - - private _spawn_number_edit(x: number, y: number, width: number, height: number, color: scheme.TextField, value: number, callback: (new_value?: number) => any) { - const element = $.spawn("div"); - element.prop("contentEditable", true); - element - .css("pointer-events", "none") - .css("background", color.background) - .css("display", "block") - .css("position", "absolute") - .css("top", y) - .css("left", x) - .css("width", width) - .css("height", height) - .css("z-index", 1e6); - element.text(value); - element.appendTo(this.manager.canvas_container); - element.focus(); - - element.on('focusout', event => { - console.log("permission changed to " + element.text()); - if (!isNaN(parseInt(element.text()))) { - callback(parseInt(element.text())); - } else { - callback(undefined); - } - element.remove(); - }); - - element.on('keypress', event => { - if (event.which == KeyCode.KEY_RETURN) - element.trigger('focusout'); - - const text = String.fromCharCode(event.which); - if (isNaN(parseInt(text)) && text != "-") - event.preventDefault(); - - if (element.text().length > 7) - event.preventDefault(); - }); - - if (window.getSelection) { - const selection = window.getSelection(); - const range = document.createRange(); - range.selectNodeContents(element[0]); - selection.removeAllRanges(); - selection.addRange(range); - } - } - - trigger_value_assign() { - this._listener_value.on_click(undefined); - } - - trigger_grant_assign() { - this._listener_grant.on_click(undefined); - } - } - - export class InteractionManager { - private _listeners: InteractionListener[] = []; - private _entered_listeners: InteractionListener[] = []; - - register_listener(listener: InteractionListener) { - this._listeners.push(listener); - } - - remove_listener(listener: InteractionListener) { - this._listeners.remove(listener); - } - - process_mouse_move(new_x: number, new_y: number): { repaint: RepaintMode, cursor: string } { - let _entered_listeners: InteractionListener[] = []; - for (const listener of this._listeners) { - const aabb = listener.region; - - if (listener.disabled) - continue; - - if (new_x < aabb.x || new_x > aabb.x + aabb.width) - continue; - - if (new_y < aabb.y || new_y > aabb.y + aabb.height) - continue; - - _entered_listeners.push(listener); - } - - let repaint: RepaintMode = RepaintMode.NONE; - _entered_listeners.sort((a, b) => (a.region_weight || 0) - (b.region_weight || 0)); - for (const listener of this._entered_listeners) { - if (listener.on_mouse_leave && _entered_listeners.indexOf(listener) == -1) { - let mode = listener.on_mouse_leave(); - if (mode == RepaintMode.REPAINT_OBJECT_FULL) { - mode = RepaintMode.REPAINT; - if (listener.set_full_draw) - listener.set_full_draw(); - } - if (mode > repaint) - repaint = mode; - } - } - for (const listener of _entered_listeners) { - if (listener.on_mouse_enter && this._entered_listeners.indexOf(listener) == -1) { - let mode = listener.on_mouse_enter(); - if (mode == RepaintMode.REPAINT_OBJECT_FULL) { - mode = RepaintMode.REPAINT; - if (listener.set_full_draw) - listener.set_full_draw(); - } - if (mode > repaint) - repaint = mode; - } - } - this._entered_listeners = _entered_listeners; - - let cursor; - for (const listener of _entered_listeners) - if (typeof (listener.mouse_cursor) === "string") { - cursor = listener.mouse_cursor; - } - return { - repaint: repaint, - cursor: cursor - }; - } - - private process_click_event(x: number, y: number, event: InteractionClickEvent): RepaintMode { - const move_result = this.process_mouse_move(x, y); - - let repaint: RepaintMode = move_result.repaint; - for (const listener of this._entered_listeners) - if (listener.on_click) { - let mode = listener.on_click(event); - if (mode == RepaintMode.REPAINT_OBJECT_FULL) { - mode = RepaintMode.REPAINT; - if (listener.set_full_draw) - listener.set_full_draw(); - } - if (mode > repaint) - repaint = mode; - } - - return repaint; - } - - process_click(x: number, y: number): RepaintMode { - const event: InteractionClickEvent = { - consumed: false, - type: ClickEventType.SIGNLE, - offset_x: x, - offset_y: y - }; - - return this.process_click_event(x, y, event); - } - - process_dblclick(x: number, y: number): RepaintMode { - const event: InteractionClickEvent = { - consumed: false, - type: ClickEventType.DOUBLE, - offset_x: x, - offset_y: y - }; - - return this.process_click_event(x, y, event); - } - - process_context_menu(js_event: MouseEvent, x: number, y: number): RepaintMode { - const event: InteractionClickEvent = { - consumed: js_event.defaultPrevented, - type: ClickEventType.CONTEXT_MENU, - offset_x: x, - offset_y: y - }; - - const result = this.process_click_event(x, y, event); - if (event.consumed) - js_event.preventDefault(); - return result; - } - } - - export class PermissionEditor { - private static readonly PERMISSION_HEIGHT = PermissionEntry.HEIGHT; - private static readonly PERMISSION_GROUP_HEIGHT = PermissionGroup.HEIGHT; - - readonly grouped_permissions: GroupedPermissions[]; - readonly canvas: HTMLCanvasElement; - readonly canvas_container: HTMLDivElement; - private _max_height: number = 0; - - private _permission_count: number = 0; - private _permission_group_count: number = 0; - private _canvas_context: CanvasRenderingContext2D; - - private _selected_entry: PermissionEntry; - - private _draw_requested: boolean = false; - private _draw_requested_full: boolean = false; - - private _elements: PermissionGroup[] = []; - private _intersect_manager: InteractionManager; - - private _permission_entry_map: { [key: number]: PermissionEntry } = {}; - - mouse: { - x: number, - y: number - } = { - x: 0, - y: 0 - }; - - constructor(permissions: GroupedPermissions[]) { - this.grouped_permissions = permissions; - - this.canvas_container = $.spawn("div") - .addClass("window-resize-listener") /* we want to handle resized */ - .css("min-width", "750px") - .css("position", "relative") - .css("user-select", "none") - [0]; - this.canvas = $.spawn("canvas")[0]; - - this.canvas_container.appendChild(this.canvas); - - this._intersect_manager = new InteractionManager(); - this.canvas_container.onmousemove = event => { - this.mouse.x = event.pageX; - this.mouse.y = event.pageY; - - const draw = this._intersect_manager.process_mouse_move(event.offsetX, event.offsetY); - this.canvas_container.style.cursor = draw.cursor || ""; - this._handle_repaint(draw.repaint); - }; - this.canvas_container.onclick = event => { - this._handle_repaint(this._intersect_manager.process_click(event.offsetX, event.offsetY)); - }; - this.canvas_container.ondblclick = event => { - this._handle_repaint(this._intersect_manager.process_dblclick(event.offsetX, event.offsetY)); - }; - this.canvas_container.oncontextmenu = (event: MouseEvent) => { - this._handle_repaint(this._intersect_manager.process_context_menu(event, event.offsetX, event.offsetY)); - }; - this.canvas_container.onresize = () => this.request_draw(true); - - - this.initialize(); - } - - private _handle_repaint(mode: RepaintMode) { - if (mode == RepaintMode.REPAINT || mode == RepaintMode.REPAINT_FULL) - this.request_draw(mode == RepaintMode.REPAINT_FULL); - } - - request_draw(full?: boolean) { - this._draw_requested_full = this._draw_requested_full || full; - if (this._draw_requested) - return; - this._draw_requested = true; - requestAnimationFrame(() => { - this.draw(this._draw_requested_full); - }); - } - - draw(full?: boolean) { - this._draw_requested = false; - this._draw_requested_full = false; - - /* clear max height */ - this.canvas_container.style.overflowY = "shown"; - this.canvas_container.style.height = undefined; - - const max_height = this._max_height; - const max_width = this.canvas_container.clientWidth; - const update_width = this.canvas.width != max_width; - const full_draw = typeof (full) !== "boolean" || full || update_width; - - if (update_width) { - this.canvas.width = max_width; - for (const element of this._elements) - element.set_width(max_width); - } - - console.log("Drawing%s on %dx%d", full_draw ? " full" : "", max_width, max_height); - if (full_draw) - this.canvas.height = max_height; - const ctx = this._canvas_context; - ctx.resetTransform(); - if (full_draw) - ctx.clearRect(0, 0, max_width, max_height); - - let sum_height = 0; - for (const element of this._elements) { - element.draw(ctx, full_draw); - const height = element.height(); - sum_height += height; - ctx.translate(0, height); - } - - this.canvas_container.style.overflowY = "hidden"; - this.canvas_container.style.height = sum_height + "px"; - } - - private initialize() { - /* setup the canvas */ - { - const apply_group = (group: GroupedPermissions) => { - for (const g of group.children || []) - apply_group(g); - this._permission_group_count++; - this._permission_count += group.permissions.length; - }; - for (const group of this.grouped_permissions) - apply_group(group); - - this._max_height = this._permission_count * PermissionEditor.PERMISSION_HEIGHT + this._permission_group_count * PermissionEditor.PERMISSION_GROUP_HEIGHT; - console.log("%d permissions and %d groups required %d height", this._permission_count, this._permission_group_count, this._max_height); - - this.canvas.style.width = "100%"; - - this.canvas.style.flexShrink = "0"; - this.canvas_container.style.flexShrink = "0"; - - this._canvas_context = this.canvas.getContext("2d"); - } - - const font = Math.floor(2 / 3 * PermissionEntry.HEIGHT) + "px Arial"; - const font_checkmark = Math.floor((5 / 4) * PermissionEntry.HEIGHT) + "px Arial"; - const checkbox = { - background: "#303036", - background_hovered: "#CCCCCC", - - background_checked: "#0000AA", - background_checked_hovered: "#0000AA77", - - border: "#000000", - checkmark: "#303036", - checkmark_font: font_checkmark - }; - const input: scheme.TextField = { - color: "#000000", - font: font, - - background_hovered: "#CCCCCCCC", - background: "#30303600" - }; - - const color_scheme: scheme.ColorScheme = { - group: { - name: "#808080", - name_font: font - }, - //#28282c - permission: { - name: "#808080", - name_unset: "#1a1a1a", - name_font: font, - - background: "#303036", - background_selected: "#00007788", - - value: input, - value_b: checkbox, - granted: input, - negate: checkbox, - skip: checkbox - } - }; - (window as any).scheme = color_scheme; - /* setup elements to draw */ - { - const process_group = (group: PermissionGroup) => { - for (const permission of group._element_permissions.permissions) - this._permission_entry_map[permission.permission().id] = permission; - for (const g of group._sub_elements) - process_group(g); - }; - - for (const group of this.grouped_permissions) { - const element = new PermissionGroup(group); - element.set_color_scheme(color_scheme); - element.set_manager(this); - process_group(element); - this._elements.push(element); - } - for (const element of this._elements) { - element.initialize(); - } - } - } - - intercept_manager() { - return this._intersect_manager; - } - - set_selected_entry(entry?: PermissionEntry) { - if (this._selected_entry === entry) - return; - - if (this._selected_entry) { - this._selected_entry.selected = false; - this._selected_entry.request_full_draw(); - } - this._selected_entry = entry; - if (this._selected_entry) { - this._selected_entry.selected = true; - this._selected_entry.request_full_draw(); - } - this.request_draw(false); - } - - permission_entries(): PermissionEntry[] { - return Object.keys(this._permission_entry_map).map(e => this._permission_entry_map[e]); - } - - collapse_all() { - for (const group of this._elements) - group.collapse_group(); - this.request_draw(true); - } - - expend_all() { - for (const group of this._elements) - group.expend_group(); - this.request_draw(true); + group: { + name: string; + name_font: string; } } } - export class CanvasPermissionEditor extends Modals.AbstractPermissionEditor { - private container: JQuery; + export enum RepaintMode { + NONE, + REPAINT, + REPAINT_OBJECT_FULL, + REPAINT_FULL + } - private mode_container_permissions: JQuery; - private mode_container_error_permission: JQuery; - private mode_container_unset: JQuery; + export interface AxisAlignedBoundingBox { + x: number; + y: number; - /* references within the container tag */ - private permission_value_map: {[key:number]:PermissionValue} = {}; + width: number; + height: number; + } - private entry_editor: ui.PermissionEditor; + export enum ClickEventType { + SIGNLE, + DOUBLE, + CONTEXT_MENU + } - icon_resolver: (id: number) => Promise; - icon_selector: (current_id: number) => Promise; + export interface InteractionClickEvent { + type: ClickEventType; + consumed: boolean; + offset_x: number; + offset_y: number; + } - constructor() { + export interface InteractionListener { + region: AxisAlignedBoundingBox; + region_weight: number; + + /** + * @return true if a redraw is required + */ + on_mouse_enter?: () => RepaintMode; + + /** + * @return true if a redraw is required + */ + on_mouse_leave?: () => RepaintMode; + + /** + * @return true if a redraw is required + */ + on_click?: (event: InteractionClickEvent) => RepaintMode; + + mouse_cursor?: string; + + set_full_draw?: () => any; + disabled?: boolean; + } + + abstract class DrawableObject { + abstract draw(context: CanvasRenderingContext2D, full: boolean); + + private _object_full_draw = false; + private _width: number = 0; + + set_width(value: number) { + this._width = value; + } + + request_full_draw() { + this._object_full_draw = true; + } + + pop_full_draw() { + const result = this._object_full_draw; + this._object_full_draw = false; + return result; + } + + width() { + return this._width; + } + + abstract height(); + + private _transforms: DOMMatrix[] = []; + + protected push_transform(context: CanvasRenderingContext2D) { + this._transforms.push(context.getTransform()); + } + + protected pop_transform(context: CanvasRenderingContext2D) { + const transform = this._transforms.pop(); + context.setTransform( + transform.a, + transform.b, + transform.c, + transform.d, + transform.e, + transform.f + ); + } + + protected original_x(context: CanvasRenderingContext2D, x: number) { + return context.getTransform().e + x; + } + + protected original_y(context: CanvasRenderingContext2D, y: number) { + return context.getTransform().f + y; + } + + protected colors: scheme.ColorScheme = {} as any; + + set_color_scheme(scheme: scheme.ColorScheme) { + this.colors = scheme; + } + + protected manager: PermissionEditor; + + set_manager(manager: PermissionEditor) { + this.manager = manager; + } + + abstract initialize(); + + abstract finalize(); + } + + class PermissionGroup extends DrawableObject { + public static readonly HEIGHT = parseFloat(getComputedStyle(document.documentElement).fontSize) * (3 / 2); /* 24 */ + public static readonly ARROW_SIZE = 10; /* 12 */ + + group: GroupedPermissions; + _sub_elements: PermissionGroup[] = []; + _element_permissions: PermissionList; + + collapsed = false; + private _listener_colaps: InteractionListener; + + constructor(group: GroupedPermissions) { super(); + + this.group = group; + + this._element_permissions = new PermissionList(this.group.permissions); + for (const sub of this.group.children) + this._sub_elements.push(new PermissionGroup(sub)); } - initialize(permissions: GroupedPermissions[]) { - this._permissions = permissions; - this.entry_editor = new ui.PermissionEditor(permissions); - this.build_tag(); - } + draw(context: CanvasRenderingContext2D, full: boolean) { + const _full = this.pop_full_draw() || full; + this.push_transform(context); + context.translate(PermissionGroup.ARROW_SIZE + 20, PermissionGroup.HEIGHT); - html_tag() { return this.container; } + let sum_height = 0; + /* let first draw the elements, because if the sum height is zero then we could hide ourselves */ + if (!this.collapsed) { /* draw the next groups */ + for (const group of this._sub_elements) { + group.draw(context, full); - private build_tag() { - this.container = $("#tmpl_permission_editor_canvas").renderTag(); - /* search for that as long we've not that much nodes */ - this.mode_container_permissions = this.container.find(".container-mode-permissions"); - this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); - this.mode_container_unset = this.container.find(".container-mode-unset"); - this.set_mode(Modals.PermissionEditorMode.UNSET); + const height = group.height(); + sum_height += height; + context.translate(0, height); + } - /* the filter */ - { - const tag_filter_input = this.container.find(".filter-input"); - const tag_filter_granted = this.container.find(".filter-granted"); - - tag_filter_granted.on('change', event => tag_filter_input.trigger('change')); - tag_filter_input.on('keyup change', event => { - let filter_mask = tag_filter_input.val() as string; - let req_granted = tag_filter_granted.prop("checked"); - - - for(const entry of this.entry_editor.permission_entries()) { - const permission = entry.permission(); - - let shown = filter_mask.length == 0 || permission.name.indexOf(filter_mask) != -1; - if(shown && req_granted) { - const value: PermissionValue = this.permission_value_map[permission.id]; - shown = value && (value.hasValue() || value.hasGrant()); - } - - entry.hidden = !shown; + this._element_permissions.draw(context, full); + if (sum_height == 0) + sum_height += this._element_permissions.height(); + } else { + const process_group = (group: PermissionGroup) => { + for (const g of group._sub_elements) + process_group(g); + group._element_permissions.handle_hide(); + if (sum_height == 0 && group._element_permissions.height() > 0) { + sum_height = 1; } - this.entry_editor.request_draw(true); - }); - } - - /* update button */ - { - this.container.find(".button-update").on('click', this.trigger_update.bind(this)); - } - - /* global context menu listener */ - { - this.container.on('contextmenu', event => { - if(event.isDefaultPrevented()) return; - event.preventDefault(); - - /* TODO allow collapse and expend all */ - }); - } - - { - const tag_container = this.container.find(".entry-editor-container"); - tag_container.append(this.entry_editor.canvas_container); - - tag_container.parent().on('contextmenu', event => { - if(event.isDefaultPrevented()) return; - event.preventDefault(); - - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Expend all"), - callback: () => this.entry_editor.expend_all() - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Collapse all"), - callback: () => this.entry_editor.collapse_all() - }); - }); - } - - /* setup the permissions */ - for(const entry of this.entry_editor.permission_entries()) { - const permission = entry.permission(); - entry.on_change = () => { - const flag_remove = typeof(entry.value) !== "number"; - this._listener_change(permission, { - remove: flag_remove, - flag_negate: entry.flag_negate, - flag_skip: entry.flag_skip, - value: flag_remove ? -2 : entry.value - }).then(() => { - if(flag_remove) { - const element = this.permission_value_map[permission.id]; - if(!element) return; /* This should never happen, if so how are we displaying this permission?! */ - - element.value = undefined; - element.flag_negate = false; - element.flag_skip = false; - } else { - const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); - - element.value = entry.value; - element.flag_skip = entry.flag_skip; - element.flag_negate = entry.flag_negate; - } - - if(permission.name === "i_icon_id") { - this.icon_resolver(entry.value).then(e => { - entry.set_icon_id_image(e); - entry.request_full_draw(); - this.entry_editor.request_draw(false); - }).catch(error => { - console.warn(tr("Failed to load icon for permission editor: %o"), error); - }); - } - entry.request_full_draw(); - this.entry_editor.request_draw(false); - }).catch(() => { - const element = this.permission_value_map[permission.id]; - - entry.value = element && element.hasValue() ? element.value : undefined; - entry.flag_skip = element && element.flag_skip; - entry.flag_negate = element && element.flag_negate; - - entry.request_full_draw(); - this.entry_editor.request_draw(false); - }); }; + process_group(this); + } + this.pop_transform(context); - entry.on_grant_change = () => { - const flag_remove = typeof(entry.granted) !== "number"; + if (_full && sum_height > 0) { + const arrow_stretch = 2 / 3; + if (!full) { + context.clearRect(0, 0, this.width(), PermissionGroup.HEIGHT); + } + context.fillStyle = this.colors.group.name; - this._listener_change(permission, { - remove: flag_remove, - granted: flag_remove ? -2 : entry.granted, - }).then(() => { - if(flag_remove) { - const element = this.permission_value_map[permission.id]; - if (!element) return; /* This should never happen, if so how are we displaying this permission?! */ + /* arrow */ + { + const x1 = this.collapsed ? PermissionGroup.ARROW_SIZE * arrow_stretch / 2 : 0; + const y1 = (PermissionGroup.HEIGHT - PermissionGroup.ARROW_SIZE) / 2 + (this.collapsed ? 0 : PermissionGroup.ARROW_SIZE * arrow_stretch / 2); /* center arrow */ - element.granted_value = undefined; - } else { - const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); - element.granted_value = entry.granted; - } - entry.request_full_draw(); - this.entry_editor.request_draw(false); - }).catch(() => { - const element = this.permission_value_map[permission.id]; + const x2 = this.collapsed ? x1 + PermissionGroup.ARROW_SIZE * arrow_stretch : x1 + PermissionGroup.ARROW_SIZE / 2; + const y2 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE / 2 : y1 + PermissionGroup.ARROW_SIZE * arrow_stretch; - entry.granted = element && element.hasGrant() ? element.granted_value : undefined; - entry.request_full_draw(); - this.entry_editor.request_draw(false); - }); - }; + const x3 = this.collapsed ? x1 : x1 + PermissionGroup.ARROW_SIZE; + const y3 = this.collapsed ? y1 + PermissionGroup.ARROW_SIZE : y1; - entry.on_context_menu = (x, y) => { - let entries: contextmenu.MenuEntry[] = []; - if(typeof(entry.value) === "undefined") { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add permission"), - callback: () => entry.trigger_value_assign() - }); - } else { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Remove permission"), - callback: () => { - entry.value = undefined; - entry.on_change(); - } - }); - } + context.beginPath(); + context.moveTo(x1, y1); - if(typeof(entry.granted) === "undefined") { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add grant permission"), - callback: () => entry.trigger_grant_assign() - }); - } else { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Remove grant permission"), - callback: () => { - entry.granted = undefined; - entry.on_grant_change(); - } - }); - } - entries.push(contextmenu.Entry.HR()); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Expend all"), - callback: () => this.entry_editor.expend_all() - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Collapse all"), - callback: () => this.entry_editor.collapse_all() - }); - entries.push(contextmenu.Entry.HR()); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Show permission description"), - callback: () => { - createInfoModal( - tr("Permission description"), - tr("Permission description for permission ") + permission.name + ":
" + permission.description - ).open(); - } - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Copy permission name"), - callback: () => { - copy_to_clipboard(permission.name); - } - }); + context.lineTo(x2, y2); + context.lineTo(x3, y3); - contextmenu.spawn_context_menu(x, y, ...entries); + context.moveTo(x2, y2); + context.lineTo(x3, y3); + context.fill(); + + this._listener_colaps.region.x = this.original_x(context, 0); + this._listener_colaps.region.y = this.original_y(context, y1); + } + /* text */ + { + context.font = this.colors.group.name_font; + context.textBaseline = "middle"; + context.textAlign = "start"; + + context.fillText(this.group.group.name, PermissionGroup.ARROW_SIZE + 5, PermissionGroup.HEIGHT / 2); } } } - set_permissions(permissions?: PermissionValue[]) { - permissions = permissions || []; - this.permission_value_map = {}; + set_width(value: number) { + super.set_width(value); + for (const element of this._sub_elements) + element.set_width(value - PermissionGroup.ARROW_SIZE - 20); + this._element_permissions.set_width(value - PermissionGroup.ARROW_SIZE - 20); + } - for(const permission of permissions) - this.permission_value_map[permission.type.id] = permission; + set_color_scheme(scheme: scheme.ColorScheme) { + super.set_color_scheme(scheme); + for (const child of this._sub_elements) + child.set_color_scheme(scheme); + this._element_permissions.set_color_scheme(scheme); + } - for(const entry of this.entry_editor.permission_entries()) { - const permission = entry.permission(); - const value: PermissionValue = this.permission_value_map[permission.id]; + set_manager(manager: PermissionEditor) { + super.set_manager(manager); + for (const child of this._sub_elements) + child.set_manager(manager); + this._element_permissions.set_manager(manager); + } - if(permission.name === "i_icon_id") { - entry.set_icon_id_image(undefined); - entry.on_icon_select = this.icon_selector; + height() { + let result = 0; + + if (!this.collapsed) { + for (const element of this._sub_elements) + result += element.height(); + + result += this._element_permissions.height(); + } else { + //We've to figure out if we have permissions + const process_group = (group: PermissionGroup) => { + if (result == 0 && group._element_permissions.height() > 0) { + result = 1; + } else { + for (const g of group._sub_elements) + process_group(g); + } + }; + process_group(this); + + if (result > 0) + return PermissionGroup.HEIGHT; + + return 0; + } + if (result > 0) { + result += PermissionGroup.HEIGHT; + return result; + } else { + return 0; + } + } + + initialize() { + for (const child of this._sub_elements) + child.initialize(); + this._element_permissions.initialize(); + + + this._listener_colaps = { + region: { + x: 0, + y: 0, + height: PermissionGroup.ARROW_SIZE, + width: PermissionGroup.ARROW_SIZE + }, + region_weight: 10, + /* + on_mouse_enter: () => { + this.collapsed_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.collapsed_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + */ + on_click: () => { + this.collapsed = !this.collapsed; + return RepaintMode.REPAINT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + + this.manager.intercept_manager().register_listener(this._listener_colaps); + } + + finalize() { + for (const child of this._sub_elements) + child.finalize(); + this._element_permissions.finalize(); + } + + collapse_group() { + for (const child of this._sub_elements) + child.collapse_group(); + + this.collapsed = true; + } + + expend_group() { + for (const child of this._sub_elements) + child.expend_group(); + + this.collapsed = false; + } + } + + class PermissionList extends DrawableObject { + permissions: PermissionEntry[] = []; + + constructor(permissions: PermissionInfo[]) { + super(); + + for (const permission of permissions) + this.permissions.push(new PermissionEntry(permission)); + } + + set_width(value: number) { + super.set_width(value); + for (const entry of this.permissions) + entry.set_width(value); + } + + + draw(context: CanvasRenderingContext2D, full: boolean) { + this.push_transform(context); + + for (const permission of this.permissions) { + permission.draw(context, full); + context.translate(0, permission.height()); + } + + this.pop_transform(context); + } + + height() { + let height = 0; + for (const permission of this.permissions) + height += permission.height(); + return height; + } + + + set_color_scheme(scheme: scheme.ColorScheme) { + super.set_color_scheme(scheme); + for (const entry of this.permissions) + entry.set_color_scheme(scheme); + } + + set_manager(manager: PermissionEditor) { + super.set_manager(manager); + + for (const entry of this.permissions) + entry.set_manager(manager); + } + + initialize() { + for (const entry of this.permissions) + entry.initialize(); + } + + finalize() { + for (const entry of this.permissions) + entry.finalize(); + } + + handle_hide() { + for (const entry of this.permissions) + entry.handle_hide(); + } + } + + class PermissionEntry extends DrawableObject { + public static readonly HEIGHT = PermissionGroup.HEIGHT; /* 24 */ + public static readonly HALF_HEIGHT = PermissionEntry.HEIGHT / 2; + public static readonly CHECKBOX_HEIGHT = PermissionEntry.HEIGHT - 2; + + public static readonly COLUMN_PADDING = 2; + public static readonly COLUMN_VALUE = 75; + public static readonly COLUMN_GRANTED = 75; + //public static readonly COLUMN_NEGATE = 25; + //public static readonly COLUMN_SKIP = 25; + public static readonly COLUMN_NEGATE = 75; + public static readonly COLUMN_SKIP = 75; + + private _permission: PermissionInfo; + + hidden: boolean; + + granted: number = 22; + value: number; + flag_skip: boolean = true; + flag_negate: boolean; + + private _prev_selected = false; + selected: boolean; + + flag_skip_hovered = false; + flag_negate_hovered = false; + flag_value_hovered = false; + flag_grant_hovered = false; + + private _listener_checkbox_skip: InteractionListener; + private _listener_checkbox_negate: InteractionListener; + private _listener_value: InteractionListener; + private _listener_grant: InteractionListener; + private _listener_general: InteractionListener; + private _icon_image: HTMLImageElement | undefined; + + on_icon_select?: (current_id: number) => Promise; + on_context_menu?: (x: number, y: number) => any; + on_grant_change?: () => any; + on_change?: () => any; + + constructor(permission: PermissionInfo) { + super(); + this._permission = permission; + } + + set_icon_id_image(image: HTMLImageElement | undefined) { + if (this._icon_image === image) + return; + this._icon_image = image; + if (image) { + image.height = 16; + image.width = 16; + } + } + + permission() { + return this._permission; + } + + draw(ctx: CanvasRenderingContext2D, full: boolean) { + if (!this.pop_full_draw() && !full) { /* Note: do not change this order! */ + /* test for update! */ + return; + } + if (this.hidden) { + this.handle_hide(); + return; + } + ctx.lineWidth = 1; + + /* debug box */ + if (false) { + ctx.fillStyle = "#FF0000"; + ctx.fillRect(0, 0, this.width(), PermissionEntry.HEIGHT); + ctx.fillStyle = "#000000"; + ctx.strokeRect(0, 0, this.width(), PermissionEntry.HEIGHT); + } + + if (!full) { + const off = this.selected || this._prev_selected ? ctx.getTransform().e : 0; + ctx.clearRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); + } + + if (this.selected) + ctx.fillStyle = this.colors.permission.background_selected; + else + ctx.fillStyle = this.colors.permission.background; + const off = this.selected ? ctx.getTransform().e : 0; + ctx.fillRect(-off, 0, this.width() + off, PermissionEntry.HEIGHT); + this._prev_selected = this.selected; + + /* permission name */ + { + ctx.fillStyle = typeof (this.value) !== "undefined" ? this.colors.permission.name : this.colors.permission.name_unset; + ctx.textBaseline = "middle"; + ctx.textAlign = "start"; + ctx.font = this.colors.permission.name_font; + + ctx.fillText(this._permission.name, 0, PermissionEntry.HALF_HEIGHT); + } + + const original_y = this.original_y(ctx, 0); + const original_x = this.original_x(ctx, 0); + const width = this.width(); + + /* draw granted */ + let w = width - PermissionEntry.COLUMN_GRANTED; + if (typeof (this.granted) === "number") { + this._listener_grant.region.x = original_x + w; + this._listener_grant.region.y = original_y; + + this._draw_number_field(ctx, this.colors.permission.granted, w, 0, PermissionEntry.COLUMN_VALUE, this.granted, this.flag_grant_hovered); + } else { + this._listener_grant.region.y = original_y; + this._listener_grant.region.x = + original_x + + width + - PermissionEntry.COLUMN_GRANTED; + } + + /* draw value and the skip stuff */ + if (typeof (this.value) === "number") { + w -= PermissionEntry.COLUMN_SKIP + PermissionEntry.COLUMN_PADDING; + { + const x = w + (PermissionEntry.COLUMN_SKIP - PermissionEntry.CHECKBOX_HEIGHT) / 2; + const y = 1; + + this._listener_checkbox_skip.region.x = original_x + x; + this._listener_checkbox_skip.region.y = original_y + y; + + this._draw_checkbox_field(ctx, this.colors.permission.skip, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_skip, this.flag_skip_hovered); } - if(value && value.hasValue()) { - entry.value = value.value; - entry.flag_skip = value.flag_skip; - entry.flag_negate = value.flag_negate; + w -= PermissionEntry.COLUMN_NEGATE + PermissionEntry.COLUMN_PADDING; + { + const x = w + (PermissionEntry.COLUMN_NEGATE - PermissionEntry.CHECKBOX_HEIGHT) / 2; + const y = 1; + + this._listener_checkbox_negate.region.x = original_x + x; + this._listener_checkbox_negate.region.y = original_y + y; + + this._draw_checkbox_field(ctx, this.colors.permission.negate, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.flag_negate, this.flag_negate_hovered); + } + + w -= PermissionEntry.COLUMN_VALUE + PermissionEntry.COLUMN_PADDING; + if (this._permission.is_boolean()) { + const x = w + PermissionEntry.COLUMN_VALUE - PermissionEntry.CHECKBOX_HEIGHT; + const y = 1; + + this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; + this._listener_value.region.x = original_x + x; + this._listener_value.region.y = original_y + y; + + this._draw_checkbox_field(ctx, this.colors.permission.value_b, x, y, PermissionEntry.CHECKBOX_HEIGHT, this.value > 0, this.flag_value_hovered); + } else if (this._permission.name === "i_icon_id" && this._icon_image) { + this._listener_value.region.x = original_x + w; + this._listener_value.region.y = original_y; + this._listener_value.region.width = PermissionEntry.CHECKBOX_HEIGHT; + + this._draw_icon_field(ctx, this.colors.permission.value_b, w, 0, PermissionEntry.COLUMN_VALUE, this.flag_value_hovered, this._icon_image); + } else { + this._listener_value.region.width = PermissionEntry.COLUMN_VALUE; + this._listener_value.region.x = original_x + w; + this._listener_value.region.y = original_y; + + this._draw_number_field(ctx, this.colors.permission.value, w, 0, PermissionEntry.COLUMN_VALUE, this.value, this.flag_value_hovered); + } + this._listener_value.disabled = false; + } else { + this._listener_checkbox_skip.region.y = -1e8; + this._listener_checkbox_negate.region.y = -1e8; + + this._listener_value.region.y = original_y; + this._listener_value.region.x = + original_x + + width + - PermissionEntry.COLUMN_GRANTED + - PermissionEntry.COLUMN_NEGATE + - PermissionEntry.COLUMN_VALUE + - PermissionEntry.COLUMN_PADDING * 4; + this._listener_value.disabled = true; + } + + this._listener_general.region.y = original_y; + this._listener_general.region.x = original_x; + } + + handle_hide() { + /* so the listener wound get triggered */ + this._listener_value.region.x = -1e8; + this._listener_grant.region.x = -1e8; + this._listener_checkbox_negate.region.x = -1e8; + this._listener_checkbox_skip.region.x = -1e8; + this._listener_general.region.x = -1e8; + } + + private _draw_icon_field(ctx: CanvasRenderingContext2D, scheme: scheme.CheckBox, x: number, y: number, width: number, hovered: boolean, image: HTMLImageElement) { + const line = ctx.lineWidth; + ctx.lineWidth = 2; + ctx.fillStyle = scheme.border; + ctx.strokeRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); + ctx.lineWidth = line; + + ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; + ctx.fillRect(x + 1, y + 1, PermissionEntry.HEIGHT - 2, PermissionEntry.HEIGHT - 2); + + const center_y = y + PermissionEntry.HEIGHT / 2; + const center_x = x + PermissionEntry.HEIGHT / 2; + ctx.drawImage(image, center_x - image.width / 2, center_y - image.height / 2); + } + + private _draw_number_field(ctx: CanvasRenderingContext2D, scheme: scheme.TextField, x: number, y: number, width: number, value: number, hovered: boolean) { + ctx.fillStyle = hovered ? scheme.background_hovered : scheme.background; + ctx.fillRect(x, y, width, PermissionEntry.HEIGHT); + + ctx.fillStyle = scheme.color; + ctx.font = scheme.font; //Math.floor(2/3 * PermissionEntry.HEIGHT) + "px Arial"; + ctx.textAlign = "start"; + ctx.fillText(value + "", x, y + PermissionEntry.HALF_HEIGHT, width); + + ctx.strokeStyle = "#6e6e6e"; + const line = ctx.lineWidth; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x, y + PermissionEntry.HEIGHT - 2); + ctx.lineTo(x + width, y + PermissionEntry.HEIGHT - 2); + ctx.stroke(); + ctx.lineWidth = line; + } + + private _draw_checkbox_field(ctx: CanvasRenderingContext2D, scheme: scheme.CheckBox, x: number, y: number, height: number, checked: boolean, hovered: boolean) { + ctx.fillStyle = scheme.border; + ctx.strokeRect(x, y, height, height); + + + ctx.fillStyle = checked ? + (hovered ? scheme.background_checked_hovered : scheme.background_checked) : + (hovered ? scheme.background_hovered : scheme.background); + ctx.fillRect(x + 1, y + 1, height - 2, height - 2); + + if (checked) { + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = scheme.checkmark; + ctx.font = scheme.checkmark_font; //Math.floor((5/4) * PermissionEntry.HEIGHT) + "px Arial"; + ctx.fillText("✓", x + height / 2, y + height / 2); + } + } + + height() { + return this.hidden ? 0 : PermissionEntry.HEIGHT; + } + + set_width(value: number) { + super.set_width(value); + this._listener_general.region.width = value; + } + + initialize() { + this._listener_checkbox_skip = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.CHECKBOX_HEIGHT, + width: PermissionEntry.CHECKBOX_HEIGHT + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_skip_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_skip_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this.flag_skip = !this.flag_skip; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_checkbox_negate = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.CHECKBOX_HEIGHT, + width: PermissionEntry.CHECKBOX_HEIGHT + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_negate_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_negate_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this.flag_negate = !this.flag_negate; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_value = { + region: { + x: -1e8, + y: -1e8, + height: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.HEIGHT, + width: this._permission.is_boolean() ? PermissionEntry.CHECKBOX_HEIGHT : PermissionEntry.COLUMN_VALUE + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_value_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_value_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + if (this._permission.is_boolean()) { + this.value = this.value > 0 ? 0 : 1; + if (this.on_change) + this.on_change(); + return RepaintMode.REPAINT_OBJECT_FULL; + } else if (this._permission.name === "i_icon_id") { + this.on_icon_select(this.value).then(value => { + this.value = value; + if (this.on_change) + this.on_change(); + }).catch(error => { + console.warn(tr("Failed to select icon: %o"), error); + }) + } else { + this._spawn_number_edit( + this._listener_value.region.x, + this._listener_value.region.y, + this._listener_value.region.width, + this._listener_value.region.height, + this.colors.permission.value, + this.value || 0, + value => { + if (typeof (value) === "number") { + this.value = value; + this.request_full_draw(); + this.manager.request_draw(false); + if (this.on_change) + this.on_change(); + } + } + ) + } + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + this._listener_grant = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.HEIGHT, + width: PermissionEntry.COLUMN_VALUE + }, + region_weight: 10, + on_mouse_enter: () => { + this.flag_grant_hovered = true; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + this.flag_grant_hovered = false; + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_click: () => { + this._spawn_number_edit( + this._listener_grant.region.x, + this._listener_grant.region.y, + this._listener_grant.region.width, + this._listener_grant.region.height, + this.colors.permission.granted, + this.granted || 0, //TODO use max assignable value? + value => { + if (typeof (value) === "number") { + this.granted = value; + this.request_full_draw(); + this.manager.request_draw(false); + + if (this.on_grant_change) + this.on_grant_change(); + } + } + ); + return RepaintMode.REPAINT_OBJECT_FULL; + }, + set_full_draw: () => this.request_full_draw(), + mouse_cursor: "pointer" + }; + + this._listener_general = { + region: { + x: -1e8, + y: -1e8, + height: PermissionEntry.HEIGHT, + width: 0 + }, + region_weight: 0, + /* + on_mouse_enter: () => { + return RepaintMode.REPAINT_OBJECT_FULL; + }, + on_mouse_leave: () => { + return RepaintMode.REPAINT_OBJECT_FULL; + }, + */ + on_click: (event: InteractionClickEvent) => { + this.manager.set_selected_entry(this); + + if (event.type == ClickEventType.DOUBLE && typeof (this.value) === "undefined") + return this._listener_value.on_click(event); + else if (event.type == ClickEventType.CONTEXT_MENU) { + const mouse = this.manager.mouse; + if (this.on_context_menu) { + this.on_context_menu(mouse.x, mouse.y); + event.consumed = true; + } + } + return RepaintMode.NONE; + }, + set_full_draw: () => this.request_full_draw(), + }; + + this.manager.intercept_manager().register_listener(this._listener_checkbox_negate); + this.manager.intercept_manager().register_listener(this._listener_checkbox_skip); + this.manager.intercept_manager().register_listener(this._listener_value); + this.manager.intercept_manager().register_listener(this._listener_grant); + this.manager.intercept_manager().register_listener(this._listener_general); + } + + finalize() { + } + + private _spawn_number_edit(x: number, y: number, width: number, height: number, color: scheme.TextField, value: number, callback: (new_value?: number) => any) { + const element = $.spawn("div"); + element.prop("contentEditable", true); + element + .css("pointer-events", "none") + .css("background", color.background) + .css("display", "block") + .css("position", "absolute") + .css("top", y) + .css("left", x) + .css("width", width) + .css("height", height) + .css("z-index", 1e6); + element.text(value); + element.appendTo(this.manager.canvas_container); + element.focus(); + + element.on('focusout', event => { + console.log("permission changed to " + element.text()); + if (!isNaN(parseInt(element.text()))) { + callback(parseInt(element.text())); + } else { + callback(undefined); + } + element.remove(); + }); + + element.on('keypress', event => { + if (event.which == KeyCode.KEY_RETURN) + element.trigger('focusout'); + + const text = String.fromCharCode(event.which); + if (isNaN(parseInt(text)) && text != "-") + event.preventDefault(); + + if (element.text().length > 7) + event.preventDefault(); + }); + + if (window.getSelection) { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element[0]); + selection.removeAllRanges(); + selection.addRange(range); + } + } + + trigger_value_assign() { + this._listener_value.on_click(undefined); + } + + trigger_grant_assign() { + this._listener_grant.on_click(undefined); + } + } + + export class InteractionManager { + private _listeners: InteractionListener[] = []; + private _entered_listeners: InteractionListener[] = []; + + register_listener(listener: InteractionListener) { + this._listeners.push(listener); + } + + remove_listener(listener: InteractionListener) { + this._listeners.remove(listener); + } + + process_mouse_move(new_x: number, new_y: number): { repaint: RepaintMode, cursor: string } { + let _entered_listeners: InteractionListener[] = []; + for (const listener of this._listeners) { + const aabb = listener.region; + + if (listener.disabled) + continue; + + if (new_x < aabb.x || new_x > aabb.x + aabb.width) + continue; + + if (new_y < aabb.y || new_y > aabb.y + aabb.height) + continue; + + _entered_listeners.push(listener); + } + + let repaint: RepaintMode = RepaintMode.NONE; + _entered_listeners.sort((a, b) => (a.region_weight || 0) - (b.region_weight || 0)); + for (const listener of this._entered_listeners) { + if (listener.on_mouse_leave && _entered_listeners.indexOf(listener) == -1) { + let mode = listener.on_mouse_leave(); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + } + for (const listener of _entered_listeners) { + if (listener.on_mouse_enter && this._entered_listeners.indexOf(listener) == -1) { + let mode = listener.on_mouse_enter(); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + } + this._entered_listeners = _entered_listeners; + + let cursor; + for (const listener of _entered_listeners) + if (typeof (listener.mouse_cursor) === "string") { + cursor = listener.mouse_cursor; + } + return { + repaint: repaint, + cursor: cursor + }; + } + + private process_click_event(x: number, y: number, event: InteractionClickEvent): RepaintMode { + const move_result = this.process_mouse_move(x, y); + + let repaint: RepaintMode = move_result.repaint; + for (const listener of this._entered_listeners) + if (listener.on_click) { + let mode = listener.on_click(event); + if (mode == RepaintMode.REPAINT_OBJECT_FULL) { + mode = RepaintMode.REPAINT; + if (listener.set_full_draw) + listener.set_full_draw(); + } + if (mode > repaint) + repaint = mode; + } + + return repaint; + } + + process_click(x: number, y: number): RepaintMode { + const event: InteractionClickEvent = { + consumed: false, + type: ClickEventType.SIGNLE, + offset_x: x, + offset_y: y + }; + + return this.process_click_event(x, y, event); + } + + process_dblclick(x: number, y: number): RepaintMode { + const event: InteractionClickEvent = { + consumed: false, + type: ClickEventType.DOUBLE, + offset_x: x, + offset_y: y + }; + + return this.process_click_event(x, y, event); + } + + process_context_menu(js_event: MouseEvent, x: number, y: number): RepaintMode { + const event: InteractionClickEvent = { + consumed: js_event.defaultPrevented, + type: ClickEventType.CONTEXT_MENU, + offset_x: x, + offset_y: y + }; + + const result = this.process_click_event(x, y, event); + if (event.consumed) + js_event.preventDefault(); + return result; + } + } + + export class PermissionEditor { + private static readonly PERMISSION_HEIGHT = PermissionEntry.HEIGHT; + private static readonly PERMISSION_GROUP_HEIGHT = PermissionGroup.HEIGHT; + + readonly grouped_permissions: GroupedPermissions[]; + readonly canvas: HTMLCanvasElement; + readonly canvas_container: HTMLDivElement; + private _max_height: number = 0; + + private _permission_count: number = 0; + private _permission_group_count: number = 0; + private _canvas_context: CanvasRenderingContext2D; + + private _selected_entry: PermissionEntry; + + private _draw_requested: boolean = false; + private _draw_requested_full: boolean = false; + + private _elements: PermissionGroup[] = []; + private _intersect_manager: InteractionManager; + + private _permission_entry_map: { [key: number]: PermissionEntry } = {}; + + mouse: { + x: number, + y: number + } = { + x: 0, + y: 0 + }; + + constructor(permissions: GroupedPermissions[]) { + this.grouped_permissions = permissions; + + this.canvas_container = $.spawn("div") + .addClass("window-resize-listener") /* we want to handle resized */ + .css("min-width", "750px") + .css("position", "relative") + .css("user-select", "none") + [0]; + this.canvas = $.spawn("canvas")[0]; + + this.canvas_container.appendChild(this.canvas); + + this._intersect_manager = new InteractionManager(); + this.canvas_container.onmousemove = event => { + this.mouse.x = event.pageX; + this.mouse.y = event.pageY; + + const draw = this._intersect_manager.process_mouse_move(event.offsetX, event.offsetY); + this.canvas_container.style.cursor = draw.cursor || ""; + this._handle_repaint(draw.repaint); + }; + this.canvas_container.onclick = event => { + this._handle_repaint(this._intersect_manager.process_click(event.offsetX, event.offsetY)); + }; + this.canvas_container.ondblclick = event => { + this._handle_repaint(this._intersect_manager.process_dblclick(event.offsetX, event.offsetY)); + }; + this.canvas_container.oncontextmenu = (event: MouseEvent) => { + this._handle_repaint(this._intersect_manager.process_context_menu(event, event.offsetX, event.offsetY)); + }; + this.canvas_container.onresize = () => this.request_draw(true); + + + this.initialize(); + } + + private _handle_repaint(mode: RepaintMode) { + if (mode == RepaintMode.REPAINT || mode == RepaintMode.REPAINT_FULL) + this.request_draw(mode == RepaintMode.REPAINT_FULL); + } + + request_draw(full?: boolean) { + this._draw_requested_full = this._draw_requested_full || full; + if (this._draw_requested) + return; + this._draw_requested = true; + requestAnimationFrame(() => { + this.draw(this._draw_requested_full); + }); + } + + draw(full?: boolean) { + this._draw_requested = false; + this._draw_requested_full = false; + + /* clear max height */ + this.canvas_container.style.overflowY = "shown"; + this.canvas_container.style.height = undefined; + + const max_height = this._max_height; + const max_width = this.canvas_container.clientWidth; + const update_width = this.canvas.width != max_width; + const full_draw = typeof (full) !== "boolean" || full || update_width; + + if (update_width) { + this.canvas.width = max_width; + for (const element of this._elements) + element.set_width(max_width); + } + + console.log("Drawing%s on %dx%d", full_draw ? " full" : "", max_width, max_height); + if (full_draw) + this.canvas.height = max_height; + const ctx = this._canvas_context; + ctx.resetTransform(); + if (full_draw) + ctx.clearRect(0, 0, max_width, max_height); + + let sum_height = 0; + for (const element of this._elements) { + element.draw(ctx, full_draw); + const height = element.height(); + sum_height += height; + ctx.translate(0, height); + } + + this.canvas_container.style.overflowY = "hidden"; + this.canvas_container.style.height = sum_height + "px"; + } + + private initialize() { + /* setup the canvas */ + { + const apply_group = (group: GroupedPermissions) => { + for (const g of group.children || []) + apply_group(g); + this._permission_group_count++; + this._permission_count += group.permissions.length; + }; + for (const group of this.grouped_permissions) + apply_group(group); + + this._max_height = this._permission_count * PermissionEditor.PERMISSION_HEIGHT + this._permission_group_count * PermissionEditor.PERMISSION_GROUP_HEIGHT; + console.log("%d permissions and %d groups required %d height", this._permission_count, this._permission_group_count, this._max_height); + + this.canvas.style.width = "100%"; + + this.canvas.style.flexShrink = "0"; + this.canvas_container.style.flexShrink = "0"; + + this._canvas_context = this.canvas.getContext("2d"); + } + + const font = Math.floor(2 / 3 * PermissionEntry.HEIGHT) + "px Arial"; + const font_checkmark = Math.floor((5 / 4) * PermissionEntry.HEIGHT) + "px Arial"; + const checkbox = { + background: "#303036", + background_hovered: "#CCCCCC", + + background_checked: "#0000AA", + background_checked_hovered: "#0000AA77", + + border: "#000000", + checkmark: "#303036", + checkmark_font: font_checkmark + }; + const input: scheme.TextField = { + color: "#000000", + font: font, + + background_hovered: "#CCCCCCCC", + background: "#30303600" + }; + + const color_scheme: scheme.ColorScheme = { + group: { + name: "#808080", + name_font: font + }, + //#28282c + permission: { + name: "#808080", + name_unset: "#1a1a1a", + name_font: font, + + background: "#303036", + background_selected: "#00007788", + + value: input, + value_b: checkbox, + granted: input, + negate: checkbox, + skip: checkbox + } + }; + (window as any).scheme = color_scheme; + /* setup elements to draw */ + { + const process_group = (group: PermissionGroup) => { + for (const permission of group._element_permissions.permissions) + this._permission_entry_map[permission.permission().id] = permission; + for (const g of group._sub_elements) + process_group(g); + }; + + for (const group of this.grouped_permissions) { + const element = new PermissionGroup(group); + element.set_color_scheme(color_scheme); + element.set_manager(this); + process_group(element); + this._elements.push(element); + } + for (const element of this._elements) { + element.initialize(); + } + } + } + + intercept_manager() { + return this._intersect_manager; + } + + set_selected_entry(entry?: PermissionEntry) { + if (this._selected_entry === entry) + return; + + if (this._selected_entry) { + this._selected_entry.selected = false; + this._selected_entry.request_full_draw(); + } + this._selected_entry = entry; + if (this._selected_entry) { + this._selected_entry.selected = true; + this._selected_entry.request_full_draw(); + } + this.request_draw(false); + } + + permission_entries(): PermissionEntry[] { + return Object.keys(this._permission_entry_map).map(e => this._permission_entry_map[e]); + } + + collapse_all() { + for (const group of this._elements) + group.collapse_group(); + this.request_draw(true); + } + + expend_all() { + for (const group of this._elements) + group.expend_group(); + this.request_draw(true); + } + } +} + +export class CanvasPermissionEditor extends AbstractPermissionEditor { + private container: JQuery; + + private mode_container_permissions: JQuery; + private mode_container_error_permission: JQuery; + private mode_container_unset: JQuery; + + /* references within the container tag */ + private permission_value_map: {[key:number]:PermissionValue} = {}; + + private entry_editor: ui.PermissionEditor; + + icon_resolver: (id: number) => Promise; + icon_selector: (current_id: number) => Promise; + + constructor() { + super(); + } + + initialize(permissions: GroupedPermissions[]) { + this._permissions = permissions; + this.entry_editor = new ui.PermissionEditor(permissions); + this.build_tag(); + } + + html_tag() { return this.container; } + + private build_tag() { + this.container = $("#tmpl_permission_editor_canvas").renderTag(); + /* search for that as long we've not that much nodes */ + this.mode_container_permissions = this.container.find(".container-mode-permissions"); + this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); + this.mode_container_unset = this.container.find(".container-mode-unset"); + this.set_mode(PermissionEditorMode.UNSET); + + /* the filter */ + { + const tag_filter_input = this.container.find(".filter-input"); + const tag_filter_granted = this.container.find(".filter-granted"); + + tag_filter_granted.on('change', event => tag_filter_input.trigger('change')); + tag_filter_input.on('keyup change', event => { + let filter_mask = tag_filter_input.val() as string; + let req_granted = tag_filter_granted.prop("checked"); + + + for(const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + + let shown = filter_mask.length == 0 || permission.name.indexOf(filter_mask) != -1; + if(shown && req_granted) { + const value: PermissionValue = this.permission_value_map[permission.id]; + shown = value && (value.hasValue() || value.hasGrant()); + } + + entry.hidden = !shown; + } + this.entry_editor.request_draw(true); + }); + } + + /* update button */ + { + this.container.find(".button-update").on('click', this.trigger_update.bind(this)); + } + + /* global context menu listener */ + { + this.container.on('contextmenu', event => { + if(event.isDefaultPrevented()) return; + event.preventDefault(); + + /* TODO allow collapse and expend all */ + }); + } + + { + const tag_container = this.container.find(".entry-editor-container"); + tag_container.append(this.entry_editor.canvas_container); + + tag_container.parent().on('contextmenu', event => { + if(event.isDefaultPrevented()) return; + event.preventDefault(); + + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Expend all"), + callback: () => this.entry_editor.expend_all() + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Collapse all"), + callback: () => this.entry_editor.collapse_all() + }); + }); + } + + /* setup the permissions */ + for(const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + entry.on_change = () => { + const flag_remove = typeof(entry.value) !== "number"; + this._listener_change(permission, { + remove: flag_remove, + flag_negate: entry.flag_negate, + flag_skip: entry.flag_skip, + value: flag_remove ? -2 : entry.value + }).then(() => { + if(flag_remove) { + const element = this.permission_value_map[permission.id]; + if(!element) return; /* This should never happen, if so how are we displaying this permission?! */ + + element.value = undefined; + element.flag_negate = false; + element.flag_skip = false; + } else { + const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); + + element.value = entry.value; + element.flag_skip = entry.flag_skip; + element.flag_negate = entry.flag_negate; + } + if(permission.name === "i_icon_id") { - this.icon_resolver(value.value).then(e => { + this.icon_resolver(entry.value).then(e => { entry.set_icon_id_image(e); entry.request_full_draw(); this.entry_editor.request_draw(false); @@ -1588,39 +1455,178 @@ namespace pe { console.warn(tr("Failed to load icon for permission editor: %o"), error); }); } + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(() => { + const element = this.permission_value_map[permission.id]; + + entry.value = element && element.hasValue() ? element.value : undefined; + entry.flag_skip = element && element.flag_skip; + entry.flag_negate = element && element.flag_negate; + + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }); + }; + + entry.on_grant_change = () => { + const flag_remove = typeof(entry.granted) !== "number"; + + this._listener_change(permission, { + remove: flag_remove, + granted: flag_remove ? -2 : entry.granted, + }).then(() => { + if(flag_remove) { + const element = this.permission_value_map[permission.id]; + if (!element) return; /* This should never happen, if so how are we displaying this permission?! */ + + element.granted_value = undefined; + } else { + const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); + element.granted_value = entry.granted; + } + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(() => { + const element = this.permission_value_map[permission.id]; + + entry.granted = element && element.hasGrant() ? element.granted_value : undefined; + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }); + }; + + entry.on_context_menu = (x, y) => { + let entries: contextmenu.MenuEntry[] = []; + if(typeof(entry.value) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add permission"), + callback: () => entry.trigger_value_assign() + }); } else { - entry.value = undefined; - entry.flag_skip = false; - entry.flag_negate = false; + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Remove permission"), + callback: () => { + entry.value = undefined; + entry.on_change(); + } + }); } - if(value && value.hasGrant()) { - entry.granted = value.granted_value; + if(typeof(entry.granted) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add grant permission"), + callback: () => entry.trigger_grant_assign() + }); } else { - entry.granted = undefined; + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Remove grant permission"), + callback: () => { + entry.granted = undefined; + entry.on_grant_change(); + } + }); } + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Expend all"), + callback: () => this.entry_editor.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Collapse all"), + callback: () => this.entry_editor.collapse_all() + }); + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Show permission description"), + callback: () => { + createInfoModal( + tr("Permission description"), + tr("Permission description for permission ") + permission.name + ":
" + permission.description + ).open(); + } + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Copy permission name"), + callback: () => { + copy_to_clipboard(permission.name); + } + }); + + contextmenu.spawn_context_menu(x, y, ...entries); } - this.entry_editor.request_draw(true); - } - - set_mode(mode: Modals.PermissionEditorMode) { - this.mode_container_permissions.css('display', mode == Modals.PermissionEditorMode.VISIBLE ? 'flex' : 'none'); - this.mode_container_error_permission.css('display', mode == Modals.PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); - this.mode_container_unset.css('display', mode == Modals.PermissionEditorMode.UNSET ? 'block' : 'none'); - if(mode == Modals.PermissionEditorMode.VISIBLE) - this.entry_editor.draw(true); - } - - update_ui() { - this.entry_editor.draw(true); - } - - set_toggle_button(callback: () => string, initial: string) { - throw "not implemented"; - } - - set_hidden_permissions(permissions: PermissionType[]) { - //TODO: Stuff here } } + + set_permissions(permissions?: PermissionValue[]) { + permissions = permissions || []; + this.permission_value_map = {}; + + for(const permission of permissions) + this.permission_value_map[permission.type.id] = permission; + + for(const entry of this.entry_editor.permission_entries()) { + const permission = entry.permission(); + const value: PermissionValue = this.permission_value_map[permission.id]; + + if(permission.name === "i_icon_id") { + entry.set_icon_id_image(undefined); + entry.on_icon_select = this.icon_selector; + } + + if(value && value.hasValue()) { + entry.value = value.value; + entry.flag_skip = value.flag_skip; + entry.flag_negate = value.flag_negate; + if(permission.name === "i_icon_id") { + this.icon_resolver(value.value).then(e => { + entry.set_icon_id_image(e); + entry.request_full_draw(); + this.entry_editor.request_draw(false); + }).catch(error => { + console.warn(tr("Failed to load icon for permission editor: %o"), error); + }); + } + } else { + entry.value = undefined; + entry.flag_skip = false; + entry.flag_negate = false; + } + + if(value && value.hasGrant()) { + entry.granted = value.granted_value; + } else { + entry.granted = undefined; + } + } + this.entry_editor.request_draw(true); + } + + set_mode(mode: PermissionEditorMode) { + this.mode_container_permissions.css('display', mode == PermissionEditorMode.VISIBLE ? 'flex' : 'none'); + this.mode_container_error_permission.css('display', mode == PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); + this.mode_container_unset.css('display', mode == PermissionEditorMode.UNSET ? 'block' : 'none'); + if(mode == PermissionEditorMode.VISIBLE) + this.entry_editor.draw(true); + } + + update_ui() { + this.entry_editor.draw(true); + } + + set_toggle_button(callback: () => string, initial: string) { + throw "not implemented"; + } + + set_hidden_permissions(permissions: PermissionType[]) { + //TODO: Stuff here + } } \ No newline at end of file diff --git a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts index f1400da9..262a5e7d 100644 --- a/shared/js/ui/modal/permission/HTMLPermissionEditor.ts +++ b/shared/js/ui/modal/permission/HTMLPermissionEditor.ts @@ -1,530 +1,318 @@ /// /* first needs the AbstractPermissionEdit */ -namespace pe { - class HTMLPermission { - readonly handle: HTMLPermissionEditor; - readonly group: HTMLPermissionGroup; - readonly permission: PermissionInfo; - readonly index: number; - - tag: JQuery; - tag_name: JQuery; - tag_container_value: JQuery; - tag_container_granted: JQuery; - tag_container_skip: JQuery; - tag_container_negate: JQuery; - - hidden: boolean; - - /* the "actual" values */ - private _mask = 0; /* fourth bit: hidden by filer | third bit: value type | second bit: grant shown | first bit: value shown */ - - private _tag_value: JQuery; - private _tag_value_input: JQuery; - - private _tag_granted: JQuery; - private _tag_granted_input: JQuery; - - private _tag_skip: JQuery; - private _tag_skip_input: JQuery; - - private _tag_negate: JQuery; - private _tag_negate_input: JQuery; - - private _value: number | undefined; - private _grant: number | undefined; - private flags: number; /* 0x01 := Skip | 0x02 := Negate */ - - constructor(handle: HTMLPermissionEditor, group: HTMLPermissionGroup, permission: PermissionInfo, index: number) { - this.handle = handle; - this.permission = permission; - this.index = index; - this.group = group; - - this.build_tag(); - } - - private static build_checkbox() : {tag: JQuery, input: JQuery} { - let tag, input; - tag = $.spawn("label").addClass("switch").append([ - input = $.spawn("input").attr("type", "checkbox"), - $.spawn("span").addClass("slider").append( - $.spawn("div").addClass("dot") - ) - ]); - return {tag: tag, input: input}; - } - - private static number_filter_re = /^[-+]?([0-9]{0,9})$/; - private static number_filter = (event: KeyboardEvent) => { - if(event.ctrlKey) - return; - - const target = event.target; - if(event.key === "Enter") { - target.blur(); - return; - } - - if('keyCode' in event) { - /* everything under 46 is a control key except 32 its space */ - if(event.keyCode < 46 && event.keyCode != 32) - return; - - if(!HTMLPermission.number_filter_re.test(target.value + String.fromCharCode(event.keyCode))) { - event.preventDefault(); - return; - } - } else { - const e = event; /* for some reason typescript deducts the event type to "never" */ - if(!HTMLPermission.number_filter_re.test(e.key)) { - e.preventDefault(); - return; - } - } - }; - - private build_tag() { - this.tag = $.spawn("div").addClass("entry permission").css('padding-left', this.index + "em").append([ - this.tag_name = $.spawn("div").addClass("column-name").text(this.permission.name), - this.tag_container_value = $.spawn("div").addClass("column-value"), - this.tag_container_skip = $.spawn("div").addClass("column-skip"), - this.tag_container_negate = $.spawn("div").addClass("column-negate"), - this.tag_container_granted = $.spawn("div").addClass("column-granted") - ]); - - if(this.permission.is_boolean()) { - let value = HTMLPermission.build_checkbox(); - this._tag_value = value.tag; - this._tag_value_input = value.input; - - this._tag_value_input.on('change', event => { - const value = this._tag_value_input.prop('checked') ? 1 : 0; - - this.handle.trigger_change(this.permission, { - remove: false, - - value: value, - flag_skip: (this.flags & 0x01) > 0, - flag_negate: (this.flags & 0x02) > 0 - }).then(() => { - this._value = value; - }).catch(error => { - this._reset_value(); - }); - }); - - this._mask |= 0x04; - } else { - this._tag_value = $.spawn("input").addClass("number"); - this._tag_value_input = this._tag_value; - - this._tag_value_input.on('keydown', HTMLPermission.number_filter as any); - this._tag_value_input.on('change', event => { - const str_value = this._tag_value_input.val() as string; - const value = parseInt(str_value); - if(!HTMLPermission.number_filter_re.test(str_value) || isNaN(value)) { - console.warn(tr("Failed to parse given permission value string: %s"), this._tag_value_input.val()); - this._reset_value(); - return; - } - - this.handle.trigger_change(this.permission, { - remove: false, - - value: value, - flag_skip: (this.flags & 0x01) > 0, - flag_negate: (this.flags & 0x02) > 0 - }).then(() => { - this._value = value; - this._update_active_class(); - }).catch(error => { - this._reset_value(); - }); - }); - } - - { - let skip = HTMLPermission.build_checkbox(); - this._tag_skip = skip.tag; - this._tag_skip_input = skip.input; - - this._tag_skip_input.on('change', event => { - const value = this._tag_skip_input.prop('checked'); - - this.handle.trigger_change(this.permission, { - remove: false, - - value: this._value, - flag_skip: value, - flag_negate: (this.flags & 0x02) > 0 - }).then(() => { - if(value) - this.flags |= 0x01; - else - this.flags &= ~0x1; - this._update_active_class(); - }).catch(error => { - this._reset_value(); - }); - }); - } - - { - let negate = HTMLPermission.build_checkbox(); - this._tag_negate = negate.tag; - this._tag_negate_input = negate.input; - - this._tag_negate_input.on('change', event => { - const value = this._tag_negate_input.prop('checked'); - - console.log("Negate value: %o", value); - this.handle.trigger_change(this.permission, { - remove: false, - - value: this._value, - flag_skip: (this.flags & 0x01) > 0, - flag_negate: value - }).then(() => { - if(value) - this.flags |= 0x02; - else - this.flags &= ~0x2; - this._update_active_class(); - }).catch(error => { - this._reset_value(); - }); - }); - } - - { - this._tag_granted = $.spawn("input").addClass("number"); - this._tag_granted_input = this._tag_granted; - - this._tag_granted_input.on('keydown', HTMLPermission.number_filter as any); - this._tag_granted_input.on('change', event => { - const str_value = this._tag_granted_input.val() as string; - const value = parseInt(str_value); - if(!HTMLPermission.number_filter_re.test(str_value) || Number.isNaN(value)) { - console.warn(tr("Failed to parse given permission granted value string: %s"), this._tag_granted_input.val()); - this._reset_value(); - return; - } - - this.handle.trigger_change(this.permission, { - remove: false, - - granted: value - }).then(() => { - this._grant = value; - this._update_active_class(); - }).catch(error => { - this._reset_grant(); - }); - }); - } - - /* double click handler */ - { - this.tag.on('dblclick', event => this._trigger_value_assign()) - } - - /* context menu */ - { - this.tag.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - event.preventDefault(); - - let entries: contextmenu.MenuEntry[] = []; - if(typeof(this._value) === "undefined") { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add permission"), - callback: () => this._trigger_value_assign() - }); - } else { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Remove permission"), - callback: () => { - this.handle.trigger_change(this.permission, { - remove: true, - value: 0 - }).then(() => { - this.value(undefined); - }).catch(error => { - //We have to do nothing - }); - } - }); - } - - if(typeof(this._grant) === "undefined") { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add grant permission"), - callback: () => this._trigger_grant_assign() - }); - } else { - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Remove grant permission"), - callback: () => { - this.handle.trigger_change(this.permission, { - remove: true, - granted: 0 - }).then(() => { - this.granted(undefined); - }).catch(error => { - //We have to do nothing - }); - } - }); - } - entries.push(contextmenu.Entry.HR()); - if(this.group.collapsed) - entries.push({ /* This could never happen! */ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Expend group"), - callback: () => this.group.expend() - }); - else - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Collapse group"), - callback: () => this.group.collapse() - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Expend all"), - callback: () => this.handle.expend_all() - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Collapse all"), - callback: () => this.handle.collapse_all() - }); - entries.push(contextmenu.Entry.HR()); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Show permission description"), - callback: () => { - createInfoModal( - tr("Permission description"), - tr("Permission description for permission ") + this.permission.name + ":
" + this.permission.description - ).open(); - } - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Copy permission name"), - callback: () => { - copy_to_clipboard(this.permission.name); - } - }); - - contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); - }); - } - } - - private _trigger_value_assign() { - if(typeof(this._value) === "undefined") - this.value(this._grant || 1, false, false); //TODO: Use max granted value? - this._tag_value_input.focus(); - if(this.permission.is_boolean()) - this._tag_value_input.trigger('change'); - } - - private _trigger_grant_assign() { - this.granted(1); //TODO: Use max granted value? - this._tag_granted_input.focus(); - } - - hide() { - this._mask &= ~0x08; - for(const element of this.tag) - (element).style.display = 'none'; - } - - show() { - this._mask |= 0x08; - for(const element of this.tag) - (element).style.display = 'flex'; - } - - is_filtered() : boolean { - return (this._mask & 0x10) > 0; - } - - set_filtered(flag: boolean) { - if(flag) - this._mask |= 0x10; - else - this._mask &= ~0x10; - } - - is_set() : boolean { - return (this._mask & 0x03) > 0; - } - - get_value() { return this._value; } - - value(value: number | undefined, skip?: boolean, negate?: boolean) { - if(typeof value === "undefined") { - this._tag_value.detach(); - this._tag_negate.detach(); - this._tag_skip.detach(); - - this._value = undefined; - this.flags = 0; - - this._update_active_class(); - this._mask &= ~0x1; - return; - } - - if((this._mask & 0x1) == 0) { - this._tag_value.appendTo(this.tag_container_value); - this._tag_negate.appendTo(this.tag_container_negate); - this._tag_skip.appendTo(this.tag_container_skip); - - this._update_active_class(); - this._mask |= 0x01; - } - - if((this._mask & 0x04) > 0) - this._tag_value_input.prop('checked', !!value); - else - this._tag_value_input.val(value); - this._tag_skip_input.prop('checked', !!skip); - this._tag_negate_input.prop('checked', !!negate); - - this._value = value; - this.flags = (!!skip ? 0x01 : 0) | (!!negate ? 0x2 : 0); - } - - granted(value: number | undefined) { - if(typeof value === "undefined") { - this._tag_granted.detach(); - - this._update_active_class(); - this._grant = undefined; - this._mask &= ~0x2; - return; - } - - if((this._mask & 0x2) == 0) { - this._mask |= 0x02; - this._tag_granted.appendTo(this.tag_container_granted); - this._update_active_class(); - } - this._tag_granted_input.val(value); - this._grant = value; - } - - reset() { - this._mask &= ~0x03; - - this._tag_value.detach(); - this._tag_negate.detach(); - this._tag_skip.detach(); - - this._tag_granted.detach(); - - this._value = undefined; - this._grant = undefined; - this.flags = 0; - - const tag = this.tag[0] as HTMLDivElement; - tag.classList.remove("active"); - } - - private _reset_value() { - if(typeof(this._value) === "undefined") { - if((this._mask & 0x1) != 0) - this.value(undefined); - } else { - this.value(this._value, (this.flags & 0x1) > 1, (this.flags & 0x2) > 1); - } - } - - private _reset_grant() { - if(typeof(this._grant) === "undefined") { - if((this._mask & 0x2) != 0) - this.granted(undefined); - } else { - this.granted(this._grant); - } - } - - private _update_active_class() { - const value = typeof(this._value) !== "undefined" || typeof(this._grant) !== "undefined"; - const tag = this.tag[0] as HTMLDivElement; - if(value) - tag.classList.add("active"); - else - tag.classList.remove("active"); - } +import { + GroupedPermissions, + PermissionGroup, + PermissionInfo, + PermissionValue +} from "tc-shared/permission/PermissionManager"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {createInfoModal} from "tc-shared/ui/elements/Modal"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {IconManager} from "tc-shared/FileManager"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import { + AbstractPermissionEditor, + ChangedPermissionValue, + PermissionEditorMode +} from "tc-shared/ui/modal/permission/AbstractPermissionEditor"; + +class HTMLPermission { + readonly handle: HTMLPermissionEditor; + readonly group: HTMLPermissionGroup; + readonly permission: PermissionInfo; + readonly index: number; + + tag: JQuery; + tag_name: JQuery; + tag_container_value: JQuery; + tag_container_granted: JQuery; + tag_container_skip: JQuery; + tag_container_negate: JQuery; + + hidden: boolean; + + /* the "actual" values */ + private _mask = 0; /* fourth bit: hidden by filer | third bit: value type | second bit: grant shown | first bit: value shown */ + + private _tag_value: JQuery; + private _tag_value_input: JQuery; + + private _tag_granted: JQuery; + private _tag_granted_input: JQuery; + + private _tag_skip: JQuery; + private _tag_skip_input: JQuery; + + private _tag_negate: JQuery; + private _tag_negate_input: JQuery; + + private _value: number | undefined; + private _grant: number | undefined; + private flags: number; /* 0x01 := Skip | 0x02 := Negate */ + + constructor(handle: HTMLPermissionEditor, group: HTMLPermissionGroup, permission: PermissionInfo, index: number) { + this.handle = handle; + this.permission = permission; + this.index = index; + this.group = group; + + this.build_tag(); } - class HTMLPermissionGroup { - readonly handle: HTMLPermissionEditor; - readonly group: PermissionGroup; - readonly index: number; + private static build_checkbox() : {tag: JQuery, input: JQuery} { + let tag, input; + tag = $.spawn("label").addClass("switch").append([ + input = $.spawn("input").attr("type", "checkbox"), + $.spawn("span").addClass("slider").append( + $.spawn("div").addClass("dot") + ) + ]); + return {tag: tag, input: input}; + } - private _tag_arrow: JQuery; + private static number_filter_re = /^[-+]?([0-9]{0,9})$/; + private static number_filter = (event: KeyboardEvent) => { + if(event.ctrlKey) + return; - permissions: HTMLPermission[] = []; - children: HTMLPermissionGroup[] = []; - - tag: JQuery; - visible: boolean; - - collapsed: boolean; - parent_collapsed: boolean; - - constructor(handle: HTMLPermissionEditor, group: PermissionGroup, index: number) { - this.handle = handle; - this.group = group; - this.index = index; - - this._build_tag(); + const target = event.target; + if(event.key === "Enter") { + target.blur(); + return; } - private _build_tag() { - this.tag = $.spawn("div").addClass("entry group").css('padding-left', this.index + "em").append([ - $.spawn("div").addClass("column-name").append([ - this._tag_arrow = $.spawn("div").addClass("arrow down"), - $.spawn("div").addClass("group-name").text(this.group.name) - ]), - $.spawn("div").addClass("column-value"), - $.spawn("div").addClass("column-skip"), - $.spawn("div").addClass("column-negate"), - $.spawn("div").addClass("column-granted") - ]); + if('keyCode' in event) { + /* everything under 46 is a control key except 32 its space */ + if(event.keyCode < 46 && event.keyCode != 32) + return; + if(!HTMLPermission.number_filter_re.test(target.value + String.fromCharCode(event.keyCode))) { + event.preventDefault(); + return; + } + } else { + const e = event; /* for some reason typescript deducts the event type to "never" */ + if(!HTMLPermission.number_filter_re.test(e.key)) { + e.preventDefault(); + return; + } + } + }; + + private build_tag() { + this.tag = $.spawn("div").addClass("entry permission").css('padding-left', this.index + "em").append([ + this.tag_name = $.spawn("div").addClass("column-name").text(this.permission.name), + this.tag_container_value = $.spawn("div").addClass("column-value"), + this.tag_container_skip = $.spawn("div").addClass("column-skip"), + this.tag_container_negate = $.spawn("div").addClass("column-negate"), + this.tag_container_granted = $.spawn("div").addClass("column-granted") + ]); + + if(this.permission.is_boolean()) { + let value = HTMLPermission.build_checkbox(); + this._tag_value = value.tag; + this._tag_value_input = value.input; + + this._tag_value_input.on('change', event => { + const value = this._tag_value_input.prop('checked') ? 1 : 0; + + this.handle.trigger_change(this.permission, { + remove: false, + + value: value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + this._value = value; + }).catch(error => { + this._reset_value(); + }); + }); + + this._mask |= 0x04; + } else { + this._tag_value = $.spawn("input").addClass("number"); + this._tag_value_input = this._tag_value; + + this._tag_value_input.on('keydown', HTMLPermission.number_filter as any); + this._tag_value_input.on('change', event => { + const str_value = this._tag_value_input.val() as string; + const value = parseInt(str_value); + if(!HTMLPermission.number_filter_re.test(str_value) || isNaN(value)) { + console.warn(tr("Failed to parse given permission value string: %s"), this._tag_value_input.val()); + this._reset_value(); + return; + } + + this.handle.trigger_change(this.permission, { + remove: false, + + value: value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + this._value = value; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + + { + let skip = HTMLPermission.build_checkbox(); + this._tag_skip = skip.tag; + this._tag_skip_input = skip.input; + + this._tag_skip_input.on('change', event => { + const value = this._tag_skip_input.prop('checked'); + + this.handle.trigger_change(this.permission, { + remove: false, + + value: this._value, + flag_skip: value, + flag_negate: (this.flags & 0x02) > 0 + }).then(() => { + if(value) + this.flags |= 0x01; + else + this.flags &= ~0x1; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + + { + let negate = HTMLPermission.build_checkbox(); + this._tag_negate = negate.tag; + this._tag_negate_input = negate.input; + + this._tag_negate_input.on('change', event => { + const value = this._tag_negate_input.prop('checked'); + + console.log("Negate value: %o", value); + this.handle.trigger_change(this.permission, { + remove: false, + + value: this._value, + flag_skip: (this.flags & 0x01) > 0, + flag_negate: value + }).then(() => { + if(value) + this.flags |= 0x02; + else + this.flags &= ~0x2; + this._update_active_class(); + }).catch(error => { + this._reset_value(); + }); + }); + } + + { + this._tag_granted = $.spawn("input").addClass("number"); + this._tag_granted_input = this._tag_granted; + + this._tag_granted_input.on('keydown', HTMLPermission.number_filter as any); + this._tag_granted_input.on('change', event => { + const str_value = this._tag_granted_input.val() as string; + const value = parseInt(str_value); + if(!HTMLPermission.number_filter_re.test(str_value) || Number.isNaN(value)) { + console.warn(tr("Failed to parse given permission granted value string: %s"), this._tag_granted_input.val()); + this._reset_value(); + return; + } + + this.handle.trigger_change(this.permission, { + remove: false, + + granted: value + }).then(() => { + this._grant = value; + this._update_active_class(); + }).catch(error => { + this._reset_grant(); + }); + }); + } + + /* double click handler */ + { + this.tag.on('dblclick', event => this._trigger_value_assign()) + } + + /* context menu */ + { this.tag.on('contextmenu', event => { if(event.isDefaultPrevented()) return; event.preventDefault(); - const entries: contextmenu.MenuEntry[] = []; - if(this.collapsed) + let entries: contextmenu.MenuEntry[] = []; + if(typeof(this._value) === "undefined") { entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add permission"), + callback: () => this._trigger_value_assign() + }); + } else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Remove permission"), + callback: () => { + this.handle.trigger_change(this.permission, { + remove: true, + value: 0 + }).then(() => { + this.value(undefined); + }).catch(error => { + //We have to do nothing + }); + } + }); + } + + if(typeof(this._grant) === "undefined") { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add grant permission"), + callback: () => this._trigger_grant_assign() + }); + } else { + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Remove grant permission"), + callback: () => { + this.handle.trigger_change(this.permission, { + remove: true, + granted: 0 + }).then(() => { + this.granted(undefined); + }).catch(error => { + //We have to do nothing + }); + } + }); + } + entries.push(contextmenu.Entry.HR()); + if(this.group.collapsed) + entries.push({ /* This could never happen! */ type: contextmenu.MenuEntryType.ENTRY, name: tr("Expend group"), - callback: () => this.expend(), + callback: () => this.group.expend() }); else entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Collapse group"), - callback: () => this.collapse(), + callback: () => this.group.collapse() }); - entries.push(contextmenu.Entry.HR()); - entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Expend all"), @@ -535,415 +323,644 @@ namespace pe { name: tr("Collapse all"), callback: () => this.handle.collapse_all() }); + entries.push(contextmenu.Entry.HR()); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Show permission description"), + callback: () => { + createInfoModal( + tr("Permission description"), + tr("Permission description for permission ") + this.permission.name + ":
" + this.permission.description + ).open(); + } + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Copy permission name"), + callback: () => { + copy_to_clipboard(this.permission.name); + } + }); contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); }); - - this._tag_arrow.on('click', event => { - if(this.collapsed) - this.expend(); - else - this.collapse(); - }) - } - - update_visibility() { - let flag = false; - if (!flag) { - for (const group of this.children) { - if (group.visible) { - flag = true; - break; - } - } - } - - if (!flag) { - for (const permission of this.permissions) { - if (!permission.is_filtered()) { - flag = true; - break; - } - } - } - - this.visible = flag; - - flag = flag && !this.parent_collapsed; - for(const element of this.tag) - (element).style.display = flag ? 'flex' : 'none'; - - const arrow_node = this._tag_arrow[0]; - arrow_node.classList.remove(this.collapsed ? "down" : "right"); - arrow_node.classList.add(!this.collapsed ? "down" : "right"); - } - - collapse() { - this.collapsed = true; - - const children = [...this.children]; - while (true) { - const child = children.pop(); - if(!child) break; - - child.parent_collapsed = true; - children.push(...child.children); - } - - this.handle.update_view(); - } - - expend() { - this.collapsed = false; - - if(this.parent_collapsed) - return; - - const children = [...this.children]; - while (true) { - const child = children.pop(); - if(!child) break; - - child.parent_collapsed = false; - if(!child.collapsed) - children.push(...child.children); - } - - this.handle.update_view(); } } - export class HTMLPermissionEditor extends Modals.AbstractPermissionEditor { - container: JQuery; + private _trigger_value_assign() { + if(typeof(this._value) === "undefined") + this.value(this._grant || 1, false, false); //TODO: Use max granted value? + this._tag_value_input.focus(); + if(this.permission.is_boolean()) + this._tag_value_input.trigger('change'); + } - private mode_container_permissions: JQuery; - private mode_container_error_permission: JQuery; - private mode_container_unset: JQuery; + private _trigger_grant_assign() { + this.granted(1); //TODO: Use max granted value? + this._tag_granted_input.focus(); + } - private icon_shown: boolean; + hide() { + this._mask &= ~0x08; + for(const element of this.tag) + (element).style.display = 'none'; + } - private filter_input: JQuery; - private filter_grant: JQuery; + show() { + this._mask |= 0x08; + for(const element of this.tag) + (element).style.display = 'flex'; + } - private hidden_permissions: PermissionType[]; + is_filtered() : boolean { + return (this._mask & 0x10) > 0; + } - private button_toggle: JQuery; + set_filtered(flag: boolean) { + if(flag) + this._mask |= 0x10; + else + this._mask &= ~0x10; + } - private even_list: ({ visible() : boolean; set_even(flag: boolean); })[]; - private permission_map: Array; - private permission_groups: HTMLPermissionGroup[]; + is_set() : boolean { + return (this._mask & 0x03) > 0; + } - constructor() { - super(); + get_value() { return this._value; } + + value(value: number | undefined, skip?: boolean, negate?: boolean) { + if(typeof value === "undefined") { + this._tag_value.detach(); + this._tag_negate.detach(); + this._tag_skip.detach(); + + this._value = undefined; + this.flags = 0; + + this._update_active_class(); + this._mask &= ~0x1; + return; } - initialize(permissions: GroupedPermissions[]) { - this._permissions = permissions; - this.build_tag(); + if((this._mask & 0x1) == 0) { + this._tag_value.appendTo(this.tag_container_value); + this._tag_negate.appendTo(this.tag_container_negate); + this._tag_skip.appendTo(this.tag_container_skip); + + this._update_active_class(); + this._mask |= 0x01; } - set_hidden_permissions(permissions: PermissionType[]) { - this.hidden_permissions = permissions; - this.update_filter(); + if((this._mask & 0x04) > 0) + this._tag_value_input.prop('checked', !!value); + else + this._tag_value_input.val(value); + this._tag_skip_input.prop('checked', !!skip); + this._tag_negate_input.prop('checked', !!negate); + + this._value = value; + this.flags = (!!skip ? 0x01 : 0) | (!!negate ? 0x2 : 0); + } + + granted(value: number | undefined) { + if(typeof value === "undefined") { + this._tag_granted.detach(); + + this._update_active_class(); + this._grant = undefined; + this._mask &= ~0x2; + return; } - private update_filter() { - const value = (this.filter_input.val() as string).toLowerCase(); - const grant = !!this.filter_grant.prop('checked'); - - const _filter = (permission: HTMLPermission) => { - if(value && permission.permission.name.indexOf(value) == -1) return false; - if(grant && !permission.is_set()) return false; - if(this.hidden_permissions && this.hidden_permissions.find(e => e && e.toLocaleLowerCase() == permission.permission.name.toLowerCase())) - return false; - - return true; - }; - - for(let id = 1; id < this.permission_map.length; id++) { - const permission = this.permission_map[id]; - let flag = _filter(permission); - permission.set_filtered(!flag); - - - flag = flag && !permission.group.collapsed && !permission.group.parent_collapsed; /* hide when parent is filtered */ - if(flag) permission.show(); - else permission.hide(); - } - - /* run in both directions, to update the parent visibility and the actiual visibility */ - for(const group of this.permission_groups) - group.update_visibility(); - for(const group of this.permission_groups.slice().reverse()) - group.update_visibility(); - - - let index = 0; - for(const entry of this.even_list) { - if(!entry.visible()) continue; - entry.set_even((index++ & 0x1) == 0); - } + if((this._mask & 0x2) == 0) { + this._mask |= 0x02; + this._tag_granted.appendTo(this.tag_container_granted); + this._update_active_class(); } + this._tag_granted_input.val(value); + this._grant = value; + } - private update_icon() { - const permission = this.icon_shown ? this.permission_map.find(e => e && e.permission.name === "i_icon_id") : undefined; - const icon_id = permission ? permission.get_value() : 0; + reset() { + this._mask &= ~0x03; - const icon_node = this.container.find(".container-icon-select .icon-preview"); - icon_node.children().remove(); + this._tag_value.detach(); + this._tag_negate.detach(); + this._tag_skip.detach(); - let resolve: Promise>; - if(icon_id >= 0 && icon_id <= 1000) - resolve = Promise.resolve(IconManager.generate_tag({id: icon_id, url: ""})); - else - resolve = this.icon_resolver(permission ? permission.get_value() : 0).then(e => $(e)); + this._tag_granted.detach(); - resolve.then(tag => tag.appendTo(icon_node)) - .catch(error => { - log.error(LogCategory.PERMISSIONS, tr("Failed to generate empty icon preview: %o"), error); + this._value = undefined; + this._grant = undefined; + this.flags = 0; + + const tag = this.tag[0] as HTMLDivElement; + tag.classList.remove("active"); + } + + private _reset_value() { + if(typeof(this._value) === "undefined") { + if((this._mask & 0x1) != 0) + this.value(undefined); + } else { + this.value(this._value, (this.flags & 0x1) > 1, (this.flags & 0x2) > 1); + } + } + + private _reset_grant() { + if(typeof(this._grant) === "undefined") { + if((this._mask & 0x2) != 0) + this.granted(undefined); + } else { + this.granted(this._grant); + } + } + + private _update_active_class() { + const value = typeof(this._value) !== "undefined" || typeof(this._grant) !== "undefined"; + const tag = this.tag[0] as HTMLDivElement; + if(value) + tag.classList.add("active"); + else + tag.classList.remove("active"); + } +} + +class HTMLPermissionGroup { + readonly handle: HTMLPermissionEditor; + readonly group: PermissionGroup; + readonly index: number; + + private _tag_arrow: JQuery; + + permissions: HTMLPermission[] = []; + children: HTMLPermissionGroup[] = []; + + tag: JQuery; + visible: boolean; + + collapsed: boolean; + parent_collapsed: boolean; + + constructor(handle: HTMLPermissionEditor, group: PermissionGroup, index: number) { + this.handle = handle; + this.group = group; + this.index = index; + + this._build_tag(); + } + + private _build_tag() { + this.tag = $.spawn("div").addClass("entry group").css('padding-left', this.index + "em").append([ + $.spawn("div").addClass("column-name").append([ + this._tag_arrow = $.spawn("div").addClass("arrow down"), + $.spawn("div").addClass("group-name").text(this.group.name) + ]), + $.spawn("div").addClass("column-value"), + $.spawn("div").addClass("column-skip"), + $.spawn("div").addClass("column-negate"), + $.spawn("div").addClass("column-granted") + ]); + + this.tag.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + event.preventDefault(); + + const entries: contextmenu.MenuEntry[] = []; + if(this.collapsed) + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Expend group"), + callback: () => this.expend(), }); + else + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Collapse group"), + callback: () => this.collapse(), + }); + entries.push(contextmenu.Entry.HR()); + + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Expend all"), + callback: () => this.handle.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Collapse all"), + callback: () => this.handle.collapse_all() + }); + + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + }); + + this._tag_arrow.on('click', event => { + if(this.collapsed) + this.expend(); + else + this.collapse(); + }) + } + + update_visibility() { + let flag = false; + if (!flag) { + for (const group of this.children) { + if (group.visible) { + flag = true; + break; + } + } } - private build_tag() { - this.container = $("#tmpl_permission_editor_html").renderTag(); - this.container.find("input").on('change', event => { - $(event.target).parents(".form-group").toggleClass('is-filled', !!(event.target as HTMLInputElement).value); - }); - - /* search for that as long we've not that much nodes */ - this.mode_container_permissions = this.container.find(".container-mode-permissions"); - this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); - this.mode_container_unset = this.container.find(".container-mode-unset"); - - this.filter_input = this.container.find(".filter-input"); - this.filter_input.on('change keyup', event => this.update_filter()); - - this.filter_grant = this.container.find(".filter-granted"); - this.filter_grant.on('change', event => this.update_filter()); - - this.button_toggle = this.container.find(".button-toggle-clients"); - this.button_toggle.on('click', () => { - if(this._toggle_callback) - this.button_toggle.text(this._toggle_callback()); - }); - - this.container.find(".button-update").on('click', event => this.trigger_update()); - - /* allocate array space */ - { - let max_index = 0; - let tmp: GroupedPermissions[] = []; - while(true) { - const entry = tmp.pop(); - if(!entry) break; - for(const permission of entry.permissions) - if(permission.id > max_index) - max_index = permission.id; - tmp.push(...entry.children); + if (!flag) { + for (const permission of this.permissions) { + if (!permission.is_filtered()) { + flag = true; + break; } - this.permission_map = new Array(max_index + 1); } - this.permission_groups = []; - this.even_list = []; + } - { - const container_permission = this.mode_container_permissions.find(".container-permission-list .body"); + this.visible = flag; - const build_group = (pgroup: HTMLPermissionGroup, group: GroupedPermissions, index: number) => { - const hgroup = new HTMLPermissionGroup(this, group.group, index); - hgroup.tag.appendTo(container_permission); + flag = flag && !this.parent_collapsed; + for(const element of this.tag) + (element).style.display = flag ? 'flex' : 'none'; + + const arrow_node = this._tag_arrow[0]; + arrow_node.classList.remove(this.collapsed ? "down" : "right"); + arrow_node.classList.add(!this.collapsed ? "down" : "right"); + } + + collapse() { + this.collapsed = true; + + const children = [...this.children]; + while (true) { + const child = children.pop(); + if(!child) break; + + child.parent_collapsed = true; + children.push(...child.children); + } + + this.handle.update_view(); + } + + expend() { + this.collapsed = false; + + if(this.parent_collapsed) + return; + + const children = [...this.children]; + while (true) { + const child = children.pop(); + if(!child) break; + + child.parent_collapsed = false; + if(!child.collapsed) + children.push(...child.children); + } + + this.handle.update_view(); + } +} + +export class HTMLPermissionEditor extends AbstractPermissionEditor { + container: JQuery; + + private mode_container_permissions: JQuery; + private mode_container_error_permission: JQuery; + private mode_container_unset: JQuery; + + private icon_shown: boolean; + + private filter_input: JQuery; + private filter_grant: JQuery; + + private hidden_permissions: PermissionType[]; + + private button_toggle: JQuery; + + private even_list: ({ visible() : boolean; set_even(flag: boolean); })[]; + private permission_map: Array; + private permission_groups: HTMLPermissionGroup[]; + + constructor() { + super(); + } + + initialize(permissions: GroupedPermissions[]) { + this._permissions = permissions; + this.build_tag(); + } + + set_hidden_permissions(permissions: PermissionType[]) { + this.hidden_permissions = permissions; + this.update_filter(); + } + + private update_filter() { + const value = (this.filter_input.val() as string).toLowerCase(); + const grant = !!this.filter_grant.prop('checked'); + + const _filter = (permission: HTMLPermission) => { + if(value && permission.permission.name.indexOf(value) == -1) return false; + if(grant && !permission.is_set()) return false; + if(this.hidden_permissions && this.hidden_permissions.find(e => e && e.toLocaleLowerCase() == permission.permission.name.toLowerCase())) + return false; + + return true; + }; + + for(let id = 1; id < this.permission_map.length; id++) { + const permission = this.permission_map[id]; + let flag = _filter(permission); + permission.set_filtered(!flag); + + + flag = flag && !permission.group.collapsed && !permission.group.parent_collapsed; /* hide when parent is filtered */ + if(flag) permission.show(); + else permission.hide(); + } + + /* run in both directions, to update the parent visibility and the actiual visibility */ + for(const group of this.permission_groups) + group.update_visibility(); + for(const group of this.permission_groups.slice().reverse()) + group.update_visibility(); + + + let index = 0; + for(const entry of this.even_list) { + if(!entry.visible()) continue; + entry.set_even((index++ & 0x1) == 0); + } + } + + private update_icon() { + const permission = this.icon_shown ? this.permission_map.find(e => e && e.permission.name === "i_icon_id") : undefined; + const icon_id = permission ? permission.get_value() : 0; + + const icon_node = this.container.find(".container-icon-select .icon-preview"); + icon_node.children().remove(); + + let resolve: Promise>; + if(icon_id >= 0 && icon_id <= 1000) + resolve = Promise.resolve(IconManager.generate_tag({id: icon_id, url: ""})); + else + resolve = this.icon_resolver(permission ? permission.get_value() : 0).then(e => $(e)); + + resolve.then(tag => tag.appendTo(icon_node)) + .catch(error => { + log.error(LogCategory.PERMISSIONS, tr("Failed to generate empty icon preview: %o"), error); + }); + } + + private build_tag() { + this.container = $("#tmpl_permission_editor_html").renderTag(); + this.container.find("input").on('change', event => { + $(event.target).parents(".form-group").toggleClass('is-filled', !!(event.target as HTMLInputElement).value); + }); + + /* search for that as long we've not that much nodes */ + this.mode_container_permissions = this.container.find(".container-mode-permissions"); + this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); + this.mode_container_unset = this.container.find(".container-mode-unset"); + + this.filter_input = this.container.find(".filter-input"); + this.filter_input.on('change keyup', event => this.update_filter()); + + this.filter_grant = this.container.find(".filter-granted"); + this.filter_grant.on('change', event => this.update_filter()); + + this.button_toggle = this.container.find(".button-toggle-clients"); + this.button_toggle.on('click', () => { + if(this._toggle_callback) + this.button_toggle.text(this._toggle_callback()); + }); + + this.container.find(".button-update").on('click', event => this.trigger_update()); + + /* allocate array space */ + { + let max_index = 0; + let tmp: GroupedPermissions[] = []; + while(true) { + const entry = tmp.pop(); + if(!entry) break; + for(const permission of entry.permissions) + if(permission.id > max_index) + max_index = permission.id; + tmp.push(...entry.children); + } + this.permission_map = new Array(max_index + 1); + } + this.permission_groups = []; + this.even_list = []; + + { + const container_permission = this.mode_container_permissions.find(".container-permission-list .body"); + + const build_group = (pgroup: HTMLPermissionGroup, group: GroupedPermissions, index: number) => { + const hgroup = new HTMLPermissionGroup(this, group.group, index); + hgroup.tag.appendTo(container_permission); + this.even_list.push({ + set_even(flag: boolean) { + if(flag) + hgroup.tag[0].classList.add('even'); + else + hgroup.tag[0].classList.remove('even'); + }, + + visible(): boolean { + return !hgroup.parent_collapsed && hgroup.visible; + } + }); + + if(pgroup) + pgroup.children.push(hgroup); + this.permission_groups.push(hgroup); + + index++; + for(const child of group.children) + build_group(hgroup, child, index); + + for(const permission of group.permissions) { + const perm = new HTMLPermission(this, hgroup, permission, index); + this.permission_map[perm.permission.id] = perm; + perm.tag.appendTo(container_permission); + hgroup.permissions.push(perm); this.even_list.push({ set_even(flag: boolean) { if(flag) - hgroup.tag[0].classList.add('even'); + perm.tag[0].classList.add('even'); else - hgroup.tag[0].classList.remove('even'); + perm.tag[0].classList.remove('even'); }, visible(): boolean { - return !hgroup.parent_collapsed && hgroup.visible; + return !perm.is_filtered() && !perm.group.collapsed && !perm.group.parent_collapsed; } }); + } + }; - if(pgroup) - pgroup.children.push(hgroup); - this.permission_groups.push(hgroup); + for(const group of this._permissions) + build_group(undefined, group, 0); + } - index++; - for(const child of group.children) - build_group(hgroup, child, index); - - for(const permission of group.permissions) { - const perm = new HTMLPermission(this, hgroup, permission, index); - this.permission_map[perm.permission.id] = perm; - perm.tag.appendTo(container_permission); - hgroup.permissions.push(perm); - this.even_list.push({ - set_even(flag: boolean) { - if(flag) - perm.tag[0].classList.add('even'); - else - perm.tag[0].classList.remove('even'); - }, - - visible(): boolean { - return !perm.is_filtered() && !perm.group.collapsed && !perm.group.parent_collapsed; - } - }); - } - }; - - for(const group of this._permissions) - build_group(undefined, group, 0); - } - - { - const container = this.container.find(".container-icon-select"); - container.find(".button-select-icon").on('click', event => { - const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); - this.icon_selector(permission ? permission.get_value() : 0).then(id => { - const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); - if(permission) { - this.trigger_change(permission.permission, { - remove: false, - value: id, - flag_skip: false, - flag_negate: false - }, false).then(() => { - log.debug(LogCategory.PERMISSIONS, tr("Selected new icon %s"), id); - - permission.value(id, false, false); - this.update_icon(); - }).catch(error => { - log.warn(LogCategory.PERMISSIONS, tr("Failed to set icon permission within permission editor: %o"), error); - }); - } else { - log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permissions within permission editor")); - } - }).catch(error => { - log.error(LogCategory.PERMISSIONS, tr("Failed to select an icon for the icon permission: %o"), error); - }); - }); - - container.find(".button-icon-remove").on('click', event => { + { + const container = this.container.find(".container-icon-select"); + container.find(".button-select-icon").on('click', event => { + const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); + this.icon_selector(permission ? permission.get_value() : 0).then(id => { const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); if(permission) { this.trigger_change(permission.permission, { - remove: true, + remove: false, + value: id, + flag_skip: false, + flag_negate: false }, false).then(() => { - permission.value(undefined); + log.debug(LogCategory.PERMISSIONS, tr("Selected new icon %s"), id); + + permission.value(id, false, false); this.update_icon(); }).catch(error => { - log.warn(LogCategory.PERMISSIONS, tr("Failed to remove icon permission within permission editor: %o"), error); + log.warn(LogCategory.PERMISSIONS, tr("Failed to set icon permission within permission editor: %o"), error); }); } else { - log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permission within permission editor")); + log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permissions within permission editor")); } + }).catch(error => { + log.error(LogCategory.PERMISSIONS, tr("Failed to select an icon for the icon permission: %o"), error); }); - } - - this.mode_container_permissions.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - event.preventDefault(); - - const entries: contextmenu.MenuEntry[] = []; - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Expend all"), - callback: () => this.expend_all() - }); - entries.push({ - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Collapse all"), - callback: () => this.collapse_all() - }); - - contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); }); - this.set_mode(Modals.PermissionEditorMode.UNSET); - } - - html_tag(): JQuery { - return this.container; - } - - set_permissions(u_permissions?: PermissionValue[]) { - const permissions = new Array(this.permission_map.length); - - /* initialize update array, boundary checks are already made by js */ - for(const perm of u_permissions) - permissions[perm.type.id] = perm; - - /* there is no permission with id 0 */ - for(let id = 1; id < permissions.length; id++) { - const new_permission = permissions[id]; - const permission_handle = this.permission_map[id]; - if(!new_permission) { - permission_handle.reset(); - continue; - } - - permission_handle.value(new_permission.value, new_permission.flag_skip, new_permission.flag_negate); - permission_handle.granted(new_permission.granted_value); - } - - this.update_icon(); - this.update_filter(); - } - - set_mode(mode: Modals.PermissionEditorMode) { - this.mode_container_permissions.css('display', mode == Modals.PermissionEditorMode.VISIBLE ? 'flex' : 'none'); - this.mode_container_error_permission.css('display', mode == Modals.PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); - this.mode_container_unset.css('display', mode == Modals.PermissionEditorMode.UNSET ? 'block' : 'none'); - if(this.icon_shown != (mode == Modals.PermissionEditorMode.VISIBLE)) { - this.icon_shown = mode == Modals.PermissionEditorMode.VISIBLE; - this.update_icon(); - } - } - - trigger_change(permission: PermissionInfo, value?: Modals.PermissionEditor.PermissionValue, update_icon?: boolean) : Promise { - if(this._listener_change) { - if((typeof(update_icon) !== "boolean" || update_icon) && permission && permission.name === "i_icon_id") - return this._listener_change(permission, value).then(e => { - setTimeout(() => this.update_icon(), 0); /* we need to fully handle the response and then only we're able to update the icon */ - return e; + container.find(".button-icon-remove").on('click', event => { + const permission = this.permission_map.find(e => e && e.permission.name === "i_icon_id"); + if(permission) { + this.trigger_change(permission.permission, { + remove: true, + }, false).then(() => { + permission.value(undefined); + this.update_icon(); + }).catch(error => { + log.warn(LogCategory.PERMISSIONS, tr("Failed to remove icon permission within permission editor: %o"), error); }); - else - return this._listener_change(permission, value); - } - - return Promise.reject(); + } else { + log.warn(LogCategory.PERMISSIONS, tr("Failed to find icon permission within permission editor")); + } + }); } - collapse_all() { - for(const group of this.permission_groups) { - group.collapsed = true; - for(const child of group.children) - child.parent_collapsed = true; + this.mode_container_permissions.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + event.preventDefault(); + + const entries: contextmenu.MenuEntry[] = []; + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Expend all"), + callback: () => this.expend_all() + }); + entries.push({ + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Collapse all"), + callback: () => this.collapse_all() + }); + + contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries); + }); + + this.set_mode(PermissionEditorMode.UNSET); + } + + html_tag(): JQuery { + return this.container; + } + + set_permissions(u_permissions?: PermissionValue[]) { + const permissions = new Array(this.permission_map.length); + + /* initialize update array, boundary checks are already made by js */ + for(const perm of u_permissions) + permissions[perm.type.id] = perm; + + /* there is no permission with id 0 */ + for(let id = 1; id < permissions.length; id++) { + const new_permission = permissions[id]; + const permission_handle = this.permission_map[id]; + if(!new_permission) { + permission_handle.reset(); + continue; } - this.update_filter(); /* update display state of all entries */ + + permission_handle.value(new_permission.value, new_permission.flag_skip, new_permission.flag_negate); + permission_handle.granted(new_permission.granted_value); } - expend_all() { - for(const group of this.permission_groups) { - group.collapsed = false; - group.parent_collapsed = false; - } - this.update_filter(); /* update display state of all entries */ + this.update_icon(); + this.update_filter(); + } + + set_mode(mode: PermissionEditorMode) { + this.mode_container_permissions.css('display', mode == PermissionEditorMode.VISIBLE ? 'flex' : 'none'); + this.mode_container_error_permission.css('display', mode == PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); + this.mode_container_unset.css('display', mode == PermissionEditorMode.UNSET ? 'block' : 'none'); + if(this.icon_shown != (mode == PermissionEditorMode.VISIBLE)) { + this.icon_shown = mode == PermissionEditorMode.VISIBLE; + this.update_icon(); + } + } + + trigger_change(permission: PermissionInfo, value?: ChangedPermissionValue, update_icon?: boolean) : Promise { + if(this._listener_change) { + if((typeof(update_icon) !== "boolean" || update_icon) && permission && permission.name === "i_icon_id") + return this._listener_change(permission, value).then(e => { + setTimeout(() => this.update_icon(), 0); /* we need to fully handle the response and then only we're able to update the icon */ + return e; + }); + else + return this._listener_change(permission, value); } - update_view() { return this.update_filter(); } + return Promise.reject(); + } - set_toggle_button(callback: () => string, initial: string) { - this._toggle_callback = callback; - if(this._toggle_callback) { - this.button_toggle.text(initial); - this.button_toggle.show(); - } else { - this.button_toggle.hide(); - } + collapse_all() { + for(const group of this.permission_groups) { + group.collapsed = true; + for(const child of group.children) + child.parent_collapsed = true; + } + this.update_filter(); /* update display state of all entries */ + } + + expend_all() { + for(const group of this.permission_groups) { + group.collapsed = false; + group.parent_collapsed = false; + } + this.update_filter(); /* update display state of all entries */ + } + + update_view() { return this.update_filter(); } + + set_toggle_button(callback: () => string, initial: string) { + this._toggle_callback = callback; + if(this._toggle_callback) { + this.button_toggle.text(initial); + this.button_toggle.show(); + } else { + this.button_toggle.hide(); } } } \ No newline at end of file diff --git a/shared/js/ui/modal/permission/ModalPermissionEdit.ts b/shared/js/ui/modal/permission/ModalPermissionEdit.ts index f405dd9c..69e0bb91 100644 --- a/shared/js/ui/modal/permission/ModalPermissionEdit.ts +++ b/shared/js/ui/modal/permission/ModalPermissionEdit.ts @@ -1,497 +1,310 @@ -/// -/// -/// +import { + PermissionManager, +} from "tc-shared/permission/PermissionManager"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import {createErrorModal, createInfoModal, createInputModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; +import {HTMLPermissionEditor} from "tc-shared/ui/modal/permission/HTMLPermissionEditor"; +import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect"; +import {CanvasPermissionEditor} from "tc-shared/ui/modal/permission/CanvasPermissionEditor"; +import {Settings, settings} from "tc-shared/settings"; +import {ChannelEntry} from "tc-shared/ui/channel"; +import {LogCategory} from "tc-shared/log"; +import {CommandResult, ErrorID} from "tc-shared/connection/ServerConnectionDeclaration"; +import * as log from "tc-shared/log"; +import {Group, GroupManager, GroupTarget, GroupType} from "tc-shared/permission/GroupManager"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import {copy_to_clipboard} from "tc-shared/utils/helpers"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import { + senseless_channel_group_permissions, + senseless_channel_permissions, senseless_client_channel_permissions, + senseless_client_permissions, senseless_server_group_permissions +} from "tc-shared/ui/modal/permission/SenselessPermissions"; +import {AbstractPermissionEditor, PermissionEditorMode} from "tc-shared/ui/modal/permission/AbstractPermissionEditor"; -interface JQuery { - dropdown: any; +declare global { + interface JQuery { + dropdown: any; + } } -namespace Modals { - export namespace PermissionEditor { - export interface PermissionEntry { - tag: JQuery; - tag_value: JQuery; - tag_grant: JQuery; - tag_flag_negate: JQuery; - tag_flag_skip: JQuery; +export type OptionsServerGroup = {}; +export type OptionsChannelGroup = {}; +export type OptionsClientPermissions = { unique_id?: string }; +export type OptionsChannelPermissions = { channel_id?: number }; +export type OptionsClientChannelPermissions = OptionsClientPermissions & OptionsChannelPermissions; +export interface OptionMap { + "sg": OptionsServerGroup, + "cg": OptionsChannelGroup, + "clp": OptionsClientPermissions, + "chp": OptionsChannelPermissions, + "clchp": OptionsClientChannelPermissions +} - id: number; - filter: string; - is_bool: boolean; +export function _space() { + const now = Date.now(); + while(now + 100 > Date.now()); +} - } +export function spawnPermissionEdit(connection: ConnectionHandler, selected_tab?: T, options?: OptionMap[T]) : Modal { + options = options || {}; - export interface PermissionValue { - remove: boolean; /* if set remove the set permission (value or granted) */ + const modal = createModal({ + header: function() { + return tr("Server Permissions"); + }, + body: function () { + let properties: any = {}; + let tag = $("#tmpl_server_permissions").renderTag(properties); - granted?: number; - value?: number; + /* build the permission editor */ + const permission_editor: AbstractPermissionEditor = (() => { + const editor = new HTMLPermissionEditor(); + editor.initialize(connection.permissions.groupedPermissions()); + editor.icon_resolver = id => connection.fileManager.icons.resolve_icon(id).then(async icon => { + if(!icon) + return undefined; - flag_skip?: boolean; - flag_negate?: boolean; - } - - export type change_listener_t = (permission: PermissionInfo, value?: PermissionEditor.PermissionValue) => Promise; - } - - export enum PermissionEditorMode { - VISIBLE, - NO_PERMISSION, - UNSET - } - - export abstract class AbstractPermissionEditor { - protected _permissions: GroupedPermissions[]; - protected _listener_update: () => any; - protected _listener_change: PermissionEditor.change_listener_t = () => Promise.resolve(); - protected _toggle_callback: () => string; - - icon_resolver: (id: number) => Promise; - icon_selector: (current_id: number) => Promise; - - protected constructor() {} - - abstract set_mode(mode: PermissionEditorMode); - - abstract initialize(permissions: GroupedPermissions[]); - abstract html_tag() : JQuery; - abstract set_permissions(permissions?: PermissionValue[]); - abstract set_hidden_permissions(permissions: PermissionType[]); - - set_listener(listener?: PermissionEditor.change_listener_t) { - this._listener_change = listener || (() => Promise.resolve()); - } - - set_listener_update(listener?: () => any) { this._listener_update = listener; } - trigger_update() { if(this._listener_update) this._listener_update(); } - - abstract set_toggle_button(callback: () => string, initial: string); - } - - export type OptionsServerGroup = {}; - export type OptionsChannelGroup = {}; - export type OptionsClientPermissions = { unique_id?: string }; - export type OptionsChannelPermissions = { channel_id?: number }; - export type OptionsClientChannelPermissions = OptionsClientPermissions & OptionsChannelPermissions; - export interface OptionMap { - "sg": OptionsServerGroup, - "cg": OptionsChannelGroup, - "clp": OptionsClientPermissions, - "chp": OptionsChannelPermissions, - "clchp": OptionsClientChannelPermissions - } - - export function _space() { - const now = Date.now(); - while(now + 100 > Date.now()); - } - - export function spawnPermissionEdit(connection: ConnectionHandler, selected_tab?: T, options?: OptionMap[T]) : Modal { - options = options || {}; - - const modal = createModal({ - header: function() { - return tr("Server Permissions"); - }, - body: function () { - let properties: any = {}; - let tag = $("#tmpl_server_permissions").renderTag(properties); - - /* build the permission editor */ - const permission_editor: AbstractPermissionEditor = (() => { - const editor = new pe.HTMLPermissionEditor(); - editor.initialize(connection.permissions.groupedPermissions()); - editor.icon_resolver = id => connection.fileManager.icons.resolve_icon(id).then(async icon => { - if(!icon) - return undefined; - - const tag = document.createElement("img"); - await new Promise((resolve, reject) => { - tag.onerror = reject; - tag.onload = resolve; - tag.src = icon.url; - }); - return tag; - }); - editor.icon_selector = current_icon => new Promise(resolve => { - spawnIconSelect(connection, id => resolve(new Int32Array([id])[0]), current_icon); + const tag = document.createElement("img"); + await new Promise((resolve, reject) => { + tag.onerror = reject; + tag.onload = resolve; + tag.src = icon.url; }); + return tag; + }); + editor.icon_selector = current_icon => new Promise(resolve => { + spawnIconSelect(connection, id => resolve(new Int32Array([id])[0]), current_icon); + }); - if(editor instanceof pe.CanvasPermissionEditor) - setTimeout(() => editor.update_ui(), 500); - return editor; - })(); - - const container_tab_list = tag.find(".right > .header"); - { - const label_current = tag.find(".left .container-selected"); - const create_tab = (tab_entry: JQuery, container_name: string, hidden_permissions: PermissionType[]) => { - const target_container = tag.find(".body .container." + container_name); - - tab_entry.on('click', () => { - /* Using a timeout here prevents unnecessary style calculations required by other click event handlers */ - setTimeout(() => { - container_tab_list.find(".selected").removeClass("selected"); - tab_entry.addClass("selected"); - label_current.text(tab_entry.find("a").text()); - - /* dont use show() here because it causes a style recalculation */ - for(const element of tag.find(".body .container")) - (element).style.display = "none"; - - permission_editor.set_hidden_permissions(settings.static_global(Settings.KEY_PERMISSIONS_SHOW_ALL) ? undefined : hidden_permissions); - permission_editor.html_tag()[0].remove(); - target_container.find(".permission-editor").trigger('show'); - target_container.find(".permission-editor").append(permission_editor.html_tag()); - - for(const element of target_container) - (element).style.display = null; - }, 0); - }); - }; - - create_tab(container_tab_list.find(".sg"), "container-view-server-groups", permissions.senseless_server_group_permissions); - create_tab(container_tab_list.find(".cg"), "container-view-channel-groups", permissions.senseless_channel_group_permissions); - create_tab(container_tab_list.find(".chp"), "container-view-channel-permissions", permissions.senseless_channel_permissions); - create_tab(container_tab_list.find(".clp"), "container-view-client-permissions", permissions.senseless_client_permissions); - create_tab(container_tab_list.find(".clchp"), "container-view-client-channel-permissions", permissions.senseless_client_channel_permissions); - } - - apply_server_groups(connection, permission_editor, tag.find(".left .container-view-server-groups"), tag.find(".right .container-view-server-groups")); - apply_channel_groups(connection, permission_editor, tag.find(".left .container-view-channel-groups"), tag.find(".right .container-view-channel-groups")); - apply_channel_permission(connection, permission_editor, tag.find(".left .container-view-channel-permissions"), tag.find(".right .container-view-channel-permissions")); - apply_client_permission(connection, permission_editor, tag.find(".left .container-view-client-permissions"), tag.find(".right .container-view-client-permissions"), selected_tab == "clp" ? options : {}); - apply_client_channel_permission(connection, permission_editor, tag.find(".left .container-view-client-channel-permissions"), tag.find(".right .container-view-client-channel-permissions"), selected_tab == "clchp" ? options : {}); - - setTimeout(() => container_tab_list.find("." + (selected_tab || "sg")).trigger('click'), 0); - return tag.dividerfy(); - }, - footer: undefined, - - min_width: "30em", - height: "80%", - trigger_tab: false, - full_size: true - }); - - const tag = modal.htmlTag; - tag.find(".modal-body").addClass("modal-permission-editor"); - if(selected_tab) - setTimeout(() => tag.find(".tab-header .entry[x-id=" + selected_tab + "]").first().trigger("click"), 1); - tag.find(".btn_close").on('click', () => { - modal.close(); - }); - - return modal; - } - - function build_channel_tree(connection: ConnectionHandler, channel_list: JQuery, selected_channel: number, select_callback: (channel: ChannelEntry, icon_update: (id: number) => any) => any) { - const root = connection.channelTree.get_first_channel(); - if(!root) return; - - const build_channel = (channel: ChannelEntry, level: number) => { - let tag = $.spawn("div").addClass("channel").css("padding-left", "calc(0.25em + " + (level * 16) + "px)").attr("channel-id", channel.channelId); - let icon_tag = connection.fileManager.icons.generateTag(channel.properties.channel_icon_id); - icon_tag.appendTo(tag); - const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + if(editor instanceof CanvasPermissionEditor) + setTimeout(() => editor.update_ui(), 500); + return editor; + })(); + const container_tab_list = tag.find(".right > .header"); { - let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name"); - name.appendTo(tag); + const label_current = tag.find(".left .container-selected"); + const create_tab = (tab_entry: JQuery, container_name: string, hidden_permissions: PermissionType[]) => { + const target_container = tag.find(".body .container." + container_name); + + tab_entry.on('click', () => { + /* Using a timeout here prevents unnecessary style calculations required by other click event handlers */ + setTimeout(() => { + container_tab_list.find(".selected").removeClass("selected"); + tab_entry.addClass("selected"); + label_current.text(tab_entry.find("a").text()); + + /* dont use show() here because it causes a style recalculation */ + for(const element of tag.find(".body .container")) + (element).style.display = "none"; + + permission_editor.set_hidden_permissions(settings.static_global(Settings.KEY_PERMISSIONS_SHOW_ALL) ? undefined : hidden_permissions); + permission_editor.html_tag()[0].remove(); + target_container.find(".permission-editor").trigger('show'); + target_container.find(".permission-editor").append(permission_editor.html_tag()); + + for(const element of target_container) + (element).style.display = null; + }, 0); + }); + }; + + create_tab(container_tab_list.find(".sg"), "container-view-server-groups", senseless_server_group_permissions); + create_tab(container_tab_list.find(".cg"), "container-view-channel-groups", senseless_channel_group_permissions); + create_tab(container_tab_list.find(".chp"), "container-view-channel-permissions", senseless_channel_permissions); + create_tab(container_tab_list.find(".clp"), "container-view-client-permissions", senseless_client_permissions); + create_tab(container_tab_list.find(".clchp"), "container-view-client-channel-permissions", senseless_client_channel_permissions); } - tag.on('click', event => { - channel_list.find(".selected").removeClass("selected"); - tag.addClass("selected"); - select_callback(channel, _update_icon); - }); + apply_server_groups(connection, permission_editor, tag.find(".left .container-view-server-groups"), tag.find(".right .container-view-server-groups")); + apply_channel_groups(connection, permission_editor, tag.find(".left .container-view-channel-groups"), tag.find(".right .container-view-channel-groups")); + apply_channel_permission(connection, permission_editor, tag.find(".left .container-view-channel-permissions"), tag.find(".right .container-view-channel-permissions")); + apply_client_permission(connection, permission_editor, tag.find(".left .container-view-client-permissions"), tag.find(".right .container-view-client-permissions"), selected_tab == "clp" ? options : {}); + apply_client_channel_permission(connection, permission_editor, tag.find(".left .container-view-client-channel-permissions"), tag.find(".right .container-view-client-channel-permissions"), selected_tab == "clchp" ? options : {}); - return tag; - }; + setTimeout(() => container_tab_list.find("." + (selected_tab || "sg")).trigger('click'), 0); + return tag.dividerfy(); + }, + footer: undefined, - const build_channels = (root: ChannelEntry, level: number) => { - build_channel(root, level).appendTo(channel_list); - const child_head = root.children(false).find(e => e.channel_previous === undefined); - if(child_head) - build_channels(child_head, level + 1); - if(root.channel_next) - build_channels(root.channel_next, level) - }; - build_channels(root, 0); + min_width: "30em", + height: "80%", + trigger_tab: false, + full_size: true + }); - let selected_channel_tag = channel_list.find(".channel[channel-id=" + selected_channel + "]"); - if(!selected_channel_tag || selected_channel_tag.length < 1) - selected_channel_tag = channel_list.find('.channel').first(); - setTimeout(() => selected_channel_tag.trigger('click'), 0); - } + const tag = modal.htmlTag; + tag.find(".modal-body").addClass("modal-permission-editor"); + if(selected_tab) + setTimeout(() => tag.find(".tab-header .entry[x-id=" + selected_tab + "]").first().trigger("click"), 1); + tag.find(".btn_close").on('click', () => { + modal.close(); + }); - function apply_client_channel_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery, options: OptionsClientChannelPermissions) { - let current_cldbid: number = 0; - let current_channel: ChannelEntry; + return modal; +} + +function build_channel_tree(connection: ConnectionHandler, channel_list: JQuery, selected_channel: number, select_callback: (channel: ChannelEntry, icon_update: (id: number) => any) => any) { + const root = connection.channelTree.get_first_channel(); + if(!root) return; + + const build_channel = (channel: ChannelEntry, level: number) => { + let tag = $.spawn("div").addClass("channel").css("padding-left", "calc(0.25em + " + (level * 16) + "px)").attr("channel-id", channel.channelId); + let icon_tag = connection.fileManager.icons.generateTag(channel.properties.channel_icon_id); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); - /* the editor */ { - const pe_client = tab_right.find(".permission-editor"); - tab_right.on('show', event => { - editor.set_toggle_button(undefined, undefined); - pe_client.append(editor.html_tag()); - if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { - if(current_cldbid && current_channel) - editor.set_mode(PermissionEditorMode.VISIBLE); - else - editor.set_mode(PermissionEditorMode.UNSET); - } else { - editor.set_mode(PermissionEditorMode.NO_PERMISSION); - return; - } - - - editor.set_listener_update(() => { - if(!current_cldbid || !current_channel) return; - - connection.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => { - editor.set_permissions(result); - editor.set_mode(PermissionEditorMode.VISIBLE); - }).catch(error => { - console.log(error); //TODO handling? - }); - }); - - /* TODO: Error handling? */ - editor.set_listener(async (permission, value) => { - if (!current_cldbid) - throw "unset client"; - if (!current_channel) - throw "unset channel"; - - if (value.remove) { - /* remove the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Removing client channel permission %s. permission.id: %o"), - permission.name, - permission.id, - ); - - await connection.serverConnection.send_command("channelclientdelperm", { - cldbid: current_cldbid, - cid: current_channel.channelId, - permid: permission.id, - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Removing client channel grant permission %s. permission.id: %o"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channelclientdelperm", { - cldbid: current_cldbid, - cid: current_channel.channelId, - permid: permission.id_grant(), - }); - } - } else { - /* add the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), - permission.name, - permission.id, - value.value, - value.flag_skip, - value.flag_negate - ); - - await connection.serverConnection.send_command("channelclientaddperm", { - cldbid: current_cldbid, - cid: current_channel.channelId, - permid: permission.id, - permvalue: value.value, - permskip: value.flag_skip, - permnegated: value.flag_negate - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel grant permission %s. permission.{id: %o, value: %o}"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channelclientaddperm", { - cldbid: current_cldbid, - cid: current_channel.channelId, - permid: permission.id_grant(), - permvalue: value.granted, - permskip: false, - permnegated: false - }); - } - } - }); - - /* FIXME: Use cached permissions */ - editor.trigger_update(); - }); + let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name"); + name.appendTo(tag); } - build_channel_tree(connection, tab_left.find(".list-channel .entries"), options.channel_id || 0, channel => { - if(current_channel == channel) return; + tag.on('click', event => { + channel_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + select_callback(channel, _update_icon); + }); - current_channel = channel; + return tag; + }; - /* TODO: Test for visibility */ + const build_channels = (root: ChannelEntry, level: number) => { + build_channel(root, level).appendTo(channel_list); + const child_head = root.children(false).find(e => e.channel_previous === undefined); + if(child_head) + build_channels(child_head, level + 1); + if(root.channel_next) + build_channels(root.channel_next, level) + }; + build_channels(root, 0); + + let selected_channel_tag = channel_list.find(".channel[channel-id=" + selected_channel + "]"); + if(!selected_channel_tag || selected_channel_tag.length < 1) + selected_channel_tag = channel_list.find('.channel').first(); + setTimeout(() => selected_channel_tag.trigger('click'), 0); +} + +function apply_client_channel_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery, options: OptionsClientChannelPermissions) { + let current_cldbid: number = 0; + let current_channel: ChannelEntry; + + /* the editor */ + { + const pe_client = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_client.append(editor.html_tag()); + if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { + if(current_cldbid && current_channel) + editor.set_mode(PermissionEditorMode.VISIBLE); + else + editor.set_mode(PermissionEditorMode.UNSET); + } else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + + + editor.set_listener_update(() => { + if(!current_cldbid || !current_channel) return; + + connection.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => { + editor.set_permissions(result); + editor.set_mode(PermissionEditorMode.VISIBLE); + }).catch(error => { + console.log(error); //TODO handling? + }); + }); + + /* TODO: Error handling? */ + editor.set_listener(async (permission, value) => { + if (!current_cldbid) + throw "unset client"; + if (!current_channel) + throw "unset channel"; + + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Removing client channel permission %s. permission.id: %o"), + permission.name, + permission.id, + ); + + await connection.serverConnection.send_command("channelclientdelperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id, + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Removing client channel grant permission %s. permission.id: %o"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channelclientdelperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id_grant(), + }); + } + } else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), + permission.name, + permission.id, + value.value, + value.flag_skip, + value.flag_negate + ); + + await connection.serverConnection.send_command("channelclientaddperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel grant permission %s. permission.{id: %o, value: %o}"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channelclientaddperm", { + cldbid: current_cldbid, + cid: current_channel.channelId, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + }); + + /* FIXME: Use cached permissions */ editor.trigger_update(); }); - - { - - const tag_select = tab_left.find(".client-select"); - const tag_select_uid = tag_select.find("input"); - const tag_select_error = tag_select.find(".invalid-feedback"); - - const tag_client_name = tab_left.find(".client-name"); - const tag_client_uid = tab_left.find(".client-uid"); - const tag_client_dbid = tab_left.find(".client-dbid"); - - - const resolve_client = () => { - let client_uid = tag_select_uid.val() as string; - connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { - if(!result || result.length == 0) return Promise.reject("invalid data"); - tag_select.removeClass('is-invalid'); - - tag_client_name.val(result[0].client_nickname ); - tag_client_uid.val(result[0].client_unique_id); - tag_client_dbid.val(result[0].client_database_id); - - current_cldbid = result[0].client_database_id; - editor.trigger_update(); - }).catch(error => { - console.log(error); - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) - error = "unknown client"; - else - error = error.extra_message || error.message; - } - - tag_client_name.val(""); - tag_client_uid.val(""); - tag_client_dbid.val(""); - - tag_select_error.text(error); - tag_select.addClass('is-invalid'); - editor.set_mode(PermissionEditorMode.UNSET); - }); - }; - - tag_select_uid.on('change', event => resolve_client()); - if(options.unique_id) { - tag_select_uid.val(options.unique_id); - setTimeout(() => resolve_client()); - } - } } - function apply_client_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery, options: OptionsClientPermissions) { - let current_cldbid: number = 0; + build_channel_tree(connection, tab_left.find(".list-channel .entries"), options.channel_id || 0, channel => { + if(current_channel == channel) return; - /* the editor */ - { - const pe_client = tab_right.find("permission-editor.client"); - tab_right.on('show', event => { - editor.set_toggle_button(undefined, undefined); - pe_client.append(editor.html_tag()); - if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { - if(current_cldbid) - editor.set_mode(PermissionEditorMode.VISIBLE); - else - editor.set_mode(PermissionEditorMode.UNSET); - } else { - editor.set_mode(PermissionEditorMode.NO_PERMISSION); - return; - } + current_channel = channel; - editor.set_listener_update(() => { - if(!current_cldbid) return; - - connection.permissions.requestClientPermissions(current_cldbid).then(result => { - editor.set_permissions(result); - editor.set_mode(PermissionEditorMode.VISIBLE); - }).catch(error => { - console.log(error); //TODO handling? - }); - }); - - /* TODO: Error handling? */ - editor.set_listener(async (permission, value) => { - if (!current_cldbid) - throw "unset client"; - - if (value.remove) { - /* remove the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Removing client permission %s. permission.id: %o"), - permission.name, - permission.id, - ); - - await connection.serverConnection.send_command("clientdelperm", { - cldbid: current_cldbid, - permid: permission.id, - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Removing client grant permission %s. permission.id: %o"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("clientdelperm", { - cldbid: current_cldbid, - permid: permission.id_grant(), - }); - } - } else { - /* add the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating client permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), - permission.name, - permission.id, - value.value, - value.flag_skip, - value.flag_negate - ); - - await connection.serverConnection.send_command("clientaddperm", { - cldbid: current_cldbid, - permid: permission.id, - permvalue: value.value, - permskip: value.flag_skip, - permnegated: value.flag_negate - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating client grant permission %s. permission.{id: %o, value: %o}"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("clientaddperm", { - cldbid: current_cldbid, - permid: permission.id_grant(), - permvalue: value.granted, - permskip: false, - permnegated: false - }); - } - } - }); - - /* FIXME: Use cached permissions */ - editor.trigger_update(); - }); - } + /* TODO: Test for visibility */ + editor.trigger_update(); + }); + { const tag_select = tab_left.find(".client-select"); const tag_select_uid = tag_select.find("input"); @@ -501,11 +314,12 @@ namespace Modals { const tag_client_uid = tab_left.find(".client-uid"); const tag_client_dbid = tab_left.find(".client-dbid"); + const resolve_client = () => { let client_uid = tag_select_uid.val() as string; connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { if(!result || result.length == 0) return Promise.reject("invalid data"); - tag_select.removeClass("is-invalid"); + tag_select.removeClass('is-invalid'); tag_client_name.val(result[0].client_nickname ); tag_client_uid.val(result[0].client_unique_id); @@ -527,7 +341,7 @@ namespace Modals { tag_client_dbid.val(""); tag_select_error.text(error); - tag_select.addClass("is-invalid"); + tag_select.addClass('is-invalid'); editor.set_mode(PermissionEditorMode.UNSET); }); }; @@ -538,965 +352,1112 @@ namespace Modals { setTimeout(() => resolve_client()); } } +} - function apply_channel_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { - let current_channel: ChannelEntry | undefined; - let update_channel_icon: (id: number) => any; +function apply_client_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery, options: OptionsClientPermissions) { + let current_cldbid: number = 0; - /* the editor */ - { - const pe_channel = tab_right.find(".permission-editor"); - tab_right.on('show', event => { - editor.set_toggle_button(undefined, undefined); - pe_channel.append(editor.html_tag()); - if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1)) + /* the editor */ + { + const pe_client = tab_right.find("permission-editor.client"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_client.append(editor.html_tag()); + if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { + if(current_cldbid) editor.set_mode(PermissionEditorMode.VISIBLE); - else { - editor.set_mode(PermissionEditorMode.NO_PERMISSION); - return; - } + else + editor.set_mode(PermissionEditorMode.UNSET); + } else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } - editor.set_listener_update(() => { - if(!current_channel) return; + editor.set_listener_update(() => { + if(!current_cldbid) return; - connection.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => { - editor.set_permissions([]); - console.log(error); //TODO handling? - }); + connection.permissions.requestClientPermissions(current_cldbid).then(result => { + editor.set_permissions(result); + editor.set_mode(PermissionEditorMode.VISIBLE); + }).catch(error => { + console.log(error); //TODO handling? }); - - editor.set_listener(async (permission, value) => { - if (!current_channel) - throw "unset channel"; - - if (value.remove) { - /* remove the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Removing channel permission %s. permission.id: %o"), - permission.name, - permission.id, - ); - - await connection.serverConnection.send_command("channeldelperm", { - cid: current_channel.channelId, - permid: permission.id, - }).then(e => { - if(permission.name === "i_icon_id" && update_channel_icon) - update_channel_icon(undefined); - return e; - }); - } else { - /* TODO Remove this because its totally useless. Remove this from the UI as well */ - log.info(LogCategory.PERMISSIONS, tr("Removing channel grant permission %s. permission.id: %o"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channeldelperm", { - cid: current_channel.channelId, - permid: permission.id_grant(), - }); - } - } else { - /* add the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), - permission.name, - permission.id, - value.value, - value.flag_skip, - value.flag_negate - ); - - await connection.serverConnection.send_command("channeladdperm", { - cid: current_channel.channelId, - permid: permission.id, - permvalue: value.value, - permskip: value.flag_skip, - permnegated: value.flag_negate - }).then(e => { - if(permission.name === "i_icon_id" && update_channel_icon) - update_channel_icon(value.value); - return e; - }); - } else { - /* TODO Remove this because its totally useless. Remove this from the UI as well */ - log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel grant permission %s. permission.{id: %o, value: %o}"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channeladdperm", { - cid: current_channel.channelId, - permid: permission.id_grant(), - permvalue: value.granted, - permskip: false, - permnegated: false - }); - } - } - }); - - /* FIXME: Use cached permissions */ - editor.trigger_update(); }); - } - let channel_list = tab_left.find(".list-channel .entries"); - build_channel_tree(connection, channel_list, 0, (channel, update) => { - current_channel = channel; - update_channel_icon = update; + /* TODO: Error handling? */ + editor.set_listener(async (permission, value) => { + if (!current_cldbid) + throw "unset client"; + + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Removing client permission %s. permission.id: %o"), + permission.name, + permission.id, + ); + + await connection.serverConnection.send_command("clientdelperm", { + cldbid: current_cldbid, + permid: permission.id, + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Removing client grant permission %s. permission.id: %o"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("clientdelperm", { + cldbid: current_cldbid, + permid: permission.id_grant(), + }); + } + } else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating client permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), + permission.name, + permission.id, + value.value, + value.flag_skip, + value.flag_negate + ); + + await connection.serverConnection.send_command("clientaddperm", { + cldbid: current_cldbid, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating client grant permission %s. permission.{id: %o, value: %o}"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("clientaddperm", { + cldbid: current_cldbid, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + }); + + /* FIXME: Use cached permissions */ editor.trigger_update(); }); } - function apply_channel_groups(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { - let current_group; - let update_group_icon: (id: number) => any; - let update_groups: (selected_group: number) => any; - let update_buttons: () => any; - /* the editor */ - { - const pe_server = tab_right.find(".permission-editor"); - tab_right.on('show', event => { - editor.set_toggle_button(undefined, undefined); - pe_server.append(editor.html_tag()); - if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1)) - editor.set_mode(PermissionEditorMode.VISIBLE); - else { - editor.set_mode(PermissionEditorMode.NO_PERMISSION); - return; - } - - editor.set_listener_update(() => { - if(!current_group) return; - - connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { - console.log(error); //TODO handling? - }); - }); - - editor.set_listener(async (permission, value) => { - if (!current_group) - throw "unset channel group"; - - if (value.remove) { - /* remove the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Removing channel group permission %s. permission.id: %o"), - permission.name, - permission.id, - ); - - await connection.serverConnection.send_command("channelgroupdelperm", { - cgid: current_group.id, - permid: permission.id, - }).then(e => { - if(permission.name === "i_icon_id" && update_group_icon) - update_group_icon(undefined); - return e; - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Removing channel group grant permission %s. permission.id: %o"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channelgroupdelperm", { - cgid: current_group.id, - permid: permission.id_grant(), - }); - } - } else { - /* add the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), - permission.name, - permission.id, - value.value, - value.flag_skip, - value.flag_negate - ); - - await connection.serverConnection.send_command("channelgroupaddperm", { - cgid: current_group.id, - permid: permission.id, - permvalue: value.value, - permskip: value.flag_skip, - permnegated: value.flag_negate - }).then(e => { - if(permission.name === "i_icon_id" && update_group_icon) - update_group_icon(value.value); - return e; - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group grant permission %s. permission.{id: %o, value: %o}"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("channelgroupaddperm", { - cgid: current_group.id, - permid: permission.id_grant(), - permvalue: value.granted, - permskip: false, - permnegated: false - }); - } - } - }); - - /* FIXME: Use cached permissions */ - editor.trigger_update(); - }); - } - - /* list all channel groups */ - { - let group_list = tab_left.find(".list-groups .entries"); - - update_groups = (selected_group: number) => { - group_list.children().remove(); - - const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); - const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); - for (let group of connection.groups.channelGroups.sort(GroupManager.sorter())) { - if (group.type == GroupType.QUERY) { - if (!allow_query_groups) - continue; - } else if (group.type == GroupType.TEMPLATE) { - if (!allow_template_groups) - continue; - } - - let tag = $.spawn("div").addClass("group").attr("group-id", group.id); - let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); - icon_tag.appendTo(tag); - const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); - - { - let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name"); - if (group.properties.savedb) - name.addClass("savedb"); - if (connection.channelTree.server.properties.virtualserver_default_channel_group == group.id) - name.addClass("default"); - name.appendTo(tag); - } - tag.appendTo(group_list); - - tag.on('click', event => { - current_group = group; - update_group_icon = _update_icon; - group_list.find(".selected").removeClass("selected"); - tag.addClass("selected"); - - update_buttons(); - //TODO trigger only if the editor is in channel group mode! - editor.trigger_update(); - }); - tag.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Create a channel group"), - icon_class: 'client-add', - invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1), - callback: () => tab_left.find(".button-add").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Rename channel group"), - icon_class: 'client-edit', - invalidPermission: !connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), - callback: () => tab_left.find(".button-rename").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Duplicate channel group"), - icon_class: 'client-copy', - callback: () => tab_left.find(".button-duplicate").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Delete channel group"), - icon_class: 'client-delete', - invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_DELETE).granted(1), - callback: () => tab_left.find(".button-delete").trigger('click') - }); - event.preventDefault(); - }); - if(group.id === selected_group) { - setTimeout(() => tag.trigger('click'), 0); - selected_group = undefined; - } - } - - /* because the server menu is the first which will be shown */ - if(typeof(selected_group) !== "undefined") { - setTimeout(() => group_list.find('.group').first().trigger('click'), 0); - } - }; - - tab_left.find(".list-groups").on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Create a channel group"), - icon_class: 'client-add', - callback: () => tab_left.find(".button-add").trigger('click') - }); - event.preventDefault(); - }); - } - - { - const container_buttons = tab_left.find(".container-buttons"); - - const button_add = container_buttons.find(".button-add"); - const button_rename = container_buttons.find(".button-rename"); - const button_duplicate = container_buttons.find(".button-duplicate"); - const button_delete = container_buttons.find(".button-delete"); - - button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); - button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); - update_buttons = () => { - const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); - button_rename.prop("disabled", !permission_modify); - button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); - }; - - button_add.on('click', () => { - spawnGroupAdd(false, connection.permissions, (name, type) => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == type) , (name, type) => { - console.log("Creating channel group: %o, %o", name, type); - connection.serverConnection.send_command('channelgroupadd', { - name: name, - type: type - }).then(() => { - createInfoModal(tr("Group created"), tr("The channel group has been created.")).open(); - update_groups(0); //TODO: May get the created group? - }).catch(error => { - console.warn(tr("Failed to create channel group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to create group"), MessageHelper.formatMessage(tr("Failed to create group:{:br:}"), error)).open(); - }); - }); - }); - - button_rename.on('click', () => { - if(!current_group) - return; - - createInputModal(tr("Rename group"), tr("Enter the new group name"), name => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { - if(typeof(result) !== "string" || !result) - return; - connection.serverConnection.send_command('channelgrouprename', { - cgid: current_group.id, - name: result - }).then(() => { - createInfoModal(tr("Group renamed"), tr("The channel group has been renamed.")).open(); - update_groups(current_group.id); - }).catch(error => { - console.warn(tr("Failed to rename channel group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to rename group"), MessageHelper.formatMessage(tr("Failed to rename group:{:br:}"), error)).open(); - }); - }).open(); - }); - - button_duplicate.on('click', () => { - createErrorModal(tr("Not implemented yet"), tr("This function hasn't been implemented yet!")).open(); - }); - - button_delete.on('click', () => { - if(!current_group) - return; - - spawnYesNo(tr("Are you sure?"), MessageHelper.formatMessage(tr("Do you really want to delete the group {}?"), current_group.name), result => { - if(result !== true) - return; - - connection.serverConnection.send_command("channelgroupdel", { - cgid: current_group.id, - force: true - }).then(() => { - createInfoModal(tr("Group deleted"), tr("The channel group has been deleted.")).open(); - update_groups(0); - }).catch(error => { - console.warn(tr("Failed to delete channel group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to delete group"), MessageHelper.formatMessage(tr("Failed to delete group:{:br:}"), error)).open(); - }); - }); - }); - } - update_groups(0); - } - - function apply_server_groups(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { - let current_group: Group; - let current_group_changed: (() => any)[] = []; - - let update_buttons: () => any; - /* list all groups */ - - let update_icon: ((icon_id: number) => any)[] = []; - - let update_groups: (selected_group: number) => any; - { - let group_list = tab_left.find(".container-group-list .list-groups .entries"); - let group_list_update_icon: (i: number) => any; - update_icon.push(i => group_list_update_icon(i)); - - update_groups = (selected_group: number) => { - group_list.children().remove(); - - const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); - const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); - for(const group of connection.groups.serverGroups.sort(GroupManager.sorter())) { - if(group.type == GroupType.QUERY) { - if(!allow_query_groups) - continue; - } else if(group.type == GroupType.TEMPLATE) { - if(!allow_template_groups) - continue; - } - let tag = $.spawn("div").addClass("group").attr("group-id", group.id); - let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); - icon_tag.appendTo(tag); - const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); - - { - let name = $.spawn("div").text(group.name + " (" + group.id + ")").addClass("name"); - if(group.properties.savedb) - name.addClass("savedb"); - if(connection.channelTree.server.properties.virtualserver_default_server_group == group.id) - name.addClass("default"); - name.appendTo(tag); - } - tag.appendTo(group_list); - - tag.on('click', event => { - if(current_group === group) - return; - - current_group = group; - group_list_update_icon = _update_icon; - if(update_buttons) - update_buttons(); - for(const entry of current_group_changed) - entry(); - - group_list.find(".selected").removeClass("selected"); - tag.addClass("selected"); - editor.trigger_update(); - }); - tag.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Create a server group"), - icon_class: 'client-add', - invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1), - callback: () => tab_left.find(".button-add").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Rename server group"), - icon_class: 'client-edit', - invalidPermission: !connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), - callback: () => tab_left.find(".button-rename").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Duplicate server group"), - icon_class: 'client-copy', - callback: () => tab_left.find(".button-duplicate").trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Delete server group"), - icon_class: 'client-delete', - invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1), - callback: () => tab_left.find(".button-delete").trigger('click') - }); - event.preventDefault(); - }); - - if(group.id === selected_group) { - setTimeout(() => tag.trigger('click'), 0); - selected_group = undefined; - } - } - - /* because the server menu is the first which will be shown */ - if(typeof(selected_group) !== "undefined") { - setTimeout(() => group_list.find('.group').first().trigger('click'), 0); - } - }; - - - tab_left.find(".list-groups").on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Create a server group"), - icon_class: 'client-add', - callback: () => tab_left.find(".button-add").trigger('click') - }); - event.preventDefault(); - }); - } - { - const container_buttons = tab_left.find(".container-group-list .container-buttons"); - - const button_add = container_buttons.find(".button-add"); - const button_rename = container_buttons.find(".button-rename"); - const button_duplicate = container_buttons.find(".button-duplicate"); - const button_delete = container_buttons.find(".button-delete"); - - button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); - button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1)); - update_buttons = () => { - const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); - button_rename.prop("disabled", !permission_modify); - button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); - }; - - button_add.on('click', () => { - spawnGroupAdd(true, connection.permissions, (name, type) => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == type) , (name, type) => { - console.log("Creating group: %o, %o", name, type); - connection.serverConnection.send_command('servergroupadd', { - name: name, - type: type - }).then(() => { - createInfoModal(tr("Group created"), tr("The server group has been created.")).open(); - update_groups(0); //TODO: May get the created group? - }).catch(error => { - console.warn(tr("Failed to create server group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to create group"), MessageHelper.formatMessage(tr("Failed to create group:{:br:}"), error)).open(); - }); - }); - }); - - button_rename.on('click', () => { - if(!current_group) - return; - - createInputModal(tr("Rename group"), tr("Enter the new group name"), name => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { - if(typeof(result) !== "string" || !result) - return; - connection.serverConnection.send_command('servergrouprename', { - sgid: current_group.id, - name: result - }).then(() => { - createInfoModal(tr("Group renamed"), tr("The server group has been renamed.")).open(); - update_groups(current_group.id); - }).catch(error => { - console.warn(tr("Failed to rename server group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to rename group"), MessageHelper.formatMessage(tr("Failed to rename group:{:br:}"), error)).open(); - }); - }).open(); - }); - - button_duplicate.on('click', () => { - createErrorModal(tr("Not implemented yet"), tr("This function hasn't been implemented yet!")).open(); - }); - - button_delete.on('click', () => { - if(!current_group) - return; - - spawnYesNo(tr("Are you sure?"), MessageHelper.formatMessage(tr("Do you really want to delete the group {}?"), current_group.name), result => { - if(result !== true) - return; - - connection.serverConnection.send_command("servergroupdel", { - sgid: current_group.id, - force: true - }).then(() => { - createInfoModal(tr("Group deleted"), tr("The server group has been deleted.")).open(); - update_groups(0); - }).catch(error => { - console.warn(tr("Failed to delete server group: %o"), error); - if(error instanceof CommandResult) { - error = error.extra_message || error.message; - } - createErrorModal(tr("Failed to delete group"), MessageHelper.formatMessage(tr("Failed to delete group:{:br:}"), error)).open(); - }); - }); - }); - } - update_groups(0); - - /* the editor */ - { - const pe_server = tab_right.find(".permission-editor"); - tab_right.on('show', event => { - pe_server.append(editor.html_tag()); - if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1)) - editor.set_mode(PermissionEditorMode.VISIBLE); - else { - editor.set_mode(PermissionEditorMode.NO_PERMISSION); - return; - } - editor.set_listener_update(() => { - console.log("Updating permissions"); - connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { - console.log(error); //TODO handling? - }); - }); - - editor.set_listener(async (permission, value) => { - if (!current_group) - throw "unset server group"; - - if (value.remove) { - /* remove the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Removing server group permission %s. permission.id: %o"), - permission.name, - permission.id, - ); - - await connection.serverConnection.send_command("servergroupdelperm", { - sgid: current_group.id, - permid: permission.id, - }).then(e => { - if(permission.name === "i_icon_id") - for(const c of update_icon) - c(0); - return e; - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Removing server group grant permission %s. permission.id: %o"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("servergroupdelperm", { - sgid: current_group.id, - permid: permission.id_grant(), - }); - } - } else { - /* add the permission */ - if (typeof (value.value) !== "undefined") { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), - permission.name, - permission.id, - value.value, - value.flag_skip, - value.flag_negate - ); - - await connection.serverConnection.send_command("servergroupaddperm", { - sgid: current_group.id, - permid: permission.id, - permvalue: value.value, - permskip: value.flag_skip, - permnegated: value.flag_negate - }).then(e => { - if(permission.name === "i_icon_id") - for(const c of update_icon) - c(value.value); - return e; - }); - } else { - log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group grant permission %s. permission.{id: %o, value: %o}"), - permission.name, - permission.id_grant(), - value.granted, - ); - - await connection.serverConnection.send_command("servergroupaddperm", { - sgid: current_group.id, - permid: permission.id_grant(), - permvalue: value.granted, - permskip: false, - permnegated: false - }); - } - } - }); - - editor.trigger_update(); - }); - } - - /* client list */ - { - //container-client-list container-group-list - let clients_visible = false; - let selected_client: { - tag: JQuery, - dbid: number - }; - - const container_client_list = tab_left.find(".container-client-list").addClass("hidden"); - const container_group_list = tab_left.find(".container-group-list"); - - const container_selected_group = container_client_list.find(".container-current-group"); - const container_clients = container_client_list.find(".list-clients .entries"); - - const input_filter = container_client_list.find(".filter-client-list"); - - const button_add = container_client_list.find(".button-add"); - const button_delete = container_client_list.find(".button-delete"); - - const update_filter = () => { - const filter_text = (input_filter.val() || "").toString().toLowerCase(); - if(!filter_text) { - container_clients.find(".entry").css('display', 'block'); - } else { - const entries = container_clients.find(".entry"); - for(const _entry of entries) { - const entry = $(_entry); - if(entry.attr("search-string").toLowerCase().indexOf(filter_text) !== -1) - entry.css('display', 'block'); - else - entry.css('display', 'none'); - } - } - }; - - const update_client_list = () => { - container_clients.empty(); - button_delete.prop('disabled', true); - - connection.serverConnection.command_helper.request_clients_by_server_group(current_group.id).then(clients => { - for(const client of clients) { - const tag = $.spawn("div").addClass("client").text(client.client_nickname); - tag.attr("search-string", client.client_nickname + "-" + client.client_unique_identifier + "-" + client.client_database_id); - container_clients.append(tag); - - tag.on('click contextmenu', event => { - container_clients.find(".selected").removeClass("selected"); - tag.addClass("selected"); - - selected_client = { - tag: tag, - dbid: client.client_database_id - }; - button_delete.prop('disabled', false); - }); - - tag.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - event.preventDefault(); - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add client"), - icon_class: 'client-add', - callback: () => button_add.trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Remove client"), - icon_class: 'client-delete', - callback: () => button_delete.trigger('click') - }, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Copy unique id"), - icon_class: 'client-copy', - callback: () => copy_to_clipboard(client.client_unique_identifier) - }) - }); - } - update_filter(); - }).catch(error => { - if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) - return; - console.warn(tr("Failed to receive server group clients for group %d: %o"), current_group.id, error); - }); - }; - current_group_changed.push(update_client_list); - - button_delete.on('click', event => { - const client = selected_client; - if(!client) return; - - connection.serverConnection.send_command("servergroupdelclient", { - sgid: current_group.id, - cldbid: client.dbid - }).then(() => { - selected_client.tag.detach(); - button_delete.prop('disabled', true); /* nothing is selected */ - }).catch(error => { - console.log(tr("Failed to delete client %o from server group %o: %o"), client.dbid, current_group.id, error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to remove client"), tr("Failed to remove client from server group")).open(); - }); - }); - - button_add.on('click', event => { - createInputModal(tr("Add client to server group"), tr("Enter the client unique id or database id"), text => { - if(!text) return false; - if(!!text.match(/^[0-9]+$/)) - return true; - try { - return atob(text).length >= 20; - } catch(error) { - return false; - } - }, async text => { - if(typeof(text) !== "string") - return; - - let dbid; - if(!!text.match(/^[0-9]+$/)) { - dbid = parseInt(text); - debugger; - } else { - try { - const data = await connection.serverConnection.command_helper.info_from_uid(text.trim()); - dbid = data[0].client_database_id; - } catch(error) { - console.log(tr("Failed to resolve client database id from unique id (%s): %o"), text, error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to add client"), MessageHelper.formatMessage(tr("Failed to add client to server group\nFailed to resolve database id: {}."), error)).open(); - return; - } - } - if(!dbid) { - console.log(tr("Failed to resolve client database id from unique id (%s): Client not found")); - createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\nClient database id not found")).open(); - return; - } - - - connection.serverConnection.send_command("servergroupaddclient", { - sgid: current_group.id, - cldbid: dbid - }).then(() => { - update_client_list(); - }).catch(error => { - console.log(tr("Failed to add client %o to server group %o: %o"), dbid, current_group.id, error); - if(error instanceof CommandResult) - error = error.extra_message || error.message; - createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\n" + error)).open(); - }); - }).open(); - }); - - container_client_list.on('contextmenu', event => { - if(event.isDefaultPrevented()) - return; - - event.preventDefault(); - contextmenu.spawn_context_menu(event.pageX, event.pageY, { - type: contextmenu.MenuEntryType.ENTRY, - name: tr("Add client"), - icon_class: 'client-add', - callback: () => button_add.trigger('click') - }) - }); - - /* icon handler and current group display */ - { - let update_icon_callback: (i: number) => any; - update_icon.push(i => update_icon_callback(i)); - - input_filter.on('change keyup', event => update_filter()); - current_group_changed.push(() => { - container_selected_group.empty(); - if(!current_group) return; - - let icon_container = $.spawn("div").addClass("icon-container").appendTo(container_selected_group); - - connection.fileManager.icons.generateTag(current_group.properties.iconid).appendTo(icon_container); - update_icon_callback = icon => { - icon_container.empty(); - connection.fileManager.icons.generateTag(icon).appendTo(icon_container); - }; - $.spawn("div").addClass("name").text(current_group.name + " (" + current_group.id + ")").appendTo(container_selected_group); - }); + const tag_select = tab_left.find(".client-select"); + const tag_select_uid = tag_select.find("input"); + const tag_select_error = tag_select.find(".invalid-feedback"); + + const tag_client_name = tab_left.find(".client-name"); + const tag_client_uid = tab_left.find(".client-uid"); + const tag_client_dbid = tab_left.find(".client-dbid"); + + const resolve_client = () => { + let client_uid = tag_select_uid.val() as string; + connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { + if(!result || result.length == 0) return Promise.reject("invalid data"); + tag_select.removeClass("is-invalid"); + + tag_client_name.val(result[0].client_nickname ); + tag_client_uid.val(result[0].client_unique_id); + tag_client_dbid.val(result[0].client_database_id); + + current_cldbid = result[0].client_database_id; + editor.trigger_update(); + }).catch(error => { + console.log(error); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) + error = "unknown client"; + else + error = error.extra_message || error.message; } - tab_right.on('show', event => { - editor.set_toggle_button(() => { - clients_visible = !clients_visible; + tag_client_name.val(""); + tag_client_uid.val(""); + tag_client_dbid.val(""); - container_client_list.toggleClass("hidden", !clients_visible); - container_group_list.toggleClass("hidden", clients_visible); + tag_select_error.text(error); + tag_select.addClass("is-invalid"); + editor.set_mode(PermissionEditorMode.UNSET); + }); + }; - return clients_visible ? tr("Hide clients in group") : tr("Show clients in group"); - }, clients_visible ? tr("Hide clients in group") : tr("Show clients in group")); + tag_select_uid.on('change', event => resolve_client()); + if(options.unique_id) { + tag_select_uid.val(options.unique_id); + setTimeout(() => resolve_client()); + } +} + +function apply_channel_permission(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { + let current_channel: ChannelEntry | undefined; + let update_channel_icon: (id: number) => any; + + /* the editor */ + { + const pe_channel = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_channel.append(editor.html_tag()); + if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + + editor.set_listener_update(() => { + if(!current_channel) return; + + connection.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => { + editor.set_permissions([]); + console.log(error); //TODO handling? + }); + }); + + editor.set_listener(async (permission, value) => { + if (!current_channel) + throw "unset channel"; + + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Removing channel permission %s. permission.id: %o"), + permission.name, + permission.id, + ); + + await connection.serverConnection.send_command("channeldelperm", { + cid: current_channel.channelId, + permid: permission.id, + }).then(e => { + if(permission.name === "i_icon_id" && update_channel_icon) + update_channel_icon(undefined); + return e; + }); + } else { + /* TODO Remove this because its totally useless. Remove this from the UI as well */ + log.info(LogCategory.PERMISSIONS, tr("Removing channel grant permission %s. permission.id: %o"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channeldelperm", { + cid: current_channel.channelId, + permid: permission.id_grant(), + }); + } + } else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), + permission.name, + permission.id, + value.value, + value.flag_skip, + value.flag_negate + ); + + await connection.serverConnection.send_command("channeladdperm", { + cid: current_channel.channelId, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if(permission.name === "i_icon_id" && update_channel_icon) + update_channel_icon(value.value); + return e; + }); + } else { + /* TODO Remove this because its totally useless. Remove this from the UI as well */ + log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel grant permission %s. permission.{id: %o, value: %o}"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channeladdperm", { + cid: current_channel.channelId, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + }); + + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + + let channel_list = tab_left.find(".list-channel .entries"); + build_channel_tree(connection, channel_list, 0, (channel, update) => { + current_channel = channel; + update_channel_icon = update; + editor.trigger_update(); + }); +} + +function apply_channel_groups(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { + let current_group; + let update_group_icon: (id: number) => any; + let update_groups: (selected_group: number) => any; + let update_buttons: () => any; + + /* the editor */ + { + const pe_server = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + editor.set_toggle_button(undefined, undefined); + pe_server.append(editor.html_tag()); + if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + + editor.set_listener_update(() => { + if(!current_group) return; + + connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { + console.log(error); //TODO handling? + }); + }); + + editor.set_listener(async (permission, value) => { + if (!current_group) + throw "unset channel group"; + + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Removing channel group permission %s. permission.id: %o"), + permission.name, + permission.id, + ); + + await connection.serverConnection.send_command("channelgroupdelperm", { + cgid: current_group.id, + permid: permission.id, + }).then(e => { + if(permission.name === "i_icon_id" && update_group_icon) + update_group_icon(undefined); + return e; + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Removing channel group grant permission %s. permission.id: %o"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channelgroupdelperm", { + cgid: current_group.id, + permid: permission.id_grant(), + }); + } + } else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), + permission.name, + permission.id, + value.value, + value.flag_skip, + value.flag_negate + ); + + await connection.serverConnection.send_command("channelgroupaddperm", { + cgid: current_group.id, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if(permission.name === "i_icon_id" && update_group_icon) + update_group_icon(value.value); + return e; + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group grant permission %s. permission.{id: %o, value: %o}"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("channelgroupaddperm", { + cgid: current_group.id, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + }); + + /* FIXME: Use cached permissions */ + editor.trigger_update(); + }); + } + + /* list all channel groups */ + { + let group_list = tab_left.find(".list-groups .entries"); + + update_groups = (selected_group: number) => { + group_list.children().remove(); + + const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); + const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); + for (let group of connection.groups.channelGroups.sort(GroupManager.sorter())) { + if (group.type == GroupType.QUERY) { + if (!allow_query_groups) + continue; + } else if (group.type == GroupType.TEMPLATE) { + if (!allow_template_groups) + continue; + } + + let tag = $.spawn("div").addClass("group").attr("group-id", group.id); + let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + + { + let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name"); + if (group.properties.savedb) + name.addClass("savedb"); + if (connection.channelTree.server.properties.virtualserver_default_channel_group == group.id) + name.addClass("default"); + name.appendTo(tag); + } + tag.appendTo(group_list); + + tag.on('click', event => { + current_group = group; + update_group_icon = _update_icon; + group_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + + update_buttons(); + //TODO trigger only if the editor is in channel group mode! + editor.trigger_update(); + }); + tag.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Create a channel group"), + icon_class: 'client-add', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1), + callback: () => tab_left.find(".button-add").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Rename channel group"), + icon_class: 'client-edit', + invalidPermission: !connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), + callback: () => tab_left.find(".button-rename").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Duplicate channel group"), + icon_class: 'client-copy', + callback: () => tab_left.find(".button-duplicate").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Delete channel group"), + icon_class: 'client-delete', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_DELETE).granted(1), + callback: () => tab_left.find(".button-delete").trigger('click') + }); + event.preventDefault(); + }); + if(group.id === selected_group) { + setTimeout(() => tag.trigger('click'), 0); + selected_group = undefined; + } + } + + /* because the server menu is the first which will be shown */ + if(typeof(selected_group) !== "undefined") { + setTimeout(() => group_list.find('.group').first().trigger('click'), 0); + } + }; + + tab_left.find(".list-groups").on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Create a channel group"), + icon_class: 'client-add', + callback: () => tab_left.find(".button-add").trigger('click') + }); + event.preventDefault(); + }); + } + + { + const container_buttons = tab_left.find(".container-buttons"); + + const button_add = container_buttons.find(".button-add"); + const button_rename = container_buttons.find(".button-rename"); + const button_duplicate = container_buttons.find(".button-duplicate"); + const button_delete = container_buttons.find(".button-delete"); + + button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + update_buttons = () => { + const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); + button_rename.prop("disabled", !permission_modify); + button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1)); + }; + + button_add.on('click', () => { + spawnGroupAdd(false, connection.permissions, (name, type) => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == type) , (name, type) => { + console.log("Creating channel group: %o, %o", name, type); + connection.serverConnection.send_command('channelgroupadd', { + name: name, + type: type + }).then(() => { + createInfoModal(tr("Group created"), tr("The channel group has been created.")).open(); + update_groups(0); //TODO: May get the created group? + }).catch(error => { + console.warn(tr("Failed to create channel group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to create group"), formatMessage(tr("Failed to create group:{:br:}"), error)).open(); + }); + }); + }); + + button_rename.on('click', () => { + if(!current_group) + return; + + createInputModal(tr("Rename group"), tr("Enter the new group name"), name => name.length > 0 && !connection.groups.channelGroups.find(e => e.target == GroupTarget.CHANNEL && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { + if(typeof(result) !== "string" || !result) + return; + connection.serverConnection.send_command('channelgrouprename', { + cgid: current_group.id, + name: result + }).then(() => { + createInfoModal(tr("Group renamed"), tr("The channel group has been renamed.")).open(); + update_groups(current_group.id); + }).catch(error => { + console.warn(tr("Failed to rename channel group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to rename group"), formatMessage(tr("Failed to rename group:{:br:}"), error)).open(); + }); + }).open(); + }); + + button_duplicate.on('click', () => { + createErrorModal(tr("Not implemented yet"), tr("This function hasn't been implemented yet!")).open(); + }); + + button_delete.on('click', () => { + if(!current_group) + return; + + spawnYesNo(tr("Are you sure?"), formatMessage(tr("Do you really want to delete the group {}?"), current_group.name), result => { + if(result !== true) + return; + + connection.serverConnection.send_command("channelgroupdel", { + cgid: current_group.id, + force: true + }).then(() => { + createInfoModal(tr("Group deleted"), tr("The channel group has been deleted.")).open(); + update_groups(0); + }).catch(error => { + console.warn(tr("Failed to delete channel group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to delete group"), formatMessage(tr("Failed to delete group:{:br:}"), error)).open(); + }); + }); + }); + } + update_groups(0); +} + +function apply_server_groups(connection: ConnectionHandler, editor: AbstractPermissionEditor, tab_left: JQuery, tab_right: JQuery) { + let current_group: Group; + let current_group_changed: (() => any)[] = []; + + let update_buttons: () => any; + /* list all groups */ + + let update_icon: ((icon_id: number) => any)[] = []; + + let update_groups: (selected_group: number) => any; + { + let group_list = tab_left.find(".container-group-list .list-groups .entries"); + let group_list_update_icon: (i: number) => any; + update_icon.push(i => group_list_update_icon(i)); + + update_groups = (selected_group: number) => { + group_list.children().remove(); + + const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); + const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); + for(const group of connection.groups.serverGroups.sort(GroupManager.sorter())) { + if(group.type == GroupType.QUERY) { + if(!allow_query_groups) + continue; + } else if(group.type == GroupType.TEMPLATE) { + if(!allow_template_groups) + continue; + } + let tag = $.spawn("div").addClass("group").attr("group-id", group.id); + let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); + icon_tag.appendTo(tag); + const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); + + { + let name = $.spawn("div").text(group.name + " (" + group.id + ")").addClass("name"); + if(group.properties.savedb) + name.addClass("savedb"); + if(connection.channelTree.server.properties.virtualserver_default_server_group == group.id) + name.addClass("default"); + name.appendTo(tag); + } + tag.appendTo(group_list); + + tag.on('click', event => { + if(current_group === group) + return; + + current_group = group; + group_list_update_icon = _update_icon; + if(update_buttons) + update_buttons(); + for(const entry of current_group_changed) + entry(); + + group_list.find(".selected").removeClass("selected"); + tag.addClass("selected"); + editor.trigger_update(); + }); + tag.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Create a server group"), + icon_class: 'client-add', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1), + callback: () => tab_left.find(".button-add").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Rename server group"), + icon_class: 'client-edit', + invalidPermission: !connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower), + callback: () => tab_left.find(".button-rename").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Duplicate server group"), + icon_class: 'client-copy', + callback: () => tab_left.find(".button-duplicate").trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Delete server group"), + icon_class: 'client-delete', + invalidPermission: !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1), + callback: () => tab_left.find(".button-delete").trigger('click') + }); + event.preventDefault(); + }); + + if(group.id === selected_group) { + setTimeout(() => tag.trigger('click'), 0); + selected_group = undefined; + } + } + + /* because the server menu is the first which will be shown */ + if(typeof(selected_group) !== "undefined") { + setTimeout(() => group_list.find('.group').first().trigger('click'), 0); + } + }; + + + tab_left.find(".list-groups").on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Create a server group"), + icon_class: 'client-add', + callback: () => tab_left.find(".button-add").trigger('click') + }); + event.preventDefault(); + }); + } + { + const container_buttons = tab_left.find(".container-group-list .container-buttons"); + + const button_add = container_buttons.find(".button-add"); + const button_rename = container_buttons.find(".button-rename"); + const button_duplicate = container_buttons.find(".button-duplicate"); + const button_delete = container_buttons.find(".button-delete"); + + button_add.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); + button_delete.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_DELETE).granted(1)); + update_buttons = () => { + const permission_modify = current_group && connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).granted(current_group.requiredModifyPower); + button_rename.prop("disabled", !permission_modify); + button_duplicate.prop("disabled", !connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1)); + }; + + button_add.on('click', () => { + spawnGroupAdd(true, connection.permissions, (name, type) => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == type) , (name, type) => { + console.log("Creating group: %o, %o", name, type); + connection.serverConnection.send_command('servergroupadd', { + name: name, + type: type + }).then(() => { + createInfoModal(tr("Group created"), tr("The server group has been created.")).open(); + update_groups(0); //TODO: May get the created group? + }).catch(error => { + console.warn(tr("Failed to create server group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to create group"), formatMessage(tr("Failed to create group:{:br:}"), error)).open(); + }); + }); + }); + + button_rename.on('click', () => { + if(!current_group) + return; + + createInputModal(tr("Rename group"), tr("Enter the new group name"), name => name.length > 0 && !connection.groups.serverGroups.find(e => e.target == GroupTarget.SERVER && e.name.toLowerCase() === name.toLowerCase() && e.type == current_group.type), result => { + if(typeof(result) !== "string" || !result) + return; + connection.serverConnection.send_command('servergrouprename', { + sgid: current_group.id, + name: result + }).then(() => { + createInfoModal(tr("Group renamed"), tr("The server group has been renamed.")).open(); + update_groups(current_group.id); + }).catch(error => { + console.warn(tr("Failed to rename server group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to rename group"), formatMessage(tr("Failed to rename group:{:br:}"), error)).open(); + }); + }).open(); + }); + + button_duplicate.on('click', () => { + createErrorModal(tr("Not implemented yet"), tr("This function hasn't been implemented yet!")).open(); + }); + + button_delete.on('click', () => { + if(!current_group) + return; + + spawnYesNo(tr("Are you sure?"), formatMessage(tr("Do you really want to delete the group {}?"), current_group.name), result => { + if(result !== true) + return; + + connection.serverConnection.send_command("servergroupdel", { + sgid: current_group.id, + force: true + }).then(() => { + createInfoModal(tr("Group deleted"), tr("The server group has been deleted.")).open(); + update_groups(0); + }).catch(error => { + console.warn(tr("Failed to delete server group: %o"), error); + if(error instanceof CommandResult) { + error = error.extra_message || error.message; + } + createErrorModal(tr("Failed to delete group"), formatMessage(tr("Failed to delete group:{:br:}"), error)).open(); + }); + }); + }); + } + update_groups(0); + + /* the editor */ + { + const pe_server = tab_right.find(".permission-editor"); + tab_right.on('show', event => { + pe_server.append(editor.html_tag()); + if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1)) + editor.set_mode(PermissionEditorMode.VISIBLE); + else { + editor.set_mode(PermissionEditorMode.NO_PERMISSION); + return; + } + editor.set_listener_update(() => { + console.log("Updating permissions"); + connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { + console.log(error); //TODO handling? + }); + }); + + editor.set_listener(async (permission, value) => { + if (!current_group) + throw "unset server group"; + + if (value.remove) { + /* remove the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Removing server group permission %s. permission.id: %o"), + permission.name, + permission.id, + ); + + await connection.serverConnection.send_command("servergroupdelperm", { + sgid: current_group.id, + permid: permission.id, + }).then(e => { + if(permission.name === "i_icon_id") + for(const c of update_icon) + c(0); + return e; + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Removing server group grant permission %s. permission.id: %o"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("servergroupdelperm", { + sgid: current_group.id, + permid: permission.id_grant(), + }); + } + } else { + /* add the permission */ + if (typeof (value.value) !== "undefined") { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), + permission.name, + permission.id, + value.value, + value.flag_skip, + value.flag_negate + ); + + await connection.serverConnection.send_command("servergroupaddperm", { + sgid: current_group.id, + permid: permission.id, + permvalue: value.value, + permskip: value.flag_skip, + permnegated: value.flag_negate + }).then(e => { + if(permission.name === "i_icon_id") + for(const c of update_icon) + c(value.value); + return e; + }); + } else { + log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group grant permission %s. permission.{id: %o, value: %o}"), + permission.name, + permission.id_grant(), + value.granted, + ); + + await connection.serverConnection.send_command("servergroupaddperm", { + sgid: current_group.id, + permid: permission.id_grant(), + permvalue: value.granted, + permskip: false, + permnegated: false + }); + } + } + }); + + editor.trigger_update(); + }); + } + + /* client list */ + { + //container-client-list container-group-list + let clients_visible = false; + let selected_client: { + tag: JQuery, + dbid: number + }; + + const container_client_list = tab_left.find(".container-client-list").addClass("hidden"); + const container_group_list = tab_left.find(".container-group-list"); + + const container_selected_group = container_client_list.find(".container-current-group"); + const container_clients = container_client_list.find(".list-clients .entries"); + + const input_filter = container_client_list.find(".filter-client-list"); + + const button_add = container_client_list.find(".button-add"); + const button_delete = container_client_list.find(".button-delete"); + + const update_filter = () => { + const filter_text = (input_filter.val() || "").toString().toLowerCase(); + if(!filter_text) { + container_clients.find(".entry").css('display', 'block'); + } else { + const entries = container_clients.find(".entry"); + for(const _entry of entries) { + const entry = $(_entry); + if(entry.attr("search-string").toLowerCase().indexOf(filter_text) !== -1) + entry.css('display', 'block'); + else + entry.css('display', 'none'); + } + } + }; + + const update_client_list = () => { + container_clients.empty(); + button_delete.prop('disabled', true); + + connection.serverConnection.command_helper.request_clients_by_server_group(current_group.id).then(clients => { + for(const client of clients) { + const tag = $.spawn("div").addClass("client").text(client.client_nickname); + tag.attr("search-string", client.client_nickname + "-" + client.client_unique_identifier + "-" + client.client_database_id); + container_clients.append(tag); + + tag.on('click contextmenu', event => { + container_clients.find(".selected").removeClass("selected"); + tag.addClass("selected"); + + selected_client = { + tag: tag, + dbid: client.client_database_id + }; + button_delete.prop('disabled', false); + }); + + tag.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add client"), + icon_class: 'client-add', + callback: () => button_add.trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Remove client"), + icon_class: 'client-delete', + callback: () => button_delete.trigger('click') + }, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Copy unique id"), + icon_class: 'client-copy', + callback: () => copy_to_clipboard(client.client_unique_identifier) + }) + }); + } + update_filter(); + }).catch(error => { + if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) + return; + console.warn(tr("Failed to receive server group clients for group %d: %o"), current_group.id, error); + }); + }; + current_group_changed.push(update_client_list); + + button_delete.on('click', event => { + const client = selected_client; + if(!client) return; + + connection.serverConnection.send_command("servergroupdelclient", { + sgid: current_group.id, + cldbid: client.dbid + }).then(() => { + selected_client.tag.detach(); + button_delete.prop('disabled', true); /* nothing is selected */ + }).catch(error => { + console.log(tr("Failed to delete client %o from server group %o: %o"), client.dbid, current_group.id, error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to remove client"), tr("Failed to remove client from server group")).open(); + }); + }); + + button_add.on('click', event => { + createInputModal(tr("Add client to server group"), tr("Enter the client unique id or database id"), text => { + if(!text) return false; + if(!!text.match(/^[0-9]+$/)) + return true; + try { + return atob(text).length >= 20; + } catch(error) { + return false; + } + }, async text => { + if(typeof(text) !== "string") + return; + + let dbid; + if(!!text.match(/^[0-9]+$/)) { + dbid = parseInt(text); + debugger; + } else { + try { + const data = await connection.serverConnection.command_helper.info_from_uid(text.trim()); + dbid = data[0].client_database_id; + } catch(error) { + console.log(tr("Failed to resolve client database id from unique id (%s): %o"), text, error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to add client"), formatMessage(tr("Failed to add client to server group\nFailed to resolve database id: {}."), error)).open(); + return; + } + } + if(!dbid) { + console.log(tr("Failed to resolve client database id from unique id (%s): Client not found")); + createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\nClient database id not found")).open(); + return; + } + + + connection.serverConnection.send_command("servergroupaddclient", { + sgid: current_group.id, + cldbid: dbid + }).then(() => { + update_client_list(); + }).catch(error => { + console.log(tr("Failed to add client %o to server group %o: %o"), dbid, current_group.id, error); + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\n" + error)).open(); + }); + }).open(); + }); + + container_client_list.on('contextmenu', event => { + if(event.isDefaultPrevented()) + return; + + event.preventDefault(); + contextmenu.spawn_context_menu(event.pageX, event.pageY, { + type: contextmenu.MenuEntryType.ENTRY, + name: tr("Add client"), + icon_class: 'client-add', + callback: () => button_add.trigger('click') + }) + }); + + /* icon handler and current group display */ + { + let update_icon_callback: (i: number) => any; + update_icon.push(i => update_icon_callback(i)); + + input_filter.on('change keyup', event => update_filter()); + current_group_changed.push(() => { + container_selected_group.empty(); + if(!current_group) return; + + let icon_container = $.spawn("div").addClass("icon-container").appendTo(container_selected_group); + + connection.fileManager.icons.generateTag(current_group.properties.iconid).appendTo(icon_container); + update_icon_callback = icon => { + icon_container.empty(); + connection.fileManager.icons.generateTag(icon).appendTo(icon_container); + }; + $.spawn("div").addClass("name").text(current_group.name + " (" + current_group.id + ")").appendTo(container_selected_group); }); } + + tab_right.on('show', event => { + editor.set_toggle_button(() => { + clients_visible = !clients_visible; + + container_client_list.toggleClass("hidden", !clients_visible); + container_group_list.toggleClass("hidden", clients_visible); + + return clients_visible ? tr("Hide clients in group") : tr("Show clients in group"); + }, clients_visible ? tr("Hide clients in group") : tr("Show clients in group")); + }); } +} - function spawnGroupAdd(server_group: boolean, permissions: PermissionManager, valid_name: (name: string, group_type: number) => boolean, callback: (group_name: string, group_type: number) => any) { - let modal: Modal; - modal = createModal({ - header: tr("Create a new group"), - body: () => { - let tag = $("#tmpl_group_add").renderTag({ - server_group: server_group - }); +function spawnGroupAdd(server_group: boolean, permissions: PermissionManager, valid_name: (name: string, group_type: number) => boolean, callback: (group_name: string, group_type: number) => any) { + let modal: Modal; + modal = createModal({ + header: tr("Create a new group"), + body: () => { + let tag = $("#tmpl_group_add").renderTag({ + server_group: server_group + }); - tag.find(".group-type-template").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1)); - tag.find(".group-type-query").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1)); + tag.find(".group-type-template").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1)); + tag.find(".group-type-query").prop("disabled", !permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1)); - const container_name = tag.find(".group-name"); - const button_create = tag.find(".button-create"); + const container_name = tag.find(".group-name"); + const button_create = tag.find(".button-create"); - const group_type = () => (tag.find(".group-type")[0] as HTMLSelectElement).selectedIndex; - container_name.on('keyup change', (event: Event) => { - if(event.type === 'keyup') { - const kevent = event as KeyboardEvent; - if(!kevent.shiftKey && kevent.key == 'Enter') { - button_create.trigger('click'); - return; - } - } - const valid = valid_name(container_name.val() as string, group_type()); - button_create.prop("disabled", !valid); - container_name.parent().toggleClass("is-invalid", !valid); - }).trigger('change'); - tag.find(".group-type").on('change', () => container_name.trigger('change')); - - button_create.on('click', event => { - if(button_create.prop("disabled")) + const group_type = () => (tag.find(".group-type")[0] as HTMLSelectElement).selectedIndex; + container_name.on('keyup change', (event: Event) => { + if(event.type === 'keyup') { + const kevent = event as KeyboardEvent; + if(!kevent.shiftKey && kevent.key == 'Enter') { + button_create.trigger('click'); return; - button_create.prop("disabled", true); /* disable double clicking */ + } + } + const valid = valid_name(container_name.val() as string, group_type()); + button_create.prop("disabled", !valid); + container_name.parent().toggleClass("is-invalid", !valid); + }).trigger('change'); + tag.find(".group-type").on('change', () => container_name.trigger('change')); - modal.close(); - callback(container_name.val() as string, group_type()); - }); - return tag; - }, - footer: null, + button_create.on('click', event => { + if(button_create.prop("disabled")) + return; + button_create.prop("disabled", true); /* disable double clicking */ - width: 600 - }); - modal.htmlTag.find(".modal-body").addClass("modal-group-add"); - modal.open_listener.push(() => { - modal.htmlTag.find(".group-name").focus(); - }); + modal.close(); + callback(container_name.val() as string, group_type()); + }); + return tag; + }, + footer: null, - modal.open(); - } + width: 600 + }); + modal.htmlTag.find(".modal-body").addClass("modal-group-add"); + modal.open_listener.push(() => { + modal.htmlTag.find(".group-name").focus(); + }); + + modal.open(); } \ No newline at end of file diff --git a/shared/js/ui/modal/permission/SenselessPermissions.ts b/shared/js/ui/modal/permission/SenselessPermissions.ts index 06ceed6a..248521af 100644 --- a/shared/js/ui/modal/permission/SenselessPermissions.ts +++ b/shared/js/ui/modal/permission/SenselessPermissions.ts @@ -1,73 +1,71 @@ -/// +import PermissionType from "tc-shared/permission/PermissionType"; -namespace permissions { - export const senseless_server_group_permissions: PermissionType[] = [ - PermissionType.B_CHANNEL_GROUP_INHERITANCE_END - ]; +export const senseless_server_group_permissions: PermissionType[] = [ + PermissionType.B_CHANNEL_GROUP_INHERITANCE_END +]; - const filter = (text, ignore_type) => Object.keys(PermissionType) - .filter(e => e.toLowerCase().substr(ignore_type ? 1 : 0).startsWith(text)).map(e => PermissionType[e]); +const filter = (text, ignore_type) => Object.keys(PermissionType) + .filter(e => e.toLowerCase().substr(ignore_type ? 1 : 0).startsWith(text)).map(e => PermissionType[e]); - export const senseless_channel_group_permissions: PermissionType[] = [ - //Not sensefull to assign serverinstance permission to channel groups - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_serverinstance_")).map(e => PermissionType[e]), - PermissionType.B_SERVERQUERY_LOGIN, +export const senseless_channel_group_permissions: PermissionType[] = [ + //Not sensefull to assign serverinstance permission to channel groups + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_serverinstance_")).map(e => PermissionType[e]), + PermissionType.B_SERVERQUERY_LOGIN, - //Not sensefull to assign virtual server permission to channel groups - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_virtualserver") && e.toLowerCase() === "b_virtualserver_channel_permission_list").map(e => PermissionType[e]), + //Not sensefull to assign virtual server permission to channel groups + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_virtualserver") && e.toLowerCase() === "b_virtualserver_channel_permission_list").map(e => PermissionType[e]), - //Not sensefull to require some playlist permissions - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_playlist")).map(e => PermissionType[e]), - PermissionType.B_PLAYLIST_CREATE, + //Not sensefull to require some playlist permissions + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_playlist")).map(e => PermissionType[e]), + PermissionType.B_PLAYLIST_CREATE, - //Not sensefull to require some playlist permissions - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_client_music")).map(e => PermissionType[e]), - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_music")).map(e => PermissionType[e]), + //Not sensefull to require some playlist permissions + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_client_music")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_music")).map(e => PermissionType[e]), - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_server_group")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("i_server_group")).map(e => PermissionType[e]), - PermissionType.I_MAX_ICON_FILESIZE, - PermissionType.I_MAX_PLAYLIST_SIZE, - PermissionType.I_MAX_PLAYLISTS, + PermissionType.I_MAX_ICON_FILESIZE, + PermissionType.I_MAX_PLAYLIST_SIZE, + PermissionType.I_MAX_PLAYLISTS, - PermissionType.I_CLIENT_KICK_FROM_SERVER_POWER, //Why should channel groups kick clients from server. Yes there are cases, but not the usual once - PermissionType.I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER, + PermissionType.I_CLIENT_KICK_FROM_SERVER_POWER, //Why should channel groups kick clients from server. Yes there are cases, but not the usual once + PermissionType.I_CLIENT_NEEDED_KICK_FROM_SERVER_POWER, - PermissionType.I_CLIENT_BAN_POWER, //Why should channel groups ban clients from server. Yes there are cases, but not the usual once - PermissionType.I_CLIENT_NEEDED_BAN_POWER, + PermissionType.I_CLIENT_BAN_POWER, //Why should channel groups ban clients from server. Yes there are cases, but not the usual once + PermissionType.I_CLIENT_NEEDED_BAN_POWER, - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_complain")).map(e => PermissionType[e]), - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_ban")).map(e => PermissionType[e]), - PermissionType.I_CLIENT_BAN_MAX_BANTIME, - PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_complain")).map(e => PermissionType[e]), + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_ban")).map(e => PermissionType[e]), + PermissionType.I_CLIENT_BAN_MAX_BANTIME, + PermissionType.B_CLIENT_SERVER_TEXTMESSAGE_SEND, - ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_query")).map(e => PermissionType[e]), - PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN, - PermissionType.B_CLIENT_DELETE_DBPROPERTIES, - PermissionType.B_CLIENT_MODIFY_DBPROPERTIES - ]; + ...Object.keys(PermissionType).filter(e => e.toLowerCase().startsWith("b_client_query")).map(e => PermissionType[e]), + PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN, + PermissionType.B_CLIENT_DELETE_DBPROPERTIES, + PermissionType.B_CLIENT_MODIFY_DBPROPERTIES +]; - export const senseless_channel_permissions: PermissionType[] = [ - ...senseless_channel_group_permissions, //Powers and needed powers are not inherited here. We "hide" all powers +export const senseless_channel_permissions: PermissionType[] = [ + ...senseless_channel_group_permissions, //Powers and needed powers are not inherited here. We "hide" all powers - ...filter("_channel_create", true), - ...filter("_client", true), - ...filter("_channel_group", true), - ...filter("_group", true), - ...filter("b_channel_", false), - ...Object.keys(PermissionType).filter(e => { - e = e.toLowerCase(); - return e.indexOf("_power") > 0 && e.indexOf("_needed_") == -1; - }).map(e => PermissionType[e]), + ...filter("_channel_create", true), + ...filter("_client", true), + ...filter("_channel_group", true), + ...filter("_group", true), + ...filter("b_channel_", false), + ...Object.keys(PermissionType).filter(e => { + e = e.toLowerCase(); + return e.indexOf("_power") > 0 && e.indexOf("_needed_") == -1; + }).map(e => PermissionType[e]), - PermissionType.B_ICON_MANAGE, - PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, - PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, - PermissionType.B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER - ]; + PermissionType.B_ICON_MANAGE, + PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, + PermissionType.B_CLIENT_USE_PRIORITY_SPEAKER, + PermissionType.B_CHANNEL_IGNORE_DESCRIPTION_VIEW_POWER +]; - export const senseless_client_permissions: PermissionType[] = [ ]; +export const senseless_client_permissions: PermissionType[] = [ ]; - export const senseless_client_channel_permissions: PermissionType[] = [ ]; -} \ No newline at end of file +export const senseless_client_channel_permissions: PermissionType[] = [ ]; \ No newline at end of file diff --git a/shared/js/ui/server.ts b/shared/js/ui/server.ts index 598f9d22..2cc93c97 100644 --- a/shared/js/ui/server.ts +++ b/shared/js/ui/server.ts @@ -1,7 +1,21 @@ -/// -/// +import {ChannelTree} from "tc-shared/ui/view"; +import {Settings, settings} from "tc-shared/settings"; +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import * as log from "tc-shared/log"; +import {LogCategory, LogType} from "tc-shared/log"; +import {Sound} from "tc-shared/sound/Sounds"; +import * as bookmarks from "tc-shared/bookmarks"; +import {spawnInviteEditor} from "tc-shared/ui/modal/ModalInvite"; +import {openServerInfo} from "tc-shared/ui/modal/ModalServerInfo"; +import {createServerModal} from "tc-shared/ui/modal/ModalServerEdit"; +import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect"; +import {spawnAvatarList} from "tc-shared/ui/modal/ModalAvatarList"; +import {server_connections} from "tc-shared/ui/frames/connection_handlers"; +import {control_bar} from "tc-shared/ui/frames/ControlBar"; +import {connection_log} from "tc-shared/ui/modal/ModalConnect"; +import * as top_menu from "./frames/MenuBar"; -class ServerProperties { +export class ServerProperties { virtualserver_host: string = ""; virtualserver_port: number = 0; @@ -78,7 +92,7 @@ class ServerProperties { virtualserver_total_bytes_uploaded: number = 0; } -interface ServerConnectionInfo { +export interface ServerConnectionInfo { connection_filetransfer_bandwidth_sent: number; connection_filetransfer_bandwidth_received: number; @@ -103,12 +117,12 @@ interface ServerConnectionInfo { connection_ping: number; } -interface ServerAddress { +export interface ServerAddress { host: string; port: number; } -class ServerEntry { +export class ServerEntry { remote_address: ServerAddress; channelTree: ChannelTree; properties: ServerProperties; @@ -209,14 +223,14 @@ class ServerEntry { name: tr("Show server info"), callback: () => { trigger_close = false; - Modals.openServerInfo(this); + openServerInfo(this); }, icon_class: "client-about" }, { type: contextmenu.MenuEntryType.ENTRY, icon_class: "client-invite_buddy", name: tr("Invite buddy"), - callback: () => Modals.spawnInviteEditor(this.channelTree.client) + callback: () => spawnInviteEditor(this.channelTree.client) }, { type: contextmenu.MenuEntryType.HR, name: '' @@ -234,7 +248,7 @@ class ServerEntry { icon_class: "client-virtualserver_edit", name: tr("Edit"), callback: () => { - Modals.createServerModal(this, properties => { + createServerModal(this, properties => { log.info(LogCategory.SERVER, tr("Changing server properties %o"), properties); console.log(tr("Changed properties: %o"), properties); if (properties) { @@ -255,13 +269,13 @@ class ServerEntry { type: contextmenu.MenuEntryType.ENTRY, icon_class: "client-iconviewer", name: tr("View icons"), - callback: () => Modals.spawnIconSelect(this.channelTree.client) + callback: () => spawnIconSelect(this.channelTree.client) }, { type: contextmenu.MenuEntryType.ENTRY, icon_class: 'client-iconsview', name: tr("View avatars"), visible: false, //TODO: Enable again as soon the new design is finished - callback: () => Modals.spawnAvatarList(this.channelTree.client) + callback: () => spawnAvatarList(this.channelTree.client) }, contextmenu.Entry.CLOSE(() => trigger_close ? on_close() : {}) ); diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts index 4530e28e..1e1c0f32 100644 --- a/shared/js/ui/view.ts +++ b/shared/js/ui/view.ts @@ -1,15 +1,25 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// +import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; +import * as log from "tc-shared/log"; +import {Settings, settings} from "tc-shared/settings"; +import PermissionType from "tc-shared/permission/PermissionType"; +import {LogCategory} from "tc-shared/log"; +import {KeyCode, SpecialKey} from "tc-shared/PPTListener"; +import {createInputModal} from "tc-shared/ui/elements/Modal"; +import {Sound} from "tc-shared/sound/Sounds"; +import {Group} from "tc-shared/permission/GroupManager"; +import * as server_log from "tc-shared/ui/frames/server_log"; +import {ServerAddress, ServerEntry} from "tc-shared/ui/server"; +import {ClientMover} from "tc-shared/ui/client_move"; +import {ChannelEntry, ChannelSubscribeMode} from "tc-shared/ui/channel"; +import {ClientEntry, ClientType, LocalClientEntry, MusicClientEntry} from "tc-shared/ui/client"; +import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler"; +import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; +import {formatMessage} from "tc-shared/ui/frames/chat"; +import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient"; +import {createChannelModal} from "tc-shared/ui/modal/ModalCreateChannel"; +import * as ppt from "tc-backend/ppt"; - -class ChannelTree { +export class ChannelTree { client: ConnectionHandler; server: ServerEntry; @@ -465,7 +475,7 @@ class ChannelTree { } onSelect(entry?: ChannelEntry | ClientEntry | ServerEntry, enforce_single?: boolean, flag_shift?: boolean) { - if(this.currently_selected && (ppt.key_pressed(ppt.SpecialKey.SHIFT) || flag_shift) && entry instanceof ClientEntry) { //Currently we're only supporting client multiselects :D + if(this.currently_selected && (ppt.key_pressed(SpecialKey.SHIFT) || flag_shift) && entry instanceof ClientEntry) { //Currently we're only supporting client multiselects :D if(!entry) return; //Nowhere if($.isArray(this.currently_selected)) { @@ -615,7 +625,7 @@ class ChannelTree { name: tr("Ban clients"), invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), callback: () => { - Modals.spawnBanClient(this.client, (clients).map(entry => { + spawnBanClient(this.client, (clients).map(entry => { return { name: entry.clientNickName(), unique_id: entry.properties.client_unique_identifier @@ -644,9 +654,9 @@ class ChannelTree { callback: () => { const param_string = clients.map((_, index) => "{" + index + "}").join(', '); const param_values = clients.map(client => client.createChatTag(true)); - const tag = $.spawn("div").append(...MessageHelper.formatMessage(tr("Do you really want to delete ") + param_string, ...param_values)); + const tag = $.spawn("div").append(...formatMessage(tr("Do you really want to delete ") + param_string, ...param_values)); const tag_container = $.spawn("div").append(tag); - Modals.spawnYesNo(tr("Are you sure?"), tag_container, result => { + spawnYesNo(tr("Are you sure?"), tag_container, result => { if(result) { for(const client of clients) this.client.serverConnection.send_command("musicbotdelete", { @@ -706,7 +716,7 @@ class ChannelTree { } spawnCreateChannel(parent?: ChannelEntry) { - Modals.createChannelModal(this.client, undefined, parent, this.client.permissions, (properties?, permissions?) => { + createChannelModal(this.client, undefined, parent, this.client.permissions, (properties?, permissions?) => { if(!properties) return; properties["cpid"] = parent ? parent.channelId : 0; log.debug(LogCategory.CHANNEL, tr("Creating a new channel.\nProperties: %o\nPermissions: %o"), properties); @@ -735,7 +745,7 @@ class ChannelTree { return new Promise(resolve => { resolve(channel); }) }).then(channel => { - this.client.log.log(log.server.Type.CHANNEL_CREATE, { + this.client.log.log(server_log.Type.CHANNEL_CREATE, { channel: channel.log_data(), creator: this.client.getClient().log_data(), own_action: true diff --git a/shared/js/utils/LaterPromise.ts b/shared/js/utils/LaterPromise.ts new file mode 100644 index 00000000..3c26df5a --- /dev/null +++ b/shared/js/utils/LaterPromise.ts @@ -0,0 +1,49 @@ +export class LaterPromise extends Promise { + private readonly _time: number; + private readonly _handle: Promise; + private _resolve: ($: T) => any; + private _reject: ($: any) => any; + + constructor() { + super((resolve, reject) => {}); + this._handle = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + this._time = Date.now(); + } + + resolved(object: T) { + this._resolve(object); + } + + rejected(reason) { + this._reject(reason); + } + + function_rejected() { + return error => this.rejected(error); + } + + time() { return this._time; } + + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise { + return this._handle.then(onfulfilled, onrejected); + } + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise { + return this._handle.then(onrejected); + } +} \ No newline at end of file diff --git a/shared/js/utils/buffers.ts b/shared/js/utils/buffers.ts new file mode 100644 index 00000000..83043e44 --- /dev/null +++ b/shared/js/utils/buffers.ts @@ -0,0 +1,71 @@ +export function str2ab8(str) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} + +/* FIXME Dont use atob, because it sucks for non UTF-8 tings */ +export function arrayBufferBase64(base64: string) { + base64 = atob(base64); + const buf = new ArrayBuffer(base64.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = base64.length; i < strLen; i++) { + bufView[i] = base64.charCodeAt(i); + } + return buf; +} + +export function base64_encode_ab(source: ArrayBufferLike) { + const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let base64 = ""; + + const bytes = new Uint8Array(source); + const byte_length = bytes.byteLength; + const byte_reminder = byte_length % 3; + const main_length = byte_length - byte_reminder; + + let a, b, c, d; + let chunk; + + // Main loop deals with bytes in chunks of 3 + for (let i = 0; i < main_length; i = i + 3) { + // Combine the three bytes into a single integer + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 + d = (chunk & 63) >> 0; // 63 = (2^6 - 1) << 0 + + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; + } + + // Deal with the remaining bytes and padding + if (byte_reminder == 1) { + chunk = bytes[main_length]; + + a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 + + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4; // 3 = 2^2 - 1 + + base64 += encodings[a] + encodings[b] + '=='; + } else if (byte_reminder == 2) { + chunk = (bytes[main_length] << 8) | bytes[main_length + 1]; + + a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 + + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2; // 15 = 2^4 - 1 + + base64 += encodings[a] + encodings[b] + encodings[c] + '='; + } + + return base64 +} \ No newline at end of file diff --git a/shared/js/utils/helpers.ts b/shared/js/utils/helpers.ts index 1b8cc2d9..9528f4d8 100644 --- a/shared/js/utils/helpers.ts +++ b/shared/js/utils/helpers.ts @@ -1,66 +1,12 @@ -/// - -namespace helpers { - export function hashPassword(password: string) : Promise { - return new Promise((resolve, reject) => { - sha.sha1(password).then(result => { - resolve(btoa(String.fromCharCode.apply(null, new Uint8Array(result)))); - }); +export function hashPassword(password: string) : Promise { + return new Promise((resolve, reject) => { + sha.sha1(password).then(result => { + resolve(btoa(String.fromCharCode.apply(null, new Uint8Array(result)))); }); - } + }); } -class LaterPromise extends Promise { - private _handle: Promise; - private _resolve: ($: T) => any; - private _reject: ($: any) => any; - private _time: number; - - constructor() { - super((resolve, reject) => {}); - this._handle = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - this._time = Date.now(); - } - - resolved(object: T) { - this._resolve(object); - } - - rejected(reason) { - this._reject(reason); - } - - function_rejected() { - return error => this.rejected(error); - } - - time() { return this._time; } - - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise { - return this._handle.then(onfulfilled, onrejected); - } - - /** - * Attaches a callback for only the rejection of the Promise. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of the callback. - */ - catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise { - return this._handle.then(onrejected); - } -} - -const copy_to_clipboard = str => { +export const copy_to_clipboard = str => { console.log(tr("Copy text to clipboard: %s"), str); const el = document.createElement('textarea'); el.value = str; diff --git a/shared/js/voice/RecorderBase.ts b/shared/js/voice/RecorderBase.ts index ba32fb38..deab0e8f 100644 --- a/shared/js/voice/RecorderBase.ts +++ b/shared/js/voice/RecorderBase.ts @@ -1,126 +1,122 @@ -namespace audio { - export namespace recorder { - export interface InputDevice { - unique_id: string; - driver: string; - name: string; - default_input: boolean; +export interface InputDevice { + unique_id: string; + driver: string; + name: string; + default_input: boolean; - supported: boolean; + supported: boolean; - sample_rate: number; - channels: number; - } + sample_rate: number; + channels: number; +} - export enum InputConsumerType { - CALLBACK, - NODE, - NATIVE - } +export enum InputConsumerType { + CALLBACK, + NODE, + NATIVE +} - export interface InputConsumer { - type: InputConsumerType; - } +export interface InputConsumer { + type: InputConsumerType; +} - export interface CallbackInputConsumer extends InputConsumer { - callback_audio?: (buffer: AudioBuffer) => any; - callback_buffer?: (buffer: Float32Array, samples: number, channels: number) => any; - } +export interface CallbackInputConsumer extends InputConsumer { + callback_audio?: (buffer: AudioBuffer) => any; + callback_buffer?: (buffer: Float32Array, samples: number, channels: number) => any; +} - export interface NodeInputConsumer extends InputConsumer { - callback_node: (source_node: AudioNode) => any; - callback_disconnect: (source_node: AudioNode) => any; - } +export interface NodeInputConsumer extends InputConsumer { + callback_node: (source_node: AudioNode) => any; + callback_disconnect: (source_node: AudioNode) => any; +} - export namespace filter { - export enum Type { - THRESHOLD, - VOICE_LEVEL, - STATE - } - - export interface Filter { - type: Type; - - is_enabled() : boolean; - } - - export interface MarginedFilter { - get_margin_frames() : number; - set_margin_frames(value: number); - } - - export interface ThresholdFilter extends Filter, MarginedFilter { - get_threshold() : number; - set_threshold(value: number) : Promise; - - get_attack_smooth() : number; - get_release_smooth() : number; - - set_attack_smooth(value: number); - set_release_smooth(value: number); - - callback_level?: (value: number) => any; - } - - export interface VoiceLevelFilter extends Filter, MarginedFilter { - get_level() : number; - } - - export interface StateFilter extends Filter { - set_state(state: boolean) : Promise; - is_active() : boolean; /* if true the the filter allows data to pass */ - } - } - - export enum InputState { - PAUSED, - INITIALIZING, - RECORDING, - DRY - } - - export enum InputStartResult { - EOK = "eok", - EUNKNOWN = "eunknown", - EBUSY = "ebusy", - ENOTALLOWED = "enotallowed", - ENOTSUPPORTED = "enotsupported" - } - - export interface AbstractInput { - callback_begin: () => any; - callback_end: () => any; - - current_state() : InputState; - - start() : Promise; - stop() : Promise; - - current_device() : InputDevice | undefined; - set_device(device: InputDevice | undefined) : Promise; - - current_consumer() : InputConsumer | undefined; - set_consumer(consumer: InputConsumer) : Promise; - - get_filter(type: filter.Type) : filter.Filter | undefined; - supports_filter(type: audio.recorder.filter.Type) : boolean; - - clear_filter(); - disable_filter(type: filter.Type); - enable_filter(type: filter.Type); - - get_volume() : number; - set_volume(volume: number); - } - - export interface LevelMeter { - device() : InputDevice; - - set_observer(callback: (value: number) => any); - - destory(); - } +export namespace filter { + export enum Type { + THRESHOLD, + VOICE_LEVEL, + STATE } + + export interface Filter { + type: Type; + + is_enabled() : boolean; + } + + export interface MarginedFilter { + get_margin_frames() : number; + set_margin_frames(value: number); + } + + export interface ThresholdFilter extends Filter, MarginedFilter { + get_threshold() : number; + set_threshold(value: number) : Promise; + + get_attack_smooth() : number; + get_release_smooth() : number; + + set_attack_smooth(value: number); + set_release_smooth(value: number); + + callback_level?: (value: number) => any; + } + + export interface VoiceLevelFilter extends Filter, MarginedFilter { + get_level() : number; + } + + export interface StateFilter extends Filter { + set_state(state: boolean) : Promise; + is_active() : boolean; /* if true the the filter allows data to pass */ + } +} + +export enum InputState { + PAUSED, + INITIALIZING, + RECORDING, + DRY +} + +export enum InputStartResult { + EOK = "eok", + EUNKNOWN = "eunknown", + EBUSY = "ebusy", + ENOTALLOWED = "enotallowed", + ENOTSUPPORTED = "enotsupported" +} + +export interface AbstractInput { + callback_begin: () => any; + callback_end: () => any; + + current_state() : InputState; + + start() : Promise; + stop() : Promise; + + current_device() : InputDevice | undefined; + set_device(device: InputDevice | undefined) : Promise; + + current_consumer() : InputConsumer | undefined; + set_consumer(consumer: InputConsumer) : Promise; + + get_filter(type: filter.Type) : filter.Filter | undefined; + supports_filter(type: filter.Type) : boolean; + + clear_filter(); + disable_filter(type: filter.Type); + enable_filter(type: filter.Type); + + get_volume() : number; + set_volume(volume: number); +} + +export interface LevelMeter { + device() : InputDevice; + + set_observer(callback: (value: number) => any); + + destory(); } \ No newline at end of file diff --git a/shared/js/voice/RecorderProfile.ts b/shared/js/voice/RecorderProfile.ts index ec5ea0dc..26825c0e 100644 --- a/shared/js/voice/RecorderProfile.ts +++ b/shared/js/voice/RecorderProfile.ts @@ -1,7 +1,15 @@ -/// +import * as log from "tc-shared/log"; +import {AbstractInput, filter, InputDevice} from "tc-shared/voice/RecorderBase"; +import {KeyDescriptor, KeyHook} from "tc-shared/PPTListener"; +import {LogCategory} from "tc-shared/log"; +import {Settings, settings} from "tc-shared/settings"; +import {ConnectionHandler} from "tc-shared/ConnectionHandler"; +import * as aplayer from "tc-backend/audio/player"; +import * as arecorder from "tc-backend/audio/recorder"; +import * as ppt from "tc-backend/ppt"; -type VadType = "threshold" | "push_to_talk" | "active"; -interface RecorderProfileConfig { +export type VadType = "threshold" | "push_to_talk" | "active"; +export interface RecorderProfileConfig { version: number; /* devices unique id */ @@ -25,13 +33,16 @@ interface RecorderProfileConfig { } } -let default_recorder: RecorderProfile; /* needs initialize */ -class RecorderProfile { +export let default_recorder: RecorderProfile; /* needs initialize */ +export function set_default_recorder(recorder: RecorderProfile) { + default_recorder = recorder; +} +export class RecorderProfile { readonly name; readonly volatile; /* not saving profile */ config: RecorderProfileConfig; - input: audio.recorder.AbstractInput; + input: AbstractInput; current_handler: ConnectionHandler; @@ -43,7 +54,7 @@ class RecorderProfile { record_supported: boolean; - private _ppt_hook: ppt.KeyHook; + private _ppt_hook: KeyHook; private _ppt_timeout: NodeJS.Timer; private _ppt_hook_registered: boolean; @@ -57,28 +68,26 @@ class RecorderProfile { clearTimeout(this._ppt_timeout); this._ppt_timeout = setTimeout(() => { - const filter = this.input.get_filter(audio.recorder.filter.Type.STATE) as audio.recorder.filter.StateFilter; - if(filter) - filter.set_state(true); + const f = this.input.get_filter(filter.Type.STATE) as filter.StateFilter; + if(f) f.set_state(true); }, Math.min(this.config.vad_push_to_talk.delay, 0)); }, callback_press: () => { if(this._ppt_timeout) clearTimeout(this._ppt_timeout); - const filter = this.input.get_filter(audio.recorder.filter.Type.STATE) as audio.recorder.filter.StateFilter; - if(filter) - filter.set_state(false); + const f = this.input.get_filter(filter.Type.STATE) as filter.StateFilter; + if(f) f.set_state(false); }, cancel: false - } as ppt.KeyHook; + } as KeyHook; this._ppt_hook_registered = false; this.record_supported = true; } async initialize() : Promise { - audio.player.on_ready(async () => { + aplayer.on_ready(async () => { this.initialize_input(); await this.load(); await this.reinitialize_filter(); @@ -86,7 +95,7 @@ class RecorderProfile { } private initialize_input() { - this.input = audio.recorder.create_input(); + this.input = arecorder.create_input(); this.input.callback_begin = () => { log.debug(LogCategory.VOICE, "Voice start"); if(this.callback_start) @@ -127,7 +136,7 @@ class RecorderProfile { this.input.set_volume(this.config.volume / 100); { - const all_devices = audio.recorder.devices(); + const all_devices = arecorder.devices(); const devices = all_devices.filter(e => e.default_input || e.unique_id === this.config.device_id); const device = devices.find(e => e.unique_id === this.config.device_id) || devices[0]; @@ -155,27 +164,27 @@ class RecorderProfile { } if(this.config.vad_type === "threshold") { - const filter = this.input.get_filter(audio.recorder.filter.Type.THRESHOLD) as audio.recorder.filter.ThresholdFilter; - await filter.set_threshold(this.config.vad_threshold.threshold); - await filter.set_margin_frames(10); /* 500ms */ + const filter_ = this.input.get_filter(filter.Type.THRESHOLD) as filter.ThresholdFilter; + await filter_.set_threshold(this.config.vad_threshold.threshold); + await filter_.set_margin_frames(10); /* 500ms */ /* legacy client support */ - if('set_attack_smooth' in filter) - filter.set_attack_smooth(.25); - if('set_release_smooth' in filter) - filter.set_release_smooth(.9); + if('set_attack_smooth' in filter_) + filter_.set_attack_smooth(.25); + if('set_release_smooth' in filter_) + filter_.set_release_smooth(.9); - this.input.enable_filter(audio.recorder.filter.Type.THRESHOLD); + this.input.enable_filter(filter.Type.THRESHOLD); } else if(this.config.vad_type === "push_to_talk") { - const filter = this.input.get_filter(audio.recorder.filter.Type.STATE) as audio.recorder.filter.StateFilter; - await filter.set_state(true); + const filter_ = this.input.get_filter(filter.Type.STATE) as filter.StateFilter; + await filter_.set_state(true); for(const key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"]) this._ppt_hook[key] = this.config.vad_push_to_talk[key]; ppt.register_key_hook(this._ppt_hook); this._ppt_hook_registered = true; - this.input.enable_filter(audio.recorder.filter.Type.STATE); + this.input.enable_filter(filter.Type.STATE); } else if(this.config.vad_type === "active") {} } @@ -219,8 +228,8 @@ class RecorderProfile { this.save(); } - get_vad_ppt_key() : ppt.KeyDescriptor { return this.config.vad_push_to_talk; } - set_vad_ppt_key(key: ppt.KeyDescriptor) { + get_vad_ppt_key() : KeyDescriptor { return this.config.vad_push_to_talk; } + set_vad_ppt_key(key: KeyDescriptor) { for(const _key of ["key_alt", "key_ctrl", "key_shift", "key_windows", "key_code"]) this.config.vad_push_to_talk[_key] = key[_key]; @@ -239,8 +248,8 @@ class RecorderProfile { } - current_device() : audio.recorder.InputDevice | undefined { return this.input.current_device(); } - set_device(device: audio.recorder.InputDevice | undefined) : Promise { + current_device() : InputDevice | undefined { return this.input.current_device(); } + set_device(device: InputDevice | undefined) : Promise { this.config.device_id = device ? device.unique_id : undefined; this.save(); return this.input.set_device(device); diff --git a/shared/loader/loader.ts b/shared/loader/loader.ts deleted file mode 100644 index bc537fdb..00000000 --- a/shared/loader/loader.ts +++ /dev/null @@ -1,762 +0,0 @@ -interface Window { - tr(message: string) : string; -} - -namespace loader { - export namespace config { - export const loader_groups = false; - export const verbose = false; - export const error = true; - } - - export type Task = { - name: string, - priority: number, /* tasks with the same priority will be executed in sync */ - function: () => Promise - }; - - export enum Stage { - /* - loading loader required files (incl this) - */ - INITIALIZING, - /* - setting up the loading process - */ - SETUP, - /* - loading all style sheet files - */ - STYLE, - /* - loading all javascript files - */ - JAVASCRIPT, - /* - loading all template files - */ - TEMPLATES, - /* - initializing static/global stuff - */ - JAVASCRIPT_INITIALIZING, - /* - finalizing load process - */ - FINALIZING, - /* - invoking main task - */ - LOADED, - - DONE - } - - let cache_tag: string | undefined; - let current_stage: Stage = undefined; - const tasks: {[key:number]:Task[]} = {}; - - /* test if all files shall be load from cache or fetch again */ - function loader_cache_tag() { - const app_version = (() => { - const version_node = document.getElementById("app_version"); - if(!version_node) return undefined; - - const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; - if(!version) return undefined; - - if(!version || version == "unknown" || version.replace(/0+/, "").length == 0) - return undefined; - - return version; - })(); - if(config.verbose) console.log("Found current app version: %o", app_version); - - if(!app_version) { - /* TODO add warning */ - cache_tag = "?_ts=" + Date.now(); - return; - } - const cached_version = localStorage.getItem("cached_version"); - if(!cached_version || cached_version != app_version) { - loader.register_task(loader.Stage.LOADED, { - priority: 0, - name: "cached version updater", - function: async () => { - localStorage.setItem("cached_version", app_version); - } - }); - } - cache_tag = "?_version=" + app_version; - } - - export function get_cache_version() { return cache_tag; } - - export function finished() { - return current_stage == Stage.DONE; - } - export function running() { return typeof(current_stage) !== "undefined"; } - - export function register_task(stage: Stage, task: Task) { - if(current_stage > stage) { - if(config.error) - console.warn("Register loading task, but it had already been finished. Executing task anyways!"); - task.function().catch(error => { - if(config.error) { - console.error("Failed to execute delayed loader task!"); - console.log(" - %s: %o", task.name, error); - } - - loader.critical_error(error); - }); - return; - } - - const task_array = tasks[stage] || []; - task_array.push(task); - tasks[stage] = task_array.sort((a, b) => a.priority - b.priority); - } - - export async function execute() { - document.getElementById("loader-overlay").classList.add("started"); - loader_cache_tag(); - - const load_begin = Date.now(); - - let begin: number = 0; - let end: number = Date.now(); - while(current_stage <= Stage.LOADED || typeof(current_stage) === "undefined") { - - let current_tasks: Task[] = []; - while((tasks[current_stage] || []).length > 0) { - if(current_tasks.length == 0 || current_tasks[0].priority == tasks[current_stage][0].priority) { - current_tasks.push(tasks[current_stage].pop()); - } else break; - } - - const errors: { - error: any, - task: Task - }[] = []; - - const promises: Promise[] = []; - for(const task of current_tasks) { - try { - if(config.verbose) console.debug("Executing loader %s (%d)", task.name, task.priority); - promises.push(task.function().catch(error => { - errors.push({ - task: task, - error: error - }); - return Promise.resolve(); - })); - } catch(error) { - errors.push({ - task: task, - error: error - }); - } - } - - if(promises.length > 0) { - await Promise.all([...promises]); - } - - if(errors.length > 0) { - if(config.loader_groups) console.groupEnd(); - console.error("Failed to execute loader. The following tasks failed (%d):", errors.length); - for(const error of errors) - console.error(" - %s: %o", error.task.name, error.error); - - throw "failed to process step " + Stage[current_stage]; - } - - if(current_tasks.length == 0) { - if(typeof(current_stage) === "undefined") { - current_stage = -1; - if(config.verbose) console.debug("[loader] Booting app"); - } else if(current_stage < Stage.INITIALIZING) { - if(config.loader_groups) console.groupEnd(); - if(config.verbose) console.debug("[loader] Entering next state (%s). Last state took %dms", Stage[current_stage + 1], (end = Date.now()) - begin); - } else { - if(config.loader_groups) console.groupEnd(); - if(config.verbose) console.debug("[loader] Finish invoke took %dms", (end = Date.now()) - begin); - } - - begin = end; - current_stage += 1; - - if(current_stage != Stage.DONE && config.loader_groups) - console.groupCollapsed("Executing loading stage %s", Stage[current_stage]); - } - } - - /* cleanup */ - { - _script_promises = {}; - } - if(config.verbose) console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - load_begin); - } - export function execute_managed() { - loader.execute().then(() => { - if(config.verbose) { - let message; - if(typeof(window.tr) !== "undefined") - message = tr("App loaded successfully!"); - else - message = "App loaded successfully!"; - - if(typeof(log) !== "undefined") { - /* We're having our log module */ - log.info(LogCategory.GENERAL, message); - } else { - console.log(message); - } - } - }).catch(error => { - console.error("App loading failed: %o", error); - loader.critical_error("Failed to execute loader", "Lookup the console for more detail"); - }); - } - - export type DependSource = { - url: string; - depends: string[]; - } - export type SourcePath = string | DependSource | string[]; - - function script_name(path: SourcePath) { - if(Array.isArray(path)) { - let buffer = ""; - let _or = " or "; - for(let entry of path) - buffer += _or + script_name(entry); - return buffer.slice(_or.length); - } else if(typeof(path) === "string") - return "" + path + ""; - else - return "" + path.url + ""; - } - - class SyntaxError { - source: any; - - constructor(source: any) { - this.source = source; - } - } - - let _script_promises: {[key: string]: Promise} = {}; - export async function load_script(path: SourcePath) : Promise { - if(Array.isArray(path)) { //We have some fallback - return load_script(path[0]).catch(error => { - if(error instanceof SyntaxError) - return Promise.reject(error.source); - - if(path.length > 1) - return load_script(path.slice(1)); - - return Promise.reject(error); - }); - } else { - const source = typeof(path) === "string" ? {url: path, depends: []} : path; - if(source.url.length == 0) return Promise.resolve(); - - return _script_promises[source.url] = (async () => { - /* await depends */ - for(const depend of source.depends) { - if(!_script_promises[depend]) - throw "Missing dependency " + depend; - await _script_promises[depend]; - } - - const tag: HTMLScriptElement = document.createElement("script"); - - await new Promise((resolve, reject) => { - let error = false; - const error_handler = (event: ErrorEvent) => { - if(event.filename == tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error - if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); - window.removeEventListener('error', error_handler as any); - - reject(new SyntaxError(event.error)); - event.preventDefault(); - error = true; - } - }; - window.addEventListener('error', error_handler as any); - - const cleanup = () => { - tag.onerror = undefined; - tag.onload = undefined; - - clearTimeout(timeout_handle); - window.removeEventListener('error', error_handler as any); - }; - const timeout_handle = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 5000); - tag.type = "application/javascript"; - tag.async = true; - tag.defer = true; - tag.onerror = error => { - cleanup(); - tag.remove(); - reject(error); - }; - tag.onload = () => { - cleanup(); - - if(config.verbose) console.debug("Script %o loaded", path); - setTimeout(resolve, 100); - }; - - document.getElementById("scripts").appendChild(tag); - - tag.src = source.url + (cache_tag || ""); - }); - })(); - } - } - - export async function load_scripts(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - script: SourcePath, - error: any - }[] = []; - - for(const script of paths) - promises.push(load_script(script).catch(error => { - errors.push({ - script: script, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if(config.error) { - console.error("Failed to load the following scripts:"); - for(const script of errors) - console.log(" - %o: %o", script.script, script.error); - } - - loader.critical_error("Failed to load script " + script_name(errors[0].script) + "
" + "View the browser console for more information!"); - throw "failed to load script " + script_name(errors[0].script); - } - } - - export async function load_style(path: SourcePath) : Promise { - if(Array.isArray(path)) { //We have some fallback - return load_style(path[0]).catch(error => { - if(error instanceof SyntaxError) - return Promise.reject(error.source); - - if(path.length > 1) - return load_script(path.slice(1)); - - return Promise.reject(error); - }); - } else { - if(!path) { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - const tag: HTMLLinkElement = document.createElement("link"); - - let error = false; - const error_handler = (event: ErrorEvent) => { - if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); - if(event.filename == tag.href) { //FIXME! - window.removeEventListener('error', error_handler as any); - - reject(new SyntaxError(event.error)); - event.preventDefault(); - error = true; - } - }; - window.addEventListener('error', error_handler as any); - - tag.type = "text/css"; - tag.rel = "stylesheet"; - - const cleanup = () => { - tag.onerror = undefined; - tag.onload = undefined; - - clearTimeout(timeout_handle); - window.removeEventListener('error', error_handler as any); - }; - - const timeout_handle = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 5000); - - tag.onerror = error => { - cleanup(); - tag.remove(); - if(config.error) - console.error("File load error for file %s: %o", path, error); - reject("failed to load file " + path); - }; - tag.onload = () => { - cleanup(); - { - const css: CSSStyleSheet = tag.sheet as CSSStyleSheet; - const rules = css.cssRules; - const rules_remove: number[] = []; - const rules_add: string[] = []; - - for(let index = 0; index < rules.length; index++) { - const rule = rules.item(index); - let rule_text = rule.cssText; - - if(rule.cssText.indexOf("%%base_path%%") != -1) { - rules_remove.push(index); - rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname)); - } - } - - for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) { - if(css.removeRule) - css.removeRule(index); - else - css.deleteRule(index); - } - for(const rule of rules_add) - css.insertRule(rule, rules_remove[0]); - } - - if(config.verbose) console.debug("Style sheet %o loaded", path); - setTimeout(resolve, 100); - }; - - document.getElementById("style").appendChild(tag); - tag.href = path + (cache_tag || ""); - }); - } - } - - export async function load_styles(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - sheet: SourcePath, - error: any - }[] = []; - - for(const sheet of paths) - promises.push(load_style(sheet).catch(error => { - errors.push({ - sheet: sheet, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if(loader.config.error) { - console.error("Failed to load the following style sheet:"); - for(const sheet of errors) - console.log(" - %o: %o", sheet.sheet, sheet.error); - } - - loader.critical_error("Failed to load style sheet " + script_name(errors[0].sheet) + "
" + "View the browser console for more information!"); - throw "failed to load style sheet " + script_name(errors[0].sheet); - } - } - - export async function load_template(path: SourcePath) : Promise { - try { - const response = await $.ajax(path + (cache_tag || "")); - - let node = document.createElement("html"); - node.innerHTML = response; - let tags: HTMLCollection; - if(node.getElementsByTagName("body").length > 0) - tags = node.getElementsByTagName("body")[0].children; - else - tags = node.children; - - let root = document.getElementById("templates"); - if(!root) { - loader.critical_error("Failed to find template tag!"); - throw "Failed to find template tag"; - } - while(tags.length > 0){ - let tag = tags.item(0); - root.appendChild(tag); - - } - } catch(error) { - let msg; - if('responseText' in error) - msg = error.responseText; - else if(error instanceof Error) - msg = error.message; - - loader.critical_error("failed to load template " + script_name(path), msg); - throw "template error"; - } - } - - export async function load_templates(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - template: SourcePath, - error: any - }[] = []; - - for(const template of paths) - promises.push(load_template(template).catch(error => { - errors.push({ - template: template, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if (loader.config.error) { - console.error("Failed to load the following templates:"); - for (const sheet of errors) - console.log(" - %o: %o", sheet.template, sheet.error); - } - - loader.critical_error("Failed to load template " + script_name(errors[0].template) + "
" + "View the browser console for more information!"); - throw "failed to load template " + script_name(errors[0].template); - } - } - - - export type ErrorHandler = (message: string, detail: string) => void; - - let _callback_critical_error: ErrorHandler; - let _callback_critical_called: boolean = false; - - export function critical_error(message: string, detail?: string) { - if(_callback_critical_called) { - console.warn("[CRITICAL] %s", message); - if(typeof(detail) === "string") - console.warn("[CRITICAL] %s", detail); - return; - } - - _callback_critical_called = true; - if(_callback_critical_error) { - _callback_critical_error(message, detail); - return; - } - - /* default handling */ - let tag = document.getElementById("critical-load"); - - { - const error_tags = tag.getElementsByClassName("error"); - error_tags[0].innerHTML = message; - } - - if(typeof(detail) === "string") { - let node_detail = tag.getElementsByClassName("detail")[0]; - node_detail.innerHTML = detail; - } - - tag.classList.add("shown"); - } - - export function critical_error_handler(handler?: ErrorHandler, override?: boolean) : ErrorHandler { - if((typeof(handler) === "object" && handler !== _callback_critical_error) || override) - _callback_critical_error = handler; - return _callback_critical_error; - } -} - -{ - - const hello_world = () => { - const clog = console.log; - const print_security = () => { - { - const css = [ - "display: block", - "text-align: center", - "font-size: 42px", - "font-weight: bold", - "-webkit-text-stroke: 2px black", - "color: red" - ].join(";"); - clog("%c ", "font-size: 100px;"); - clog("%cSecurity warning:", css); - } - { - const css = [ - "display: block", - "text-align: center", - "font-size: 18px", - "font-weight: bold" - ].join(";"); - - clog("%cPasting anything in here could give attackers access to your data.", css); - clog("%cUnless you understand exactly what you are doing, close this window and stay safe.", css); - clog("%c ", "font-size: 100px;"); - } - }; - - /* print the hello world */ - { - const css = [ - "display: block", - "text-align: center", - "font-size: 72px", - "font-weight: bold", - "-webkit-text-stroke: 2px black", - "color: #18BC9C" - ].join(";"); - clog("%cHey, hold on!", css); - } - { - const css = [ - "display: block", - "text-align: center", - "font-size: 26px", - "font-weight: bold" - ].join(";"); - - const css_2 = [ - "display: block", - "text-align: center", - "font-size: 26px", - "font-weight: bold", - "color: blue" - ].join(";"); - - const display_detect = /./; - display_detect.toString = function() { print_security(); return ""; }; - - clog("%cLovely to see you using and debugging the TeaSpeak-Web client.", css); - clog("%cIf you have some good ideas or already done some incredible changes,", css); - clog("%cyou'll be may interested to share them here: %chttps://github.com/TeaSpeak/TeaWeb", css, css_2); - clog("%c ", display_detect); - } - }; - - try { /* lets try to print it as VM code :)*/ - let hello_world_code = hello_world.toString(); - hello_world_code = hello_world_code.substr(hello_world_code.indexOf('() => {') + 8); - hello_world_code = hello_world_code.substring(0, hello_world_code.lastIndexOf("}")); - - //Look aheads are not possible with firefox - //hello_world_code = hello_world_code.replace(/(? fadeoutLoader(duration, 0, true), minAge - age); - return; - } - */ - - $(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, duration); - $(".loader .half").animate({width: 0}, duration, () => { - $(".loader").detach(); - }); -} - - -/* set a timeout here, so if this script is merged with the actual loader (like in rel mode) the actual could load this manually here */ -setTimeout(() => { - if(loader.running()) { - if(loader.config.verbose) - console.debug("Do not execute debug loading"); - return; - } - - loader.register_task(loader.Stage.INITIALIZING, { - priority: 100, - function: async () => { - let loader_type; - /* - location.search.replace(/(?:^\?|&)([a-zA-Z_]+)=([.a-zA-Z0-9]+)(?=$|&)/g, (_, key: string, value: string) => { - if(key.toLowerCase() == "loader_target") - loader_type = value; - return ""; - }); - */ - for(const node of document.head.getElementsByTagName("meta")) { - if(node.name === "app-loader-target") { - loader_type = node.content; - if(loader_type) - break; - } - } - loader_type = loader_type || "app"; - if(loader_type === "app") { - try { - await loader.load_scripts(["loader/app.js"]); - } catch (error) { - console.error("Failed to load main app script: %o", error); - loader.critical_error("Failed to load main app script", error); - throw "app loader failed"; - } - } else if(loader_type === "certaccept") { - try { - await loader.load_scripts(["loader/certaccept.js"]); - } catch (error) { - console.error("Failed to load cert accept script: %o", error); - loader.critical_error("Failed to load cert accept script", error); - throw "app loader failed"; - } - } else { - loader.critical_error("Missing loader target: " + loader_type); - throw "Missing loader target: " + loader_type; - } - }, - name: "loading target" - }); - - loader.execute_managed(); -}, 0); diff --git a/shared/popup/certaccept/js/main.ts b/shared/popup/certaccept/js/main.ts index 7d31c110..003129be 100644 --- a/shared/popup/certaccept/js/main.ts +++ b/shared/popup/certaccept/js/main.ts @@ -1,4 +1,9 @@ +import {settings, Settings} from "tc-shared/settings"; +import * as loader from "tc-loader"; +import {LogCategory} from "tc-shared/log"; +import * as bipc from "tc-shared/BrowserIPC"; +const is_debug = false; //TODO: Sync with loader! function tr(text: string) { return text; } loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { @@ -38,4 +43,25 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { } }, priority: 10 +}); + + + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "settings initialisation", + function: async () => Settings.initialize(), + priority: 200 +}); + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "bipc initialisation", + function: async () => bipc.setup(), + priority: 100 +}); + + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + name: "log enabled initialisation", + function: async () => log.initialize(is_debug ? log.LogType.TRACE : log.LogType.INFO), + priority: 150 }); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index fdf294a3..9bd65288 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,8 @@ "paths": { "*": ["shared/declarations/*"], "tc-shared/*": ["shared/js/*"], - "tc-backend/*": ["web/js/*"] + "tc-backend/*": ["shared/backend.d/*"], + "tc-loader": ["loader/exports/loader.d.ts"] } }, "exclude": [ diff --git a/web/js/WebPPTListener.ts b/web/js/WebPPTListener.ts deleted file mode 100644 index 92ec8883..00000000 --- a/web/js/WebPPTListener.ts +++ /dev/null @@ -1,152 +0,0 @@ -/// - - -namespace ppt { - interface WebKeyEvent extends KeyEvent { - canceled: boolean; - } - - let key_listener: ((_: KeyEvent) => any)[] = []; - - function listener_key(type: EventType, event: KeyboardEvent) { - const key_event = { - type: type, - - key: event.key, - key_code: event.code, - - key_ctrl: event.ctrlKey, - key_shift: event.shiftKey, - key_alt: event.altKey, - key_windows: event.metaKey, - - canceled: event.defaultPrevented - } as WebKeyEvent; - //console.debug("Trigger key event %o", key_event); - - for(const listener of key_listener) - listener(key_event); - - if(key_event.canceled) - event.preventDefault(); - } - - const proxy_key_press = event => listener_key(EventType.KEY_PRESS, event); - const proxy_key_release = event => listener_key(EventType.KEY_RELEASE, event); - const proxy_key_typed = event => listener_key(EventType.KEY_TYPED, event); - - export function initialize() : Promise { - document.addEventListener('keypress', proxy_key_typed); - document.addEventListener('keydown', proxy_key_press); - document.addEventListener('keyup', proxy_key_release); - window.addEventListener('blur', listener_blur); - - register_key_listener(listener_hook); - return Promise.resolve(); - } - - export function finalize() { - document.removeEventListener("keypress", proxy_key_typed); - document.removeEventListener("keydown", proxy_key_press); - document.removeEventListener("keyup", proxy_key_release); - window.removeEventListener('blur', listener_blur); - - unregister_key_listener(listener_hook); - } - - export function register_key_listener(listener: (_: KeyEvent) => any) { - key_listener.push(listener); - } - - export function unregister_key_listener(listener: (_: KeyEvent) => any) { - key_listener.remove(listener); - } - - - let key_hooks: KeyHook[] = []; - - interface CurrentState { - keys: {[code: string]:KeyEvent}; - special: { [key:number]:boolean }; - } - let current_state: CurrentState = { - special: [] - } as any; - - let key_hooks_active: KeyHook[] = []; - - function listener_blur() { - current_state.special[SpecialKey.ALT] = false; - current_state.special[SpecialKey.CTRL] = false; - current_state.special[SpecialKey.SHIFT] = false; - current_state.special[SpecialKey.WINDOWS] = false; - - for(const code of Object.keys(current_state)) - if(code !== "special") - delete current_state[code]; - - for(const hook of key_hooks_active) - hook.callback_release(); - key_hooks_active = []; - } - - function listener_hook(event: KeyEvent) { - if(event.type == EventType.KEY_TYPED) - return; - - let old_hooks = [...key_hooks_active]; - let new_hooks = []; - - current_state.special[SpecialKey.ALT] = event.key_alt; - current_state.special[SpecialKey.CTRL] = event.key_ctrl; - current_state.special[SpecialKey.SHIFT] = event.key_shift; - current_state.special[SpecialKey.WINDOWS] = event.key_windows; - - current_state[event.key_code] = undefined; - if(event.type == EventType.KEY_PRESS) { - current_state[event.key_code] = event; - - for(const hook of key_hooks) { - if(hook.key_code !== event.key_code) continue; - if(hook.key_alt != event.key_alt) continue; - if(hook.key_ctrl != event.key_ctrl) continue; - if(hook.key_shift != event.key_shift) continue; - if(hook.key_windows != event.key_windows) continue; - - new_hooks.push(hook); - if(!old_hooks.remove(hook) && hook.callback_press) { - hook.callback_press(); - log.trace(LogCategory.GENERAL, tr("Trigger key press for %o!"), hook); - } - } - } - - //We have a new situation - for(const hook of old_hooks) { - //Do not test for meta key states because they could differ in a key release event - if(hook.key_code === event.key_code) { - if(hook.callback_release) { - hook.callback_release(); - log.trace(LogCategory.GENERAL, tr("Trigger key release for %o!"), hook); - } - } else { - new_hooks.push(hook); - } - } - key_hooks_active = new_hooks; - } - - export function register_key_hook(hook: KeyHook) { - key_hooks.push(hook); - } - - export function unregister_key_hook(hook: KeyHook) { - key_hooks.remove(hook); - } - - export function key_pressed(code: string | SpecialKey) : boolean { - if(typeof(code) === 'string') - return typeof current_state[code] !== "undefined"; - return current_state.special[code]; - } -} \ No newline at end of file diff --git a/web/js/audio/AudioPlayer.ts b/web/js/audio/AudioPlayer.ts deleted file mode 100644 index 25538997..00000000 --- a/web/js/audio/AudioPlayer.ts +++ /dev/null @@ -1,103 +0,0 @@ -namespace audio.player { - let _globalContext: AudioContext; - let _global_destination: GainNode; - - let _globalContextPromise: Promise; - let _initialized_listener: (() => any)[] = []; - let _master_volume: number = 1; - let _no_device = false; - - export function initialize() : boolean { - context(); - return true; - } - - export function initialized() : boolean { - return !!_globalContext && _globalContext.state === 'running'; - } - - function fire_initialized() { - log.info(LogCategory.AUDIO, tr("File initialized for %d listeners"), _initialized_listener.length); - while(_initialized_listener.length > 0) - _initialized_listener.pop_front()(); - } - - - export function context() : AudioContext { - if(_globalContext && _globalContext.state != "suspended") return _globalContext; - - if(!_globalContext) - _globalContext = new (window.webkitAudioContext || window.AudioContext)(); - - _initialized_listener.unshift(() => { - _global_destination = _globalContext.createGain(); - _global_destination.gain.value = _no_device ? 0 : _master_volume; - _global_destination.connect(_globalContext.destination); - }); - if(_globalContext.state == "suspended") { - if(!_globalContextPromise) { - (_globalContextPromise = _globalContext.resume()).then(() => { - fire_initialized(); - }).catch(error => { - loader.critical_error("Failed to initialize global audio context! (" + error + ")"); - }); - } - _globalContext.resume(); //We already have our listener - return _globalContext; - } - - if(_globalContext.state == "running") { - fire_initialized(); - return _globalContext; - } - return _globalContext; - } - - export function get_master_volume() : number { - return _master_volume; - } - export function set_master_volume(volume: number) { - _master_volume = volume; - if(_global_destination) - _global_destination.gain.value = _no_device ? 0 : _master_volume; - } - - export function destination() : AudioNode { - const ctx = context(); - if(!ctx) throw tr("Audio player isn't initialized yet!"); - - return _global_destination; - } - - export function on_ready(cb: () => any) { - if(initialized()) - cb(); - else - _initialized_listener.push(cb); - } - - export const WEB_DEVICE: Device = { - device_id: "default", - name: "default playback", - driver: 'Web Audio' - }; - - export function available_devices() : Promise { - return Promise.resolve([WEB_DEVICE]) - } - - export function set_device(device_id: string) : Promise { - _no_device = !device_id; - _global_destination.gain.value = _no_device ? 0 : _master_volume; - - return Promise.resolve(); - } - - export function current_device() : Device { - return WEB_DEVICE; - } - - export function initializeFromGesture() { - context(); - } -} diff --git a/web/js/audio/WebCodec.ts b/web/js/audio/WebCodec.ts deleted file mode 100644 index fa472b71..00000000 --- a/web/js/audio/WebCodec.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// - -namespace audio.codec { - export function new_instance(type: CodecType) : BasicCodec { - return new CodecWrapperWorker(type); - } - - export function supported(type: CodecType) : boolean { - return type == CodecType.OPUS_MUSIC || type == CodecType.OPUS_VOICE; - } -} \ No newline at end of file diff --git a/web/js/audio/player.ts b/web/js/audio/player.ts new file mode 100644 index 00000000..a929e0e5 --- /dev/null +++ b/web/js/audio/player.ts @@ -0,0 +1,128 @@ +/* +import {Device} from "tc-shared/audio/player"; + +export function initialize() : boolean; +export function initialized() : boolean; + +export function context() : AudioContext; +export function get_master_volume() : number; +export function set_master_volume(volume: number); + +export function destination() : AudioNode; + +export function on_ready(cb: () => any); + +export function available_devices() : Promise; +export function set_device(device_id: string) : Promise; + +export function current_device() : Device; + +export function initializeFromGesture(); +*/ + +import {Device} from "tc-shared/audio/player"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import * as loader from "tc-loader"; + +let _globalContext: AudioContext; +let _global_destination: GainNode; + +let _globalContextPromise: Promise; +let _initialized_listener: (() => any)[] = []; +let _master_volume: number = 1; +let _no_device = false; + +export function initialize() : boolean { + context(); + return true; +} + +export function initialized() : boolean { + return !!_globalContext && _globalContext.state === 'running'; +} + +function fire_initialized() { + log.info(LogCategory.AUDIO, tr("File initialized for %d listeners"), _initialized_listener.length); + while(_initialized_listener.length > 0) + _initialized_listener.pop_front()(); +} + + +export function context() : AudioContext { + if(_globalContext && _globalContext.state != "suspended") return _globalContext; + + if(!_globalContext) + _globalContext = new (window.webkitAudioContext || window.AudioContext)(); + + _initialized_listener.unshift(() => { + _global_destination = _globalContext.createGain(); + _global_destination.gain.value = _no_device ? 0 : _master_volume; + _global_destination.connect(_globalContext.destination); + }); + if(_globalContext.state == "suspended") { + if(!_globalContextPromise) { + (_globalContextPromise = _globalContext.resume()).then(() => { + fire_initialized(); + }).catch(error => { + loader.critical_error("Failed to initialize global audio context! (" + error + ")"); + }); + } + _globalContext.resume(); //We already have our listener + return _globalContext; + } + + if(_globalContext.state == "running") { + fire_initialized(); + return _globalContext; + } + return _globalContext; +} + +export function get_master_volume() : number { + return _master_volume; +} +export function set_master_volume(volume: number) { + _master_volume = volume; + if(_global_destination) + _global_destination.gain.value = _no_device ? 0 : _master_volume; +} + +export function destination() : AudioNode { + const ctx = context(); + if(!ctx) throw tr("Audio player isn't initialized yet!"); + + return _global_destination; +} + +export function on_ready(cb: () => any) { + if(initialized()) + cb(); + else + _initialized_listener.push(cb); +} + +export const WEB_DEVICE: Device = { + device_id: "default", + name: "default playback", + driver: 'Web Audio' +}; + +export function available_devices() : Promise { + return Promise.resolve([WEB_DEVICE]) +} + +export function set_device(device_id: string) : Promise { + _no_device = !device_id; + _global_destination.gain.value = _no_device ? 0 : _master_volume; + + return Promise.resolve(); +} + +export function current_device() : Device { + return WEB_DEVICE; +} + +export function initializeFromGesture() { + context(); +} \ No newline at end of file diff --git a/web/js/audio/recorder.ts b/web/js/audio/recorder.ts new file mode 100644 index 00000000..d79e2db9 --- /dev/null +++ b/web/js/audio/recorder.ts @@ -0,0 +1,873 @@ +import { + AbstractInput, CallbackInputConsumer, + InputConsumer, + InputConsumerType, + InputDevice, InputStartResult, + InputState, + LevelMeter, NodeInputConsumer +} from "tc-shared/voice/RecorderBase"; +import * as log from "tc-shared/log"; +import * as loader from "tc-loader"; +import {LogCategory} from "tc-shared/log"; +import * as aplayer from "./player"; +import * as rbase from "tc-shared/voice/RecorderBase"; + +declare global { + interface MediaStream { + stop(); + } +} + +let _queried_devices: JavascriptInputDevice[]; +let _queried_permissioned: boolean = false; + +export interface JavascriptInputDevice extends InputDevice { + device_id: string; + group_id: string; +} + +function getUserMediaFunctionPromise() : (constraints: MediaStreamConstraints) => Promise { + if('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) + return constraints => navigator.mediaDevices.getUserMedia(constraints); + + const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + if(!_callbacked_function) + return undefined; + + return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); +} + +async function query_devices() { + const general_supported = !!getUserMediaFunctionPromise(); + + try { + const context = aplayer.context(); + const devices = await navigator.mediaDevices.enumerateDevices(); + + _queried_permissioned = false; + if(devices.filter(e => !!e.label).length > 0) + _queried_permissioned = true; + + _queried_devices = devices.filter(e => e.kind === "audioinput").map((e: MediaDeviceInfo): JavascriptInputDevice => { + return { + channels: context ? context.destination.channelCount : 2, + sample_rate: context ? context.sampleRate : 44100, + + default_input: e.deviceId == "default", + + driver: "WebAudio", + name: e.label || "device-id{" + e.deviceId+ "}", + + supported: general_supported, + + device_id: e.deviceId, + group_id: e.groupId, + + unique_id: e.deviceId + } + }); + if(_queried_devices.length > 0 && _queried_devices.filter(e => e.default_input).length == 0) + _queried_devices[0].default_input = true; + } catch(error) { + log.error(LogCategory.AUDIO, tr("Failed to query microphone devices (%o)"), error); + _queried_devices = []; + } +} + +export function devices() : InputDevice[] { + if(typeof(_queried_devices) === "undefined") + query_devices(); + + return _queried_devices || []; +} + + +export function device_refresh_available() : boolean { return true; } +export function refresh_devices() : Promise { return query_devices(); } + +export function create_input() : AbstractInput { return new JavascriptInput(); } + +export async function create_levelmeter(device: InputDevice) : Promise { + const meter = new JavascriptLevelmeter(device as any); + await meter.initialize(); + return meter; +} + +loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { + function: async () => { query_devices(); }, /* May wait for it? */ + priority: 10, + name: "query media devices" +}); + +export namespace filter { + export abstract class JAbstractFilter implements rbase.filter.Filter { + type; + + source_node: AudioNode; + audio_node: NodeType; + + context: AudioContext; + enabled: boolean = false; + + active: boolean = false; /* if true the filter filters! */ + callback_active_change: (new_state: boolean) => any; + + paused: boolean = true; + + abstract initialize(context: AudioContext, source_node: AudioNode); + abstract finalize(); + + /* whatever the input has been paused and we don't expect any input */ + abstract set_pause(flag: boolean); + + is_enabled(): boolean { + return this.enabled; + } + } + + export class JThresholdFilter extends JAbstractFilter implements rbase.filter.ThresholdFilter { + public static update_task_interval = 20; /* 20ms */ + + type = rbase.filter.Type.THRESHOLD; + callback_level?: (value: number) => any; + + private _threshold = 50; + + private _update_task: any; + private _analyser: AnalyserNode; + private _analyse_buffer: Uint8Array; + + private _silence_count = 0; + private _margin_frames = 5; + + private _current_level = 0; + private _smooth_release = 0; + private _smooth_attack = 0; + + finalize() { + this.set_pause(true); + + if(this.source_node) { + try { this.source_node.disconnect(this._analyser) } catch (error) {} + try { this.source_node.disconnect(this.audio_node) } catch (error) {} + } + + this._analyser = undefined; + this.source_node = undefined; + this.audio_node = undefined; + this.context = undefined; + } + + initialize(context: AudioContext, source_node: AudioNode) { + this.context = context; + this.source_node = source_node; + + this.audio_node = context.createGain(); + this._analyser = context.createAnalyser(); + + const optimal_ftt_size = Math.ceil((source_node.context || context).sampleRate * (JThresholdFilter.update_task_interval / 1000)); + const base2_ftt = Math.pow(2, Math.ceil(Math.log2(optimal_ftt_size))); + this._analyser.fftSize = base2_ftt; + + if(!this._analyse_buffer || this._analyse_buffer.length < this._analyser.fftSize) + this._analyse_buffer = new Uint8Array(this._analyser.fftSize); + + this.active = false; + this.audio_node.gain.value = 1; + + this.source_node.connect(this.audio_node); + this.source_node.connect(this._analyser); + + /* force update paused state */ + this.set_pause(!(this.paused = !this.paused)); + } + + get_margin_frames(): number { return this._margin_frames; } + set_margin_frames(value: number) { + this._margin_frames = value; + } + + get_attack_smooth(): number { + return this._smooth_attack; + } + + get_release_smooth(): number { + return this._smooth_release; + } + + set_attack_smooth(value: number) { + this._smooth_attack = value; + } + + set_release_smooth(value: number) { + this._smooth_release = value; + } + + get_threshold(): number { + return this._threshold; + } + + set_threshold(value: number): Promise { + this._threshold = value; + return Promise.resolve(); + } + + public static process(buffer: Uint8Array, ftt_size: number, previous: number, smooth: number) { + let level; + { + let total = 0, float, rms; + + for(let index = 0; index < ftt_size; index++) { + float = ( buffer[index++] / 0x7f ) - 1; + total += (float * float); + } + rms = Math.sqrt(total / ftt_size); + let db = 20 * ( Math.log(rms) / Math.log(10) ); + // sanity check + + db = Math.max(-192, Math.min(db, 0)); + level = 100 + ( db * 1.92 ); + } + + return previous * smooth + level * (1 - smooth); + } + + private _analyse() { + this._analyser.getByteTimeDomainData(this._analyse_buffer); + + let smooth; + if(this._silence_count == 0) + smooth = this._smooth_release; + else + smooth = this._smooth_attack; + + this._current_level = JThresholdFilter.process(this._analyse_buffer, this._analyser.fftSize, this._current_level, smooth); + + this._update_gain_node(); + if(this.callback_level) + this.callback_level(this._current_level); + } + + private _update_gain_node() { + let state; + if(this._current_level > this._threshold) { + this._silence_count = 0; + state = true; + } else { + state = this._silence_count++ < this._margin_frames; + } + if(state) { + this.audio_node.gain.value = 1; + if(this.active) { + this.active = false; + this.callback_active_change(false); + } + } else { + this.audio_node.gain.value = 0; + if(!this.active) { + this.active = true; + this.callback_active_change(true); + } + } + } + + set_pause(flag: boolean) { + if(flag === this.paused) return; + this.paused = flag; + + if(this.paused) { + clearInterval(this._update_task); + this._update_task = undefined; + + if(this.active) { + this.active = false; + this.callback_active_change(false); + } + } else { + if(!this._update_task && this._analyser) + this._update_task = setInterval(() => this._analyse(), JThresholdFilter.update_task_interval); + } + } + } + + export class JStateFilter extends JAbstractFilter implements rbase.filter.StateFilter { + type = rbase.filter.Type.STATE; + + finalize() { + if(this.source_node) { + try { this.source_node.disconnect(this.audio_node) } catch (error) {} + } + + this.source_node = undefined; + this.audio_node = undefined; + this.context = undefined; + } + + initialize(context: AudioContext, source_node: AudioNode) { + this.context = context; + this.source_node = source_node; + + this.audio_node = context.createGain(); + this.audio_node.gain.value = this.active ? 0 : 1; + + this.source_node.connect(this.audio_node); + } + + is_active(): boolean { + return this.active; + } + + set_state(state: boolean): Promise { + if(this.active === state) + return Promise.resolve(); + + this.active = state; + if(this.audio_node) + this.audio_node.gain.value = state ? 0 : 1; + this.callback_active_change(state); + return Promise.resolve(); + } + + set_pause(flag: boolean) { + this.paused = flag; + } + } +} + +class JavascriptInput implements AbstractInput { + private _state: InputState = InputState.PAUSED; + private _current_device: JavascriptInputDevice | undefined; + private _current_consumer: InputConsumer; + + private _current_stream: MediaStream; + private _current_audio_stream: MediaStreamAudioSourceNode; + + private _audio_context: AudioContext; + private _source_node: AudioNode; /* last node which could be connected to the target; target might be the _consumer_node */ + private _consumer_callback_node: ScriptProcessorNode; + private readonly _consumer_audio_callback; + private _volume_node: GainNode; + private _mute_node: GainNode; + + private _filters: rbase.filter.Filter[] = []; + private _filter_active: boolean = false; + + private _volume: number = 1; + + callback_begin: () => any = undefined; + callback_end: () => any = undefined; + + constructor() { + aplayer.on_ready(() => this._audio_initialized()); + this._consumer_audio_callback = this._audio_callback.bind(this); + } + + private _audio_initialized() { + this._audio_context = aplayer.context(); + if(!this._audio_context) + return; + + this._mute_node = this._audio_context.createGain(); + this._mute_node.gain.value = 0; + this._mute_node.connect(this._audio_context.destination); + + this._consumer_callback_node = this._audio_context.createScriptProcessor(1024 * 4); + this._consumer_callback_node.connect(this._mute_node); + + this._volume_node = this._audio_context.createGain(); + this._volume_node.gain.value = this._volume; + + this._initialize_filters(); + if(this._state === InputState.INITIALIZING) + this.start(); + } + + private _initialize_filters() { + const filters = this._filters as any as filter.JAbstractFilter[]; + for(const filter of filters) { + if(filter.is_enabled()) + filter.finalize(); + } + + if(this._audio_context && this._volume_node) { + const active_filter = filters.filter(e => e.is_enabled()); + let stream: AudioNode = this._volume_node; + for(const f of active_filter) { + f.initialize(this._audio_context, stream); + stream = f.audio_node; + } + this._switch_source_node(stream); + } + } + + private _audio_callback(event: AudioProcessingEvent) { + if(!this._current_consumer || this._current_consumer.type !== InputConsumerType.CALLBACK) + return; + + const callback = this._current_consumer as CallbackInputConsumer; + if(callback.callback_audio) + callback.callback_audio(event.inputBuffer); + if(callback.callback_buffer) { + log.warn(LogCategory.AUDIO, tr("AudioInput has callback buffer, but this isn't supported yet!")); + } + } + + current_state() : InputState { return this._state; }; + + private _start_promise: Promise; + async start() : Promise { + if(this._start_promise) { + try { + await this._start_promise; + if(this._state != InputState.PAUSED) + return; + } catch(error) { + log.debug(LogCategory.AUDIO, tr("JavascriptInput:start() Start promise await resulted in an error: %o"), error); + } + } + + return await (this._start_promise = this._start()); + } + + /* request permission for devices only one per time! */ + private static _running_request: Promise; + static async request_media_stream(device_id: string, group_id: string) : Promise { + while(this._running_request) { + try { + await this._running_request; + } catch(error) { } + } + const promise = (this._running_request = this.request_media_stream0(device_id, group_id)); + try { + return await this._running_request; + } finally { + if(this._running_request === promise) + this._running_request = undefined; + } + } + + static async request_media_stream0(device_id: string, group_id: string) : Promise { + const media_function = getUserMediaFunctionPromise(); + if(!media_function) return InputStartResult.ENOTSUPPORTED; + + try { + log.info(LogCategory.AUDIO, tr("Requesting a microphone stream for device %s in group %s"), device_id, group_id); + + const audio_constrains: MediaTrackConstraints = {}; + audio_constrains.deviceId = device_id; + audio_constrains.groupId = group_id; + + audio_constrains.echoCancellation = true; + /* may supported */ (audio_constrains as any).autoGainControl = true; + /* may supported */ (audio_constrains as any).noiseSuppression = true; + /* disabled because most the time we get a OverconstrainedError */ //audio_constrains.sampleSize = {min: 420, max: 960 * 10, ideal: 960}; + + const stream = await media_function({audio: audio_constrains, video: undefined}); + if(!_queried_permissioned) query_devices(); /* we now got permissions, requery devices */ + return stream; + } catch(error) { + if('name' in error) { + if(error.name === "NotAllowedError") { + //createErrorModal(tr("Failed to create microphone"), tr("Microphone recording failed. Please allow TeaWeb access to your microphone")).open(); + //FIXME: Move this to somewhere else! + + log.warn(LogCategory.AUDIO, tr("Microphone request failed (No permissions). Browser message: %o"), error.message); + return InputStartResult.ENOTALLOWED; + } else { + log.warn(LogCategory.AUDIO, tr("Microphone request failed. Request resulted in error: %o: %o"), error.name, error); + } + } else { + log.warn(LogCategory.AUDIO, tr("Failed to initialize recording stream (%o)"), error); + } + return InputStartResult.EUNKNOWN; + } + } + + private async _start() : Promise { + try { + if(this._state != InputState.PAUSED) + throw tr("recorder already started"); + + this._state = InputState.INITIALIZING; + if(!this._current_device) + throw tr("invalid device"); + + if(!this._audio_context) { + debugger; + throw tr("missing audio context"); + } + + const _result = await JavascriptInput.request_media_stream(this._current_device.device_id, this._current_device.group_id); + if(!(_result instanceof MediaStream)) { + this._state = InputState.PAUSED; + return _result; + } + this._current_stream = _result; + + for(const f of this._filters) + if(f.is_enabled() && f instanceof filter.JAbstractFilter) + f.set_pause(false); + this._consumer_callback_node.addEventListener('audioprocess', this._consumer_audio_callback); + + this._current_audio_stream = this._audio_context.createMediaStreamSource(this._current_stream); + this._current_audio_stream.connect(this._volume_node); + this._state = InputState.RECORDING; + return InputStartResult.EOK; + } catch(error) { + if(this._state == InputState.INITIALIZING) { + this._state = InputState.PAUSED; + } + throw error; + } finally { + this._start_promise = undefined; + } + } + + async stop() { + /* await all starts */ + try { + if(this._start_promise) + await this._start_promise; + } catch(error) {} + + this._state = InputState.PAUSED; + if(this._current_audio_stream) + this._current_audio_stream.disconnect(); + + if(this._current_stream) { + if(this._current_stream.stop) + this._current_stream.stop(); + else + this._current_stream.getTracks().forEach(value => { + value.stop(); + }); + } + + this._current_stream = undefined; + this._current_audio_stream = undefined; + for(const f of this._filters) + if(f.is_enabled() && f instanceof filter.JAbstractFilter) + f.set_pause(true); + if(this._consumer_callback_node) + this._consumer_callback_node.removeEventListener('audioprocess', this._consumer_audio_callback); + return undefined; + } + + + current_device(): InputDevice | undefined { + return this._current_device; + } + + async set_device(device: InputDevice | undefined) { + if(this._current_device === device) + return; + + + const saved_state = this._state; + try { + await this.stop(); + } catch(error) { + log.warn(LogCategory.AUDIO, tr("Failed to stop previous record session (%o)"), error); + } + + this._current_device = device as any; /* TODO: Test for device_id and device_group */ + if(!device) { + this._state = saved_state === InputState.PAUSED ? InputState.PAUSED : InputState.DRY; + return; + } + + if(saved_state !== InputState.PAUSED) { + try { + await this.start() + } catch(error) { + log.warn(LogCategory.AUDIO, tr("Failed to start new recording stream (%o)"), error); + throw "failed to start record"; + } + } + return; + } + + + get_filter(type: rbase.filter.Type): rbase.filter.Filter | undefined { + for(const filter of this._filters) + if(filter.type == type) + return filter; + + let new_filter: filter.JAbstractFilter; + switch (type) { + case rbase.filter.Type.STATE: + new_filter = new filter.JStateFilter(); + break; + case rbase.filter.Type.VOICE_LEVEL: + throw "voice filter isn't supported!"; + case rbase.filter.Type.THRESHOLD: + new_filter = new filter.JThresholdFilter(); + break; + default: + throw "invalid filter type, or type isn't implemented! (" + type + ")"; + } + + new_filter.callback_active_change = () => this._recalculate_filter_status(); + this._filters.push(new_filter as any); + this.enable_filter(type); + return new_filter as any; + } + + supports_filter(type: rbase.filter.Type) : boolean { + switch (type) { + case rbase.filter.Type.THRESHOLD: + case rbase.filter.Type.STATE: + return true; + default: + return false; + } + } + + private find_filter(type: rbase.filter.Type) : filter.JAbstractFilter | undefined { + for(const filter of this._filters) + if(filter.type == type) + return filter as any; + return undefined; + } + + clear_filter() { + for(const _filter of this._filters) { + if(!_filter.is_enabled()) + continue; + + const c_filter = _filter as any as filter.JAbstractFilter; + c_filter.finalize(); + c_filter.enabled = false; + } + + this._initialize_filters(); + this._recalculate_filter_status(); + } + + disable_filter(type: rbase.filter.Type) { + const filter = this.find_filter(type); + if(!filter) return; + + /* test if the filter is active */ + if(!filter.is_enabled()) + return; + + filter.enabled = false; + filter.set_pause(true); + filter.finalize(); + this._initialize_filters(); + this._recalculate_filter_status(); + } + + enable_filter(type: rbase.filter.Type) { + const filter = this.get_filter(type) as any as filter.JAbstractFilter; + if(filter.is_enabled()) + return; + + filter.enabled = true; + filter.set_pause(typeof this._current_audio_stream !== "object"); + this._initialize_filters(); + this._recalculate_filter_status(); + } + + private _recalculate_filter_status() { + let filtered = this._filters.filter(e => e.is_enabled()).filter(e => (e as any as filter.JAbstractFilter).active).length > 0; + if(filtered === this._filter_active) + return; + + this._filter_active = filtered; + if(filtered) { + if(this.callback_end) + this.callback_end(); + } else { + if(this.callback_begin) + this.callback_begin(); + } + } + + current_consumer(): InputConsumer | undefined { + return this._current_consumer; + } + + async set_consumer(consumer: InputConsumer) { + if(this._current_consumer) { + if(this._current_consumer.type == InputConsumerType.NODE) { + if(this._source_node) + (this._current_consumer as NodeInputConsumer).callback_disconnect(this._source_node) + } else if(this._current_consumer.type === InputConsumerType.CALLBACK) { + if(this._source_node) + this._source_node.disconnect(this._consumer_callback_node); + } + } + + if(consumer) { + if(consumer.type == InputConsumerType.CALLBACK) { + if(this._source_node) + this._source_node.connect(this._consumer_callback_node); + } else if(consumer.type == InputConsumerType.NODE) { + if(this._source_node) + (consumer as NodeInputConsumer).callback_node(this._source_node); + } else { + throw "native callback consumers are not supported!"; + } + } + this._current_consumer = consumer; + } + + private _switch_source_node(new_node: AudioNode) { + if(this._current_consumer) { + if(this._current_consumer.type == InputConsumerType.NODE) { + const node_consumer = this._current_consumer as NodeInputConsumer; + if(this._source_node) + node_consumer.callback_disconnect(this._source_node); + if(new_node) + node_consumer.callback_node(new_node); + } else if(this._current_consumer.type == InputConsumerType.CALLBACK) { + this._source_node.disconnect(this._consumer_callback_node); + if(new_node) + new_node.connect(this._consumer_callback_node); + } + } + this._source_node = new_node; + } + + get_volume(): number { + return this._volume; + } + + set_volume(volume: number) { + if(volume === this._volume) + return; + this._volume = volume; + this._volume_node.gain.value = volume; + } +} + +class JavascriptLevelmeter implements LevelMeter { + private static _instances: JavascriptLevelmeter[] = []; + private static _update_task: number; + + readonly _device: JavascriptInputDevice; + + private _callback: (num: number) => any; + + private _context: AudioContext; + private _gain_node: GainNode; + private _source_node: MediaStreamAudioSourceNode; + private _analyser_node: AnalyserNode; + + private _media_stream: MediaStream; + + private _analyse_buffer: Uint8Array; + + private _current_level = 0; + + constructor(device: JavascriptInputDevice) { + this._device = device; + } + + async initialize() { + try { + await new Promise((resolve, reject) => { + const timeout = setTimeout(reject, 5000); + aplayer.on_ready(() => { + clearTimeout(timeout); + resolve(); + }); + }); + } catch(error) { + throw tr("audio context timeout"); + } + this._context = aplayer.context(); + if(!this._context) throw tr("invalid context"); + + this._gain_node = this._context.createGain(); + this._gain_node.gain.setValueAtTime(0, 0); + + /* analyser node */ + this._analyser_node = this._context.createAnalyser(); + + const optimal_ftt_size = Math.ceil(this._context.sampleRate * (filter.JThresholdFilter.update_task_interval / 1000)); + this._analyser_node.fftSize = Math.pow(2, Math.ceil(Math.log2(optimal_ftt_size))); + + if(!this._analyse_buffer || this._analyse_buffer.length < this._analyser_node.fftSize) + this._analyse_buffer = new Uint8Array(this._analyser_node.fftSize); + + /* starting stream */ + const _result = await JavascriptInput.request_media_stream(this._device.device_id, this._device.group_id); + if(!(_result instanceof MediaStream)){ + if(_result === InputStartResult.ENOTALLOWED) + throw tr("No permissions"); + if(_result === InputStartResult.ENOTSUPPORTED) + throw tr("Not supported"); + if(_result === InputStartResult.EBUSY) + throw tr("Device busy"); + if(_result === InputStartResult.EUNKNOWN) + throw tr("an error occurred"); + throw _result; + } + this._media_stream = _result; + + this._source_node = this._context.createMediaStreamSource(this._media_stream); + this._source_node.connect(this._analyser_node); + this._analyser_node.connect(this._gain_node); + this._gain_node.connect(this._context.destination); + + JavascriptLevelmeter._instances.push(this); + if(JavascriptLevelmeter._instances.length == 1) { + clearInterval(JavascriptLevelmeter._update_task); + JavascriptLevelmeter._update_task = setInterval(() => JavascriptLevelmeter._analyse_all(), filter.JThresholdFilter.update_task_interval) as any; + } + } + + destory() { + JavascriptLevelmeter._instances.remove(this); + if(JavascriptLevelmeter._instances.length == 0) { + clearInterval(JavascriptLevelmeter._update_task); + JavascriptLevelmeter._update_task = 0; + } + + if(this._source_node) { + this._source_node.disconnect(); + this._source_node = undefined; + } + if(this._media_stream) { + if(this._media_stream.stop) + this._media_stream.stop(); + else + this._media_stream.getTracks().forEach(value => { + value.stop(); + }); + this._media_stream = undefined; + } + if(this._gain_node) { + this._gain_node.disconnect(); + this._gain_node = undefined; + } + if(this._analyser_node) { + this._analyser_node.disconnect(); + this._analyser_node = undefined; + } + } + + device(): InputDevice { + return this._device; + } + + set_observer(callback: (value: number) => any) { + this._callback = callback; + } + + private static _analyse_all() { + for(const instance of [...this._instances]) + instance._analyse(); + } + + private _analyse() { + this._analyser_node.getByteTimeDomainData(this._analyse_buffer); + + this._current_level = filter.JThresholdFilter.process(this._analyse_buffer, this._analyser_node.fftSize, this._current_level, .75); + if(this._callback) + this._callback(this._current_level); + } +} \ No newline at end of file diff --git a/web/js/audio/sounds.ts b/web/js/audio/sounds.ts index 117733bf..86a01874 100644 --- a/web/js/audio/sounds.ts +++ b/web/js/audio/sounds.ts @@ -1,126 +1,129 @@ -namespace audio.sounds { - interface SoundEntry { - cached?: AudioBuffer; - node?: HTMLAudioElement; - } +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import {SoundFile} from "tc-shared/sound/Sounds"; +import * as aplayer from "./player"; - const error_already_handled = "---- error handled ---"; +interface SoundEntry { + cached?: AudioBuffer; + node?: HTMLAudioElement; +} - const file_cache: {[key: string]: Promise & { timestamp: number }} = {}; - let warned = false; +const error_already_handled = "---- error handled ---"; - function get_song_entry(file: sound.SoundFile) : Promise { - if(typeof file_cache[file.path] === "object") { - return new Promise((resolve, reject) => { - if(file_cache[file.path].timestamp + 60 * 1000 > Date.now()) { - file_cache[file.path].then(resolve).catch(reject); - return; - } +const file_cache: {[key: string]: Promise & { timestamp: number }} = {}; +let warned = false; - const original_timestamp = Date.now(); - return file_cache[file.path].catch(error => { - if(file_cache[file.path].timestamp + 60 * 1000 > original_timestamp) - return Promise.reject(error); - delete file_cache[file.path]; - return get_song_entry(file); - }); - }); - } - - const context = audio.player.context(); - if(!context) throw tr("audio context not initialized"); - - return (file_cache[file.path] = Object.assign((async () => { - const entry = {} as SoundEntry; - if(context.decodeAudioData) { - const xhr = new XMLHttpRequest(); - xhr.open('GET', file.path, true); - xhr.responseType = 'arraybuffer'; - - try { - const result = new Promise((resolve, reject) => { - xhr.onload = resolve; - xhr.onerror = reject; - }); - - xhr.send(); - await result; - - if (xhr.status != 200) - throw "invalid response code (" + xhr.status + ")"; - - try { - entry.cached = await context.decodeAudioData(xhr.response); - } catch(error) { - log.error(LogCategory.AUDIO, error); - throw tr("failed to decode audio data"); - } - } catch(error) { - log.error(LogCategory.AUDIO, tr("Failed to load audio file %s. Error: %o"), sound, error); - throw error_already_handled; - } - } else { - if(!warned) { - warned = true; - log.warn(LogCategory.AUDIO, tr("Your browser does not support decodeAudioData! Using a node to playback! This bypasses the audio output and volume regulation!")); - } - const container = $("#sounds"); - const node = $.spawn("audio").attr("src", file.path); - node.appendTo(container); - - entry.node = node[0]; - } - return entry; - })(), { timestamp: Date.now() })); - } - - export async function play_sound(file: sound.SoundFile) : Promise { - const entry = get_song_entry(file); - if(!entry) { - log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s because it could not be resolved."), file.path); - return; - } - - try { - const sound = await entry; - - if(sound.cached) { - const context = audio.player.context(); - if(!context) throw tr("audio context not initialized (this error should never show up!)"); - - const player = context.createBufferSource(); - player.buffer = sound.cached; - player.start(0); - - const play_promise = new Promise(resolve => player.onended = resolve); - if(file.volume != 1 && context.createGain) { - const gain = context.createGain(); - if(gain.gain.setValueAtTime) - gain.gain.setValueAtTime(file.volume, 0); - else - gain.gain.value = file.volume; - - player.connect(gain); - gain.connect(context.destination); - } else { - player.connect(context.destination); - } - - await play_promise; - } else if(sound.node) { - sound.node.currentTime = 0; - await sound.node.play(); - } else { - throw "missing playback handle"; - } - } catch(error) { - if(error === error_already_handled) { - log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s because of an error while loading (see log above)."), file.path); +function get_song_entry(file: SoundFile) : Promise { + if(typeof file_cache[file.path] === "object") { + return new Promise((resolve, reject) => { + if(file_cache[file.path].timestamp + 60 * 1000 > Date.now()) { + file_cache[file.path].then(resolve).catch(reject); return; } - log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s: %o"), file.path, error); + const original_timestamp = Date.now(); + return file_cache[file.path].catch(error => { + if(file_cache[file.path].timestamp + 60 * 1000 > original_timestamp) + return Promise.reject(error); + delete file_cache[file.path]; + return get_song_entry(file); + }); + }); + } + + const context = aplayer.context(); + if(!context) throw tr("audio context not initialized"); + + return (file_cache[file.path] = Object.assign((async () => { + const entry = {} as SoundEntry; + if(context.decodeAudioData) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', file.path, true); + xhr.responseType = 'arraybuffer'; + + try { + const result = new Promise((resolve, reject) => { + xhr.onload = resolve; + xhr.onerror = reject; + }); + + xhr.send(); + await result; + + if (xhr.status != 200) + throw "invalid response code (" + xhr.status + ")"; + + try { + entry.cached = await context.decodeAudioData(xhr.response); + } catch(error) { + log.error(LogCategory.AUDIO, error); + throw tr("failed to decode audio data"); + } + } catch(error) { + log.error(LogCategory.AUDIO, tr("Failed to load audio file %s. Error: %o"), file, error); + throw error_already_handled; + } + } else { + if(!warned) { + warned = true; + log.warn(LogCategory.AUDIO, tr("Your browser does not support decodeAudioData! Using a node to playback! This bypasses the audio output and volume regulation!")); + } + const container = $("#sounds"); + const node = $.spawn("audio").attr("src", file.path); + node.appendTo(container); + + entry.node = node[0]; + } + return entry; + })(), { timestamp: Date.now() })); +} + +export async function play_sound(file: SoundFile) : Promise { + const entry = get_song_entry(file); + if(!entry) { + log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s because it could not be resolved."), file.path); + return; + } + + try { + const sound = await entry; + + if(sound.cached) { + const context = aplayer.context(); + if(!context) throw tr("audio context not initialized (this error should never show up!)"); + + const player = context.createBufferSource(); + player.buffer = sound.cached; + player.start(0); + + const play_promise = new Promise(resolve => player.onended = resolve); + if(file.volume != 1 && context.createGain) { + const gain = context.createGain(); + if(gain.gain.setValueAtTime) + gain.gain.setValueAtTime(file.volume, 0); + else + gain.gain.value = file.volume; + + player.connect(gain); + gain.connect(context.destination); + } else { + player.connect(context.destination); + } + + await play_promise; + } else if(sound.node) { + sound.node.currentTime = 0; + await sound.node.play(); + } else { + throw "missing playback handle"; + } + } catch(error) { + if(error === error_already_handled) { + log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s because of an error while loading (see log above)."), file.path); return; } + + log.warn(LogCategory.AUDIO, tr("Failed to replay sound %s: %o"), file.path, error); + return; } } \ No newline at end of file diff --git a/web/js/codec/BasicCodec.ts b/web/js/codec/BasicCodec.ts index 5334686d..6c772a0c 100644 --- a/web/js/codec/BasicCodec.ts +++ b/web/js/codec/BasicCodec.ts @@ -1,4 +1,8 @@ -/// +import * as log from "tc-shared/log"; +import * as aplayer from "../audio/player"; +import {LogCategory} from "tc-shared/log"; +import {BufferChunk, Codec, CodecClientCache} from "./Codec"; +import {AudioResampler} from "../voice/AudioResampler"; class AVGCalculator { history_size: number = 100; @@ -18,7 +22,7 @@ class AVGCalculator { } } -abstract class BasicCodec implements Codec { +export abstract class BasicCodec implements Codec { protected _audioContext: OfflineAudioContext; protected _decodeResampler: AudioResampler; protected _encodeResampler: AudioResampler; @@ -32,9 +36,9 @@ abstract class BasicCodec implements Codec { protected constructor(codecSampleRate: number) { this.channelCount = 1; this.samplesPerUnit = 960; - this._audioContext = new (window.webkitOfflineAudioContext || window.OfflineAudioContext)(audio.player.destination().channelCount, 1024, audio.player.context().sampleRate); + this._audioContext = new (window.webkitOfflineAudioContext || window.OfflineAudioContext)(aplayer.destination().channelCount, 1024, aplayer.context().sampleRate); this._codecSampleRate = codecSampleRate; - this._decodeResampler = new AudioResampler(audio.player.context().sampleRate); + this._decodeResampler = new AudioResampler(aplayer.context().sampleRate); this._encodeResampler = new AudioResampler(codecSampleRate); } diff --git a/web/js/codec/Codec.ts b/web/js/codec/Codec.ts index fbbd416d..9b74ae52 100644 --- a/web/js/codec/Codec.ts +++ b/web/js/codec/Codec.ts @@ -1,8 +1,8 @@ -interface CodecCostructor { +export interface CodecConstructor { new (codecSampleRate: number) : Codec; } -enum CodecType { +export enum CodecType { OPUS_VOICE, OPUS_MUSIC, @@ -12,7 +12,7 @@ enum CodecType { CELT_MONO } -class BufferChunk { +export class BufferChunk { buffer: AudioBuffer; index: number; @@ -34,7 +34,7 @@ class BufferChunk { } } -class CodecClientCache { +export class CodecClientCache { _last_access: number; _chunks: BufferChunk[] = []; @@ -46,7 +46,7 @@ class CodecClientCache { } } -interface Codec { +export interface Codec { on_encoded_data: (Uint8Array) => void; channelCount: number; diff --git a/web/js/codec/CodecWrapperRaw.ts b/web/js/codec/CodecWrapperRaw.ts index dc7b937d..b6b591f1 100644 --- a/web/js/codec/CodecWrapperRaw.ts +++ b/web/js/codec/CodecWrapperRaw.ts @@ -1,6 +1,6 @@ -/// +import {BasicCodec} from "./BasicCodec"; -class CodecWrapperRaw extends BasicCodec { +export class CodecWrapperRaw extends BasicCodec { converterRaw: any; converter: Uint8Array; bufferSize: number = 4096 * 4; diff --git a/web/js/codec/CodecWrapperWorker.ts b/web/js/codec/CodecWrapperWorker.ts index de79127b..ea6c42ed 100644 --- a/web/js/codec/CodecWrapperWorker.ts +++ b/web/js/codec/CodecWrapperWorker.ts @@ -1,6 +1,10 @@ -/// +import {BasicCodec} from "./BasicCodec"; +import {CodecType} from "./Codec"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import {settings} from "tc-shared/settings"; -class CodecWrapperWorker extends BasicCodec { +export class CodecWrapperWorker extends BasicCodec { private _worker: Worker; private _workerListener: {token: string, resolve: (data: any) => void}[] = []; private _workerCallbackToken = "callback_token"; diff --git a/web/js/connection.ts b/web/js/connection.ts new file mode 100644 index 00000000..f6867385 --- /dev/null +++ b/web/js/connection.ts @@ -0,0 +1,4 @@ +import * as sconnection from "./connection/ServerConnection"; + +export const spawn_server_connection = sconnection.spawn_server_connection; +export const destroy_server_connection = sconnection.destroy_server_connection; \ No newline at end of file diff --git a/web/js/connection/ServerConnection.ts b/web/js/connection/ServerConnection.ts index 2c9d3bc9..9b7b3469 100644 --- a/web/js/connection/ServerConnection.ts +++ b/web/js/connection/ServerConnection.ts @@ -1,3 +1,23 @@ +import { + AbstractServerConnection, + CommandOptionDefaults, + CommandOptions, + ConnectionStateListener, voice +} from "tc-shared/connection/ConnectionBase"; +import {ConnectionHandler, ConnectionState, DisconnectReason} from "tc-shared/ConnectionHandler"; +import {ServerAddress} from "tc-shared/ui/server"; +import {HandshakeHandler} from "tc-shared/connection/HandshakeHandler"; +import {ConnectionCommandHandler, ServerConnectionCommandBoss} from "tc-shared/connection/CommandHandler"; +import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; +import {settings, Settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import {Regex} from "tc-shared/ui/modal/ModalConnect"; +import AbstractVoiceConnection = voice.AbstractVoiceConnection; +import {AbstractCommandHandlerBoss} from "tc-shared/connection/AbstractCommandHandler"; +import * as elog from "tc-shared/ui/frames/server_log"; +import {VoiceConnection} from "../voice/VoiceHandler"; + class ReturnListener { resolve: (value?: T | PromiseLike) => void; reject: (reason?: any) => void; @@ -6,499 +26,497 @@ class ReturnListener { timeout: NodeJS.Timer; } -namespace connection { - export class ServerConnection extends AbstractServerConnection { - _connectionState: ConnectionState = ConnectionState.UNCONNECTED; +export class ServerConnection extends AbstractServerConnection { + _connectionState: ConnectionState = ConnectionState.UNCONNECTED; - private _remote_address: ServerAddress; - private _handshakeHandler: HandshakeHandler; + private _remote_address: ServerAddress; + private _handshakeHandler: HandshakeHandler; - private _command_boss: ServerConnectionCommandBoss; - private _command_handler_default: ConnectionCommandHandler; + private _command_boss: ServerConnectionCommandBoss; + private _command_handler_default: ConnectionCommandHandler; - private _socket_connected: WebSocket; + private _socket_connected: WebSocket; - private _connect_timeout_timer: NodeJS.Timer = undefined; + private _connect_timeout_timer: NodeJS.Timer = undefined; - private _connected: boolean = false; - private _retCodeIdx: number; - private _retListener: ReturnListener[]; + private _connected: boolean = false; + private _retCodeIdx: number; + private _retListener: ReturnListener[]; - private _connection_state_listener: connection.ConnectionStateListener; - private _voice_connection: audio.js.VoiceConnection; + private _connection_state_listener: ConnectionStateListener; + private _voice_connection: VoiceConnection; - private _ping = { - thread_id: 0, + private _ping = { + thread_id: 0, - last_request: 0, - last_response: 0, + last_request: 0, + last_response: 0, - request_id: 0, - interval: 5000, - timeout: 7500, + request_id: 0, + interval: 5000, + timeout: 7500, - value: 0, - value_native: 0 /* ping value for native (WS) */ + value: 0, + value_native: 0 /* ping value for native (WS) */ + }; + + constructor(client : ConnectionHandler) { + super(client); + + this._retCodeIdx = 0; + this._retListener = []; + + this._command_boss = new ServerConnectionCommandBoss(this); + this._command_handler_default = new ConnectionCommandHandler(this); + + this._command_boss.register_handler(this._command_handler_default); + this.command_helper.initialize(); + + if(!settings.static_global(Settings.KEY_DISABLE_VOICE, false)) + this._voice_connection = new VoiceConnection(this); + } + + destroy() { + this.disconnect("handle destroyed").catch(error => { + log.warn(LogCategory.NETWORKING, tr("Failed to disconnect on server connection destroy: %o"), error); + }).then(() => { + clearInterval(this._ping.thread_id); + clearTimeout(this._connect_timeout_timer); + + for(const listener of this._retListener) { + try { + listener.reject("handler destroyed"); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to reject command promise: %o"), error); + } + } + this._retListener = undefined; + + this.command_helper.destroy(); + + this._command_handler_default && this._command_boss.unregister_handler(this._command_handler_default); + this._command_handler_default = undefined; + + this._voice_connection && this._voice_connection.destroy(); + this._voice_connection = undefined; + + this._command_boss && this._command_boss.destroy(); + this._command_boss = undefined; + }); + } + + private generateReturnCode() : string { + return (this._retCodeIdx++).toString(); + } + + async connect(address : ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise { + timeout = typeof(timeout) === "number" ? timeout : 5000; + + try { + await this.disconnect() + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to close old connection properly. Error: %o"), error); + throw "failed to cleanup old connection"; + } + + this.updateConnectionState(ConnectionState.CONNECTING); + this._remote_address = address; + + this._handshakeHandler = handshake; + this._handshakeHandler.setConnection(this); + + /* The direct one connect directly to the target address. The other via the .con-gate.work */ + let local_direct_socket: WebSocket; + let local_proxy_socket: WebSocket; + let connected_socket: WebSocket; + let local_timeout_timer: NodeJS.Timer; + + /* setting up an timeout */ + local_timeout_timer = setTimeout(async () => { + log.error(LogCategory.NETWORKING, tr("Connect timeout triggered. Aborting connect attempt!")); + try { + await this.disconnect(); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to close connection after timeout had been triggered! (%o)"), error); + } + + error_cleanup(); + this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); + }, timeout); + this._connect_timeout_timer = local_timeout_timer; + + const error_cleanup = () => { + try { local_direct_socket.close(); } catch(ex) {} + try { local_proxy_socket.close(); } catch(ex) {} + clearTimeout(local_timeout_timer); }; - constructor(client : ConnectionHandler) { - super(client); + try { + let proxy_host; + if(Regex.IP_V4.test(address.host)) + proxy_host = address.host.replace(/\./g, "-") + ".con-gate.work"; + else if(Regex.IP_V6.test(address.host)) + proxy_host = address.host.replace(/\[(.*)]/, "$1").replace(/:/g, "_") + ".con-gate.work"; - this._retCodeIdx = 0; - this._retListener = []; + if(proxy_host && !settings.static_global(Settings.KEY_CONNECT_NO_DNSPROXY)) + local_proxy_socket = new WebSocket('wss://' + proxy_host + ":" + address.port); + local_direct_socket = new WebSocket('wss://' + address.host + ":" + address.port); - this._command_boss = new ServerConnectionCommandBoss(this); - this._command_handler_default = new ConnectionCommandHandler(this); + connected_socket = await new Promise(resolve => { + let pending = 0, succeed = false; + if(local_proxy_socket) { + pending++; - this._command_boss.register_handler(this._command_handler_default); - this.command_helper.initialize(); + local_proxy_socket.onerror = event => { + --pending; + if(this._connect_timeout_timer != local_timeout_timer) + log.trace(LogCategory.NETWORKING, tr("Proxy socket send an error while connecting. Pending sockets: %d. Any succeed: %s"), pending, succeed ? tr("yes") : tr("no")); + if(!succeed && pending == 0) + resolve(undefined); + }; - if(!settings.static_global(Settings.KEY_DISABLE_VOICE, false)) - this._voice_connection = new audio.js.VoiceConnection(this); - } - - destroy() { - this.disconnect("handle destroyed").catch(error => { - log.warn(LogCategory.NETWORKING, tr("Failed to disconnect on server connection destroy: %o"), error); - }).then(() => { - clearInterval(this._ping.thread_id); - clearTimeout(this._connect_timeout_timer); - - for(const listener of this._retListener) { - try { - listener.reject("handler destroyed"); - } catch(error) { - log.warn(LogCategory.NETWORKING, tr("Failed to reject command promise: %o"), error); - } - } - this._retListener = undefined; - - this.command_helper.destroy(); - - this._command_handler_default && this._command_boss.unregister_handler(this._command_handler_default); - this._command_handler_default = undefined; - - this._voice_connection && this._voice_connection.destroy(); - this._voice_connection = undefined; - - this._command_boss && this._command_boss.destroy(); - this._command_boss = undefined; - }); - } - - private generateReturnCode() : string { - return (this._retCodeIdx++).toString(); - } - - async connect(address : ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise { - timeout = typeof(timeout) === "number" ? timeout : 5000; - - try { - await this.disconnect() - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to close old connection properly. Error: %o"), error); - throw "failed to cleanup old connection"; - } - - this.updateConnectionState(ConnectionState.CONNECTING); - this._remote_address = address; - - this._handshakeHandler = handshake; - this._handshakeHandler.setConnection(this); - - /* The direct one connect directly to the target address. The other via the .con-gate.work */ - let local_direct_socket: WebSocket; - let local_proxy_socket: WebSocket; - let connected_socket: WebSocket; - let local_timeout_timer: NodeJS.Timer; - - /* setting up an timeout */ - local_timeout_timer = setTimeout(async () => { - log.error(LogCategory.NETWORKING, tr("Connect timeout triggered. Aborting connect attempt!")); - try { - await this.disconnect(); - } catch(error) { - log.warn(LogCategory.NETWORKING, tr("Failed to close connection after timeout had been triggered! (%o)"), error); - } - - error_cleanup(); - this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); - }, timeout); - this._connect_timeout_timer = local_timeout_timer; - - const error_cleanup = () => { - try { local_direct_socket.close(); } catch(ex) {} - try { local_proxy_socket.close(); } catch(ex) {} - clearTimeout(local_timeout_timer); - }; - - try { - let proxy_host; - if(Modals.Regex.IP_V4.test(address.host)) - proxy_host = address.host.replace(/\./g, "-") + ".con-gate.work"; - else if(Modals.Regex.IP_V6.test(address.host)) - proxy_host = address.host.replace(/\[(.*)]/, "$1").replace(/:/g, "_") + ".con-gate.work"; - - if(proxy_host && !settings.static_global(Settings.KEY_CONNECT_NO_DNSPROXY)) - local_proxy_socket = new WebSocket('wss://' + proxy_host + ":" + address.port); - local_direct_socket = new WebSocket('wss://' + address.host + ":" + address.port); - - connected_socket = await new Promise(resolve => { - let pending = 0, succeed = false; - if(local_proxy_socket) { - pending++; - - local_proxy_socket.onerror = event => { - --pending; - if(this._connect_timeout_timer != local_timeout_timer) - log.trace(LogCategory.NETWORKING, tr("Proxy socket send an error while connecting. Pending sockets: %d. Any succeed: %s"), pending, succeed ? tr("yes") : tr("no")); - if(!succeed && pending == 0) - resolve(undefined); - }; - - local_proxy_socket.onopen = event => { - --pending; - if(this._connect_timeout_timer != local_timeout_timer) - log.trace(LogCategory.NETWORKING, tr("Proxy socket connected. Pending sockets: %d. Any succeed before: %s"), pending, succeed ? tr("yes") : tr("no")); - if(!succeed) { - succeed = true; - resolve(local_proxy_socket); - } - }; - } - - if(local_direct_socket) { - pending++; - - local_direct_socket.onerror = event => { - --pending; - if(this._connect_timeout_timer != local_timeout_timer) - log.trace(LogCategory.NETWORKING, tr("Direct socket send an error while connecting. Pending sockets: %d. Any succeed: %s"), pending, succeed ? tr("yes") : tr("no")); - if(!succeed && pending == 0) - resolve(undefined); - }; - - local_direct_socket.onopen = event => { - --pending; - if(this._connect_timeout_timer != local_timeout_timer) - log.trace(LogCategory.NETWORKING, tr("Direct socket connected. Pending sockets: %d. Any succeed before: %s"), pending, succeed ? tr("yes") : tr("no")); - if(!succeed) { - succeed = true; - resolve(local_direct_socket); - } - }; - } - - if(local_proxy_socket && local_proxy_socket.readyState == WebSocket.OPEN) - local_proxy_socket.onopen(undefined); - - if(local_direct_socket && local_direct_socket.readyState == WebSocket.OPEN) - local_direct_socket.onopen(undefined); - }); - - if(!connected_socket) { - //We failed to connect. Lets test if we're still relevant - if(this._connect_timeout_timer != local_timeout_timer) { - log.trace(LogCategory.NETWORKING, tr("Failed to connect to %s, but we're already obsolete."), address.host + ":" + address.port); - error_cleanup(); - } else { - try { - await this.disconnect(); - } catch(error) { - log.warn(LogCategory.NETWORKING, tr("Failed to cleanup connection after unsuccessful connect attempt: %o"), error); + local_proxy_socket.onopen = event => { + --pending; + if(this._connect_timeout_timer != local_timeout_timer) + log.trace(LogCategory.NETWORKING, tr("Proxy socket connected. Pending sockets: %d. Any succeed before: %s"), pending, succeed ? tr("yes") : tr("no")); + if(!succeed) { + succeed = true; + resolve(local_proxy_socket); } - error_cleanup(); - this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); - } - return; + }; } + if(local_direct_socket) { + pending++; + + local_direct_socket.onerror = event => { + --pending; + if(this._connect_timeout_timer != local_timeout_timer) + log.trace(LogCategory.NETWORKING, tr("Direct socket send an error while connecting. Pending sockets: %d. Any succeed: %s"), pending, succeed ? tr("yes") : tr("no")); + if(!succeed && pending == 0) + resolve(undefined); + }; + + local_direct_socket.onopen = event => { + --pending; + if(this._connect_timeout_timer != local_timeout_timer) + log.trace(LogCategory.NETWORKING, tr("Direct socket connected. Pending sockets: %d. Any succeed before: %s"), pending, succeed ? tr("yes") : tr("no")); + if(!succeed) { + succeed = true; + resolve(local_direct_socket); + } + }; + } + + if(local_proxy_socket && local_proxy_socket.readyState == WebSocket.OPEN) + local_proxy_socket.onopen(undefined); + + if(local_direct_socket && local_direct_socket.readyState == WebSocket.OPEN) + local_direct_socket.onopen(undefined); + }); + + if(!connected_socket) { + //We failed to connect. Lets test if we're still relevant if(this._connect_timeout_timer != local_timeout_timer) { - log.trace(LogCategory.NETWORKING, tr("Successfully connected to %s, but we're already obsolete. Closing connections"), address.host + ":" + address.port); + log.trace(LogCategory.NETWORKING, tr("Failed to connect to %s, but we're already obsolete."), address.host + ":" + address.port); error_cleanup(); - return; - } - - clearTimeout(local_timeout_timer); - this._connect_timeout_timer = undefined; - - if(connected_socket == local_proxy_socket) { - log.debug(LogCategory.NETWORKING, tr("Established a TCP connection to %s via proxy to %s"), address.host + ":" + address.port, proxy_host); - this._remote_address.host = proxy_host; } else { - log.debug(LogCategory.NETWORKING, tr("Established a TCP connection to %s directly"), address.host + ":" + address.port); - } - - this._socket_connected = connected_socket; - this._socket_connected.onclose = event => { - if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ - - this.client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, { - code: event.code, - reason: event.reason, - event: event - }); - }; - - this._socket_connected.onerror = e => { - if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ - - log.warn(LogCategory.NETWORKING, tr("Received web socket error: (%o)"), e); - }; - - this._socket_connected.onmessage = msg => { - if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ - - this.handle_socket_message(msg.data); - }; - - this._connected = true; - this.start_handshake(); - } catch (error) { - error_cleanup(); - if(this._socket_connected != connected_socket && this._connect_timeout_timer != local_timeout_timer) - return; /* we're not from interest anymore */ - - log.warn(LogCategory.NETWORKING, tr("Received unexpected error while connecting: %o"), error); - try { - await this.disconnect(); - } catch(error) { - log.warn(LogCategory.NETWORKING, tr("Failed to cleanup connection after unsuccessful connect attempt: %o"), error); - } - this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE, error); - } - } - - private start_handshake() { - this.updateConnectionState(ConnectionState.INITIALISING); - this.client.log.log(log.server.Type.CONNECTION_LOGIN, {}); - this._handshakeHandler.initialize(); - this._handshakeHandler.startHandshake(); - } - - updateConnectionState(state: ConnectionState) { - const old_state = this._connectionState; - this._connectionState = state; - if(this._connection_state_listener) - this._connection_state_listener(old_state, state); - } - - async disconnect(reason?: string) : Promise { - clearTimeout(this._connect_timeout_timer); - this._connect_timeout_timer = undefined; - - clearTimeout(this._ping.thread_id); - this._ping.thread_id = undefined; - - if(typeof(reason) === "string") { - //TODO send disconnect reason - } - - - if(this._connectionState != ConnectionState.UNCONNECTED) - this.updateConnectionState(ConnectionState.UNCONNECTED); - - if(this._voice_connection) - this._voice_connection.drop_rtp_session(); - - - if(this._socket_connected) { - this._socket_connected.close(3000 + 0xFF, tr("request disconnect")); - this._socket_connected = undefined; - } - - - for(let future of this._retListener) - future.reject(tr("Connection closed")); - this._retListener = []; - - this._connected = false; - this._retCodeIdx = 0; - } - - private handle_socket_message(data) { - if(typeof(data) === "string") { - let json; - try { - json = JSON.parse(data); - } catch(e) { - log.warn(LogCategory.NETWORKING, tr("Could not parse message json!")); - alert(e); // error in the above string (in this case, yes)! - return; - } - if(json["type"] === undefined) { - log.warn(LogCategory.NETWORKING, tr("Missing data type in message!")); - return; - } - if(json["type"] === "command") { - let group = log.group(log.LogType.DEBUG, LogCategory.NETWORKING, tr("Handling command '%s'"), json["command"]); - group.log(tr("Handling command '%s'"), json["command"]); - group.group(log.LogType.TRACE, tr("Json:")).collapsed(true).log("%o", json).end(); - - this._command_boss.invoke_handle({ - command: json["command"], - arguments: json["data"] - }); - - if(json["command"] === "initserver") { - this._ping.thread_id = setInterval(() => this.do_ping(), this._ping.interval) as any; - this.do_ping(); - this.updateConnectionState(ConnectionState.CONNECTED); - if(this._voice_connection) - this._voice_connection.start_rtc_session(); /* FIXME: Move it to a handler boss and not here! */ + try { + await this.disconnect(); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to cleanup connection after unsuccessful connect attempt: %o"), error); } - group.end(); - } else if(json["type"] === "WebRTC") { - if(this._voice_connection) - this._voice_connection.handleControlPacket(json); - else - log.warn(LogCategory.NETWORKING, tr("Dropping WebRTC command packet, because we haven't a bridge.")) - } else if(json["type"] === "ping") { - this.sendData(JSON.stringify({ - type: 'pong', - payload: json["payload"] - })); - } else if(json["type"] === "pong") { - const id = parseInt(json["payload"]); - if(id != this._ping.request_id) { - log.warn(LogCategory.NETWORKING, tr("Received pong which is older than the last request. Delay may over %oms? (Index: %o, Current index: %o)"), this._ping.timeout, id, this._ping.request_id); - } else { - this._ping.last_response = 'now' in performance ? performance.now() : Date.now(); - this._ping.value = this._ping.last_response - this._ping.last_request; - this._ping.value_native = parseInt(json["ping_native"]) / 1000; /* we're getting it in microseconds and not milliseconds */ - log.debug(LogCategory.NETWORKING, tr("Received new pong. Updating ping to: JS: %o Native: %o"), this._ping.value.toFixed(3), this._ping.value_native.toFixed(3)); - } - } else { - log.warn(LogCategory.NETWORKING, tr("Unknown command type %o"), json["type"]); + error_cleanup(); + this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); } - } else { - log.warn(LogCategory.NETWORKING, tr("Received unknown message of type %s. Dropping message"), typeof(data)); - } - } - - sendData(data: any) { - if(!this._socket_connected || this._socket_connected.readyState != 1) { - log.warn(LogCategory.NETWORKING, tr("Tried to send on a invalid socket (%s)"), this._socket_connected ? "invalid state (" + this._socket_connected.readyState + ")" : "invalid socket"); return; } - this._socket_connected.send(data); - } - private commandiefy(input: any) : string { - return JSON.stringify(input, (key, value) => { - switch (typeof value) { - case "boolean": return value == true ? "1" : "0"; - case "function": return value(); - default: - return value; - } - }); - - } - - send_command(command: string, data?: any | any[], _options?: CommandOptions) : Promise { - if(!this._socket_connected || !this.connected()) { - log.warn(LogCategory.NETWORKING, tr("Tried to send a command without a valid connection.")); - return Promise.reject(tr("not connected")); + if(this._connect_timeout_timer != local_timeout_timer) { + log.trace(LogCategory.NETWORKING, tr("Successfully connected to %s, but we're already obsolete. Closing connections"), address.host + ":" + address.port); + error_cleanup(); + return; } - const options: CommandOptions = {}; - Object.assign(options, CommandOptionDefaults); - Object.assign(options, _options); + clearTimeout(local_timeout_timer); + this._connect_timeout_timer = undefined; - data = $.isArray(data) ? data : [data || {}]; - if(data.length == 0) /* we require min one arg to append return_code */ - data.push({}); - - const _this = this; - let result = new Promise((resolve, failed) => { - let _data = $.isArray(data) ? data : [data]; - let retCode = _data[0]["return_code"] !== undefined ? _data[0].return_code : _this.generateReturnCode(); - _data[0].return_code = retCode; - - let listener = new ReturnListener(); - listener.resolve = resolve; - listener.reject = failed; - listener.code = retCode; - listener.timeout = setTimeout(() => { - _this._retListener.remove(listener); - listener.reject("timeout"); - }, 1500); - this._retListener.push(listener); - - this._socket_connected.send(this.commandiefy({ - "type": "command", - "command": command, - "data": _data, - "flags": options.flagset.filter(entry => entry.length != 0) - })); - }); - - return this._command_handler_default.proxy_command_promise(result, options); - } - - connected() : boolean { - return !!this._socket_connected && this._socket_connected.readyState == WebSocket.OPEN; - } - - support_voice(): boolean { - return this._voice_connection !== undefined; - } - - voice_connection(): connection.voice.AbstractVoiceConnection | undefined { - return this._voice_connection; - } - - command_handler_boss(): connection.AbstractCommandHandlerBoss { - return this._command_boss; - } - - - get onconnectionstatechanged() : connection.ConnectionStateListener { - return this._connection_state_listener; - } - set onconnectionstatechanged(listener: connection.ConnectionStateListener) { - this._connection_state_listener = listener; - } - - handshake_handler(): connection.HandshakeHandler { - return this._handshakeHandler; - } - - remote_address(): ServerAddress { - return this._remote_address; - } - - private do_ping() { - if(this._ping.last_request + this._ping.timeout < Date.now()) { - this._ping.value = this._ping.timeout; - this._ping.last_response = this._ping.last_request + 1; + if(connected_socket == local_proxy_socket) { + log.debug(LogCategory.NETWORKING, tr("Established a TCP connection to %s via proxy to %s"), address.host + ":" + address.port, proxy_host); + this._remote_address.host = proxy_host; + } else { + log.debug(LogCategory.NETWORKING, tr("Established a TCP connection to %s directly"), address.host + ":" + address.port); } - if(this._ping.last_response > this._ping.last_request) { - this._ping.last_request = 'now' in performance ? performance.now() : Date.now(); - this.sendData(JSON.stringify({ - type: 'ping', - payload: (++this._ping.request_id).toString() - })); - } - } - ping(): { native: number; javascript?: number } { - return { - javascript: this._ping.value, - native: this._ping.value_native + this._socket_connected = connected_socket; + this._socket_connected.onclose = event => { + if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ + + this.client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, { + code: event.code, + reason: event.reason, + event: event + }); }; + + this._socket_connected.onerror = e => { + if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ + + log.warn(LogCategory.NETWORKING, tr("Received web socket error: (%o)"), e); + }; + + this._socket_connected.onmessage = msg => { + if(this._socket_connected != connected_socket) return; /* this socket isn't from interest anymore */ + + this.handle_socket_message(msg.data); + }; + + this._connected = true; + this.start_handshake(); + } catch (error) { + error_cleanup(); + if(this._socket_connected != connected_socket && this._connect_timeout_timer != local_timeout_timer) + return; /* we're not from interest anymore */ + + log.warn(LogCategory.NETWORKING, tr("Received unexpected error while connecting: %o"), error); + try { + await this.disconnect(); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to cleanup connection after unsuccessful connect attempt: %o"), error); + } + this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE, error); } } - export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection { - return new ServerConnection(handle); /* will be overridden by the client */ + private start_handshake() { + this.updateConnectionState(ConnectionState.INITIALISING); + this.client.log.log(elog.Type.CONNECTION_LOGIN, {}); + this._handshakeHandler.initialize(); + this._handshakeHandler.startHandshake(); } - export function destroy_server_connection(handle: AbstractServerConnection) { - if(!(handle instanceof ServerConnection)) - throw "invalid handle"; - handle.destroy(); + updateConnectionState(state: ConnectionState) { + const old_state = this._connectionState; + this._connectionState = state; + if(this._connection_state_listener) + this._connection_state_listener(old_state, state); } + + async disconnect(reason?: string) : Promise { + clearTimeout(this._connect_timeout_timer); + this._connect_timeout_timer = undefined; + + clearTimeout(this._ping.thread_id); + this._ping.thread_id = undefined; + + if(typeof(reason) === "string") { + //TODO send disconnect reason + } + + + if(this._connectionState != ConnectionState.UNCONNECTED) + this.updateConnectionState(ConnectionState.UNCONNECTED); + + if(this._voice_connection) + this._voice_connection.drop_rtp_session(); + + + if(this._socket_connected) { + this._socket_connected.close(3000 + 0xFF, tr("request disconnect")); + this._socket_connected = undefined; + } + + + for(let future of this._retListener) + future.reject(tr("Connection closed")); + this._retListener = []; + + this._connected = false; + this._retCodeIdx = 0; + } + + private handle_socket_message(data) { + if(typeof(data) === "string") { + let json; + try { + json = JSON.parse(data); + } catch(e) { + log.warn(LogCategory.NETWORKING, tr("Could not parse message json!")); + alert(e); // error in the above string (in this case, yes)! + return; + } + if(json["type"] === undefined) { + log.warn(LogCategory.NETWORKING, tr("Missing data type in message!")); + return; + } + if(json["type"] === "command") { + let group = log.group(log.LogType.DEBUG, LogCategory.NETWORKING, tr("Handling command '%s'"), json["command"]); + group.log(tr("Handling command '%s'"), json["command"]); + group.group(log.LogType.TRACE, tr("Json:")).collapsed(true).log("%o", json).end(); + + this._command_boss.invoke_handle({ + command: json["command"], + arguments: json["data"] + }); + + if(json["command"] === "initserver") { + this._ping.thread_id = setInterval(() => this.do_ping(), this._ping.interval) as any; + this.do_ping(); + this.updateConnectionState(ConnectionState.CONNECTED); + if(this._voice_connection) + this._voice_connection.start_rtc_session(); /* FIXME: Move it to a handler boss and not here! */ + } + group.end(); + } else if(json["type"] === "WebRTC") { + if(this._voice_connection) + this._voice_connection.handleControlPacket(json); + else + log.warn(LogCategory.NETWORKING, tr("Dropping WebRTC command packet, because we haven't a bridge.")) + } else if(json["type"] === "ping") { + this.sendData(JSON.stringify({ + type: 'pong', + payload: json["payload"] + })); + } else if(json["type"] === "pong") { + const id = parseInt(json["payload"]); + if(id != this._ping.request_id) { + log.warn(LogCategory.NETWORKING, tr("Received pong which is older than the last request. Delay may over %oms? (Index: %o, Current index: %o)"), this._ping.timeout, id, this._ping.request_id); + } else { + this._ping.last_response = 'now' in performance ? performance.now() : Date.now(); + this._ping.value = this._ping.last_response - this._ping.last_request; + this._ping.value_native = parseInt(json["ping_native"]) / 1000; /* we're getting it in microseconds and not milliseconds */ + log.debug(LogCategory.NETWORKING, tr("Received new pong. Updating ping to: JS: %o Native: %o"), this._ping.value.toFixed(3), this._ping.value_native.toFixed(3)); + } + } else { + log.warn(LogCategory.NETWORKING, tr("Unknown command type %o"), json["type"]); + } + } else { + log.warn(LogCategory.NETWORKING, tr("Received unknown message of type %s. Dropping message"), typeof(data)); + } + } + + sendData(data: any) { + if(!this._socket_connected || this._socket_connected.readyState != 1) { + log.warn(LogCategory.NETWORKING, tr("Tried to send on a invalid socket (%s)"), this._socket_connected ? "invalid state (" + this._socket_connected.readyState + ")" : "invalid socket"); + return; + } + this._socket_connected.send(data); + } + + private commandiefy(input: any) : string { + return JSON.stringify(input, (key, value) => { + switch (typeof value) { + case "boolean": return value == true ? "1" : "0"; + case "function": return value(); + default: + return value; + } + }); + + } + + send_command(command: string, data?: any | any[], _options?: CommandOptions) : Promise { + if(!this._socket_connected || !this.connected()) { + log.warn(LogCategory.NETWORKING, tr("Tried to send a command without a valid connection.")); + return Promise.reject(tr("not connected")); + } + + const options: CommandOptions = {}; + Object.assign(options, CommandOptionDefaults); + Object.assign(options, _options); + + data = $.isArray(data) ? data : [data || {}]; + if(data.length == 0) /* we require min one arg to append return_code */ + data.push({}); + + const _this = this; + let result = new Promise((resolve, failed) => { + let _data = $.isArray(data) ? data : [data]; + let retCode = _data[0]["return_code"] !== undefined ? _data[0].return_code : _this.generateReturnCode(); + _data[0].return_code = retCode; + + let listener = new ReturnListener(); + listener.resolve = resolve; + listener.reject = failed; + listener.code = retCode; + listener.timeout = setTimeout(() => { + _this._retListener.remove(listener); + listener.reject("timeout"); + }, 1500); + this._retListener.push(listener); + + this._socket_connected.send(this.commandiefy({ + "type": "command", + "command": command, + "data": _data, + "flags": options.flagset.filter(entry => entry.length != 0) + })); + }); + + return this._command_handler_default.proxy_command_promise(result, options); + } + + connected() : boolean { + return !!this._socket_connected && this._socket_connected.readyState == WebSocket.OPEN; + } + + support_voice(): boolean { + return this._voice_connection !== undefined; + } + + voice_connection(): AbstractVoiceConnection | undefined { + return this._voice_connection; + } + + command_handler_boss(): AbstractCommandHandlerBoss { + return this._command_boss; + } + + + get onconnectionstatechanged() : ConnectionStateListener { + return this._connection_state_listener; + } + set onconnectionstatechanged(listener: ConnectionStateListener) { + this._connection_state_listener = listener; + } + + handshake_handler(): HandshakeHandler { + return this._handshakeHandler; + } + + remote_address(): ServerAddress { + return this._remote_address; + } + + private do_ping() { + if(this._ping.last_request + this._ping.timeout < Date.now()) { + this._ping.value = this._ping.timeout; + this._ping.last_response = this._ping.last_request + 1; + } + if(this._ping.last_response > this._ping.last_request) { + this._ping.last_request = 'now' in performance ? performance.now() : Date.now(); + this.sendData(JSON.stringify({ + type: 'ping', + payload: (++this._ping.request_id).toString() + })); + } + } + + ping(): { native: number; javascript?: number } { + return { + javascript: this._ping.value, + native: this._ping.value_native + }; + } +} + +export function spawn_server_connection(handle: ConnectionHandler) : AbstractServerConnection { + return new ServerConnection(handle); /* will be overridden by the client */ +} + +export function destroy_server_connection(handle: AbstractServerConnection) { + if(!(handle instanceof ServerConnection)) + throw "invalid handle"; + handle.destroy(); } \ No newline at end of file diff --git a/web/js/dns.ts b/web/js/dns.ts new file mode 100644 index 00000000..d6412d0b --- /dev/null +++ b/web/js/dns.ts @@ -0,0 +1,481 @@ +import {ServerAddress} from "tc-shared/ui/server"; +import {AddressTarget, default_options, ResolveOptions} from "tc-shared/dns"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; + +export enum RRType { + A = 1, // a host address,[RFC1035], + NS = 2, // an authoritative name server,[RFC1035], + MD = 3, // a mail destination (OBSOLETE - use MX),[RFC1035], + MF = 4, // a mail forwarder (OBSOLETE - use MX),[RFC1035], + CNAME = 5, // the canonical name for an alias,[RFC1035], + SOA = 6, // marks the start of a zone of authority,[RFC1035], + MB = 7, // a mailbox domain name (EXPERIMENTAL),[RFC1035], + MG = 8, // a mail group member (EXPERIMENTAL),[RFC1035], + MR = 9, // a mail rename domain name (EXPERIMENTAL),[RFC1035], + NULL_ = 10, // a null RR (EXPERIMENTAL),[RFC1035], + WKS = 11, // a well known service description,[RFC1035], + PTR = 12, // a domain name pointer,[RFC1035], + HINFO = 13, // host information,[RFC1035], + MINFO = 14, // mailbox or mail list information,[RFC1035], + MX = 15, // mail exchange,[RFC1035], + TXT = 16, // text strings,[RFC1035], + RP = 17, // for Responsible Person,[RFC1183], + AFSDB = 18, // for AFS Data Base location,[RFC1183][RFC5864], + X25 = 19, // for X.25 PSDN address,[RFC1183], + ISDN = 20, // for ISDN address,[RFC1183], + RT = 21, // for Route Through,[RFC1183], + NSAP = 22, // "for NSAP address, NSAP style A record",[RFC1706], + NSAP_PTR = 23, // "for domain name pointer, NSAP style",[RFC1348][RFC1637][RFC1706], + SIG = 24, // for security signature,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2931][RFC3110][RFC3008], + KEY = 25, // for security key,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110], + PX = 26, // X.400 mail mapping information,[RFC2163], + GPOS = 27, // Geographical Position,[RFC1712], + AAAA = 28, // IP6 Address,[RFC3596], + LOC = 29, // Location Information,[RFC1876], + NXT = 30, // Next Domain (OBSOLETE),[RFC3755][RFC2535], + EID = 31, // Endpoint Identifier,[Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt], + NIMLOC = 32, // Nimrod Locator,[1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt], + SRV = 33, // Server Selection,[1][RFC2782], + ATMA = 34, // ATM Address,"[ ATM Forum Technical Committee, ""ATM Name System, V2.0"", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]", + NAPTR = 35, // Naming Authority Pointer,[RFC2915][RFC2168][RFC3403], + KX = 36, // Key Exchanger,[RFC2230], + CERT = 37, //CERT, // [RFC4398], + A6 = 38, // A6 (OBSOLETE - use AAAA),[RFC3226][RFC2874][RFC6563], + DNAME = 39, //DNAME, // [RFC6672], + SINK = 40, //SINK, // [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink], + OPT = 41, //OPT, // [RFC6891][RFC3225], + APL = 42, //APL, // [RFC3123], + DS = 43, // Delegation Signer,[RFC4034][RFC3658], + SSHFP = 44, // SSH Key Fingerprint,[RFC4255], + IPSECKEY = 45, //IPSECKEY, // [RFC4025], + RRSIG = 46, //RRSIG, // [RFC4034][RFC3755], + NSEC = 47, //NSEC, // [RFC4034][RFC3755], + DNSKEY = 48, //DNSKEY, // [RFC4034][RFC3755], + DHCID = 49, //DHCID, // [RFC4701], + NSEC3 = 50, //NSEC3, // [RFC5155], + NSEC3PARAM = 51, //NSEC3PARAM, // [RFC5155], + TLSA = 52, //TLSA, // [RFC6698], + SMIMEA = 53, // S/MIME cert association,[RFC8162],SMIMEA/smimea-completed-template + Unassigned = 54, // , + HIP = 55, // Host Identity Protocol,[RFC8005], + NINFO = 56, //NINFO [Jim_Reid], // NINFO/ninfo-completed-template + RKEY = 57, //RKEY [Jim_Reid], // RKEY/rkey-completed-template + TALINK = 58, // Trust Anchor LINK,[Wouter_Wijngaards],TALINK/talink-completed-template + CDS = 59, // Child DS,[RFC7344],CDS/cds-completed-template + CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS,[RFC7344], + OPENPGPKEY = 61, // OpenPGP Key,[RFC7929],OPENPGPKEY/openpgpkey-completed-template + CSYNC = 62, // Child-To-Parent Synchronization,[RFC7477], + ZONEMD = 63, // message digest for DNS zone,[draft-wessels-dns-zone-digest],ZONEMD/zonemd-completed-template + //Unassigned = 64-98, + SPF = 99, // [RFC7208], + UINFO = 100, // [IANA-Reserved], + UID = 101, // [IANA-Reserved], + GID = 102, // [IANA-Reserved], + UNSPEC = 103, // [IANA-Reserved], + NID = 104, //[RFC6742], // ILNP/nid-completed-template + L32 = 105, //[RFC6742], // ILNP/l32-completed-template + L64 = 106, //[RFC6742], // ILNP/l64-completed-template + LP = 107, //[RFC6742], // ILNP/lp-completed-template + EUI48 = 108, // an EUI-48 address,[RFC7043],EUI48/eui48-completed-template + EUI64 = 109, // an EUI-64 address,[RFC7043],EUI64/eui64-completed-template + //Unassigned = 110-248, // , + TKEY = 249, // Transaction Key,[RFC2930], + TSIG = 250, // Transaction Signature,[RFC2845], + IXFR = 251, // incremental transfer,[RFC1995], + AXFR = 252, // transfer of an entire zone,[RFC1035][RFC5936], + MAILB = 253, // "mailbox-related RRs (MB, MG or MR)",[RFC1035], + MAILA = 254, // mail agent RRs (OBSOLETE - see MX),[RFC1035], + ANY = 255, // A request for some or all records the server has available,[RFC1035][RFC6895][RFC8482], + URI = 256, //URI [RFC7553], // URI/uri-completed-template + CAA = 257, // Certification Authority Restriction,[RFC-ietf-lamps-rfc6844bis-07],CAA/caa-completed-template + AVC = 258, // Application Visibility and Control,[Wolfgang_Riedel],AVC/avc-completed-template + DOA = 259, // Digital Object Architecture,[draft-durand-doa-over-dns],DOA/doa-completed-template + AMTRELAY = 260, // Automatic Multicast Tunneling Relay,[draft-ietf-mboned-driad-amt-discovery],AMTRELAY/amtrelay-completed-template + //Unassigned = 261-32767, + TA = 32768, // DNSSEC Trust Authorities,"[Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, + // Information Networking Institute, Carnegie Mellon University, April 2004.]", + DLV = 32769, // DNSSEC Lookaside Validation,[RFC4431], + //Unassigned = 32770-65279,, // , + //Private use,65280-65534,,,, + Reserved = 65535, +} +export enum ErrorCode { + NOERROR = 0, + FORMERR = 1, + SERVFAIL = 2, + NXDOMAIN = 3, + NOTIMP = 4, + REFUSED = 5, + YXDOMAIN = 6, + XRRSET = 7, + NOTAUTH = 8, + NOTZONE = 9 +} + +interface DNSAnswer { + name: string; + type: RRType; + TTL: null; + data: string; +} + +interface DNSQuery { + name: string; + type: RRType; +} + +interface DNSResponse { + Status: ErrorCode; + Comment: string; + + TC: boolean; /* truncated */ + RD: true; + RA: true; + AD: boolean; /* DNSSEC valid */ + CD: boolean; /* client DNSSEC disabled */ + + Question: DNSQuery[]; + Answer?: DNSAnswer[]; + Authority?: DNSAnswer[]; + Additional: any[]; +} + +export async function resolve(address: string, type: RRType) : Promise { + const parameters = {}; + parameters["name"] = address; + parameters["type"] = type; + parameters["cd"] = false; /* check disabled */ + parameters["do"] = true; /* DNSSEC info */ + + const parameter_string = Object.keys(parameters).reduceRight((a, b) => a + "&" + b + "=" + encodeURIComponent(parameters[b])); + const response = await fetch("https://dns.google/resolve?" + parameter_string, { + method: "GET" + }); + if(response.status !== 200) + throw response.statusText || tr("server returned ") + response.status; + + let response_string = "unknown"; + let response_data: DNSResponse; + try { + response_string = await response.text(); + response_data = JSON.parse(response_string); + } catch(ex) { + log.error(LogCategory.DNS, tr("Failed to parse response data: %o. Data: %s"), ex, response_string); + throw "failed to parse response"; + } + + if(response_data.TC) + throw "truncated response"; + + if(response_data.Status !== ErrorCode.NOERROR) { + if(response_data.Status === ErrorCode.NXDOMAIN) + return []; + throw "dns error code " + response_data.Status; + } + + log.trace(LogCategory.DNS, tr("Result for query %s (%s): %o"), address, RRType[type], response_data); + + if(!response_data.Answer) return []; + return response_data.Answer.filter(e => (e.name === address || e.name === address + ".") && e.type === type); +} + +type Address = { host: string, port: number }; + +interface DNSResolveMethod { + name() : string; + resolve(address: Address) : Promise
; +} + +class IPResolveMethod implements DNSResolveMethod { + readonly v6: boolean; + + constructor(v6: boolean) { + this.v6 = v6; + } + + + name(): string { + return "ip v" + (this.v6 ? "6" : "4") + " resolver"; + } + + resolve(address: Address): Promise
{ + return resolve(address.host, this.v6 ? RRType.AAAA : RRType.A).then(e => { + if(!e.length) return undefined; + + return { + host: e[0].data, + port: address.port + } + }); + } +} + +type ParsedSVRRecord = { + target: string; + port: number; + + priority: number; + weight: number; +} +class SRVResolveMethod implements DNSResolveMethod { + readonly application: string; + + constructor(app: string) { + this.application = app; + } + + name(): string { + return "srv resolve [" + this.application + "]"; + } + + resolve(address: Address): Promise
{ + return resolve((this.application ? this.application + "." : "") + address.host, RRType.SRV).then(e => { + if(!e) return undefined; + + const records: {[key: number]:ParsedSVRRecord[]} = {}; + for(const record of e) { + const parts = record.data.split(" "); + if(parts.length !== 4) { + log.warn(LogCategory.DNS, tr("Failed to parse SRV record %s. Invalid split length."), record); + continue; + } + + const priority = parseInt(parts[0]); + const weight = parseInt(parts[1]); + const port = parseInt(parts[2]); + + if((priority < 0 || priority > 65535) || (weight < 0 || weight > 65535) || (port < 0 || port > 65535)) { + log.warn(LogCategory.DNS, tr("Failed to parse SRV record %s. Malformed data."), record); + continue; + } + + (records[priority] || (records[priority] = [])).push({ + priority: priority, + weight: weight, + port: port, + target: parts[3] + }); + } + + /* get the record with the highest priority */ + const priority_strings = Object.keys(records); + if(!priority_strings.length) return undefined; + + let highest_priority: ParsedSVRRecord[]; + for(const priority_str of priority_strings) { + if(!highest_priority || !highest_priority.length) + highest_priority = records[priority_str]; + + if(highest_priority[0].priority < parseInt(priority_str)) + highest_priority = records[priority_str]; + } + + if(!highest_priority.length) return undefined; + + /* select randomly one record */ + let record: ParsedSVRRecord; + const max_weight = highest_priority.map(e => e.weight).reduce((a, b) => a + b, 0); + if(max_weight == 0) record = highest_priority[Math.floor(Math.random() * highest_priority.length)]; + else { + let rnd = Math.random() * max_weight; + for(let i = 0; i < highest_priority.length; i++) { + rnd -= highest_priority[i].weight; + if(rnd > 0) continue; + + record = highest_priority[i]; + break; + } + } + if(!record) /* shall never happen */ + record = highest_priority[0]; + return { + host: record.target, + port: record.port == 0 ? address.port : record.port + }; + }); + } +} + +class SRV_IPResolveMethod implements DNSResolveMethod { + readonly srv_resolver: DNSResolveMethod; + readonly ipv4_resolver: IPResolveMethod; + readonly ipv6_resolver: IPResolveMethod; + + constructor(srv_resolver: DNSResolveMethod, ipv4_resolver: IPResolveMethod, ipv6_resolver: IPResolveMethod) { + this.srv_resolver = srv_resolver; + this.ipv4_resolver = ipv4_resolver; + this.ipv6_resolver = ipv6_resolver; + } + + name(): string { + return "srv ip resolver [" + this.srv_resolver.name() + "; " + this.ipv4_resolver.name() + "; " + this.ipv6_resolver.name() + "]"; + } + + resolve(address: Address): Promise
{ + return this.srv_resolver.resolve(address).then(e => { + if(!e) return undefined; + + return this.ipv4_resolver.resolve(e).catch(() => this.ipv6_resolver.resolve(e)); + }); + } +} + +class DomainRootResolveMethod implements DNSResolveMethod { + readonly resolver: DNSResolveMethod; + + constructor(resolver: DNSResolveMethod) { + this.resolver = resolver; + } + + name(): string { + return "domain-root [" + this.resolver.name() + "]"; + } + + resolve(address: Address): Promise
{ + const parts = address.host.split("."); + if(parts.length < 3) return undefined; + + return this.resolver.resolve({ + host: parts.slice(-2).join("."), + port: address.port + }); + } +} + +class TeaSpeakDNSResolve { + readonly address: Address; + private resolvers: {[key: string]:{resolver: DNSResolveMethod, after: string[]}} = {}; + private resolving = false; + private timeout; + + private callback_success; + private callback_fail; + + private finished_resolvers: string[]; + private resolving_resolvers: string[]; + + constructor(addr: Address) { + this.address = addr; + } + + register_resolver(resolver: DNSResolveMethod, ...after: (string | DNSResolveMethod)[]) { + if(this.resolving) throw tr("resolver is already resolving"); + + this.resolvers[resolver.name()] = { resolver: resolver, after: after.map(e => typeof e === "string" ? e : e.name()) }; + } + + resolve(timeout: number) : Promise
{ + if(this.resolving) throw tr("already resolving"); + this.resolving = true; + + this.finished_resolvers = []; + this.resolving_resolvers = []; + + const cleanup = () => { + clearTimeout(this.timeout); + this.resolving = false; + }; + + this.timeout = setTimeout(() => { + this.callback_fail(tr("timeout")); + }, timeout); + log.trace(LogCategory.DNS, tr("Start resolving %s:%d"), this.address.host, this.address.port); + + return new Promise
((resolve, reject) => { + this.callback_success = data => { + cleanup(); + resolve(data); + }; + + this.callback_fail = error => { + cleanup(); + reject(error); + }; + + this.invoke_resolvers(); + }); + } + + private invoke_resolvers() { + let invoke_count = 0; + + _main_loop: + for(const resolver_name of Object.keys(this.resolvers)) { + if(this.resolving_resolvers.findIndex(e => e === resolver_name) !== -1) continue; + if(this.finished_resolvers.findIndex(e => e === resolver_name) !== -1) continue; + + const resolver = this.resolvers[resolver_name]; + for(const after of resolver.after) + if(this.finished_resolvers.findIndex(e => e === after) === -1) continue _main_loop; + + invoke_count++; + log.trace(LogCategory.DNS, tr(" Executing resolver %s"), resolver_name); + + this.resolving_resolvers.push(resolver_name); + resolver.resolver.resolve(this.address).then(result => { + if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */ + this.finished_resolvers.push(resolver_name); + + if(!result) { + log.trace(LogCategory.DNS, tr(" Resolver %s returned an empty response."), resolver_name); + this.invoke_resolvers(); + return; + } + + log.trace(LogCategory.DNS, tr(" Successfully resolved address %s:%d to %s:%d via resolver %s"), + this.address.host, this.address.port, + result.host, result.port, + resolver_name); + this.callback_success(result); + }).catch(error => { + if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */ + this.finished_resolvers.push(resolver_name); + + log.trace(LogCategory.DNS, tr(" Resolver %s ran into an error: %o"), resolver_name, error); + this.invoke_resolvers(); + }).then(() => { + this.resolving_resolvers.remove(resolver_name); + if(!this.resolving_resolvers.length && this.resolving) + this.invoke_resolvers(); + }); + } + + if(invoke_count === 0 && !this.resolving_resolvers.length && this.resolving) + this.callback_fail("no response"); + } +} + +const resolver_ip_v4 = new IPResolveMethod(false); +const resolver_ip_v6 = new IPResolveMethod(true); + +const resolver_srv_ts = new SRV_IPResolveMethod(new SRVResolveMethod("_ts._udp"), resolver_ip_v4, resolver_ip_v6); +const resolver_srv_ts3 = new SRV_IPResolveMethod(new SRVResolveMethod("_ts3._udp"), resolver_ip_v4, resolver_ip_v6); + +const resolver_dr_srv_ts = new DomainRootResolveMethod(resolver_srv_ts); +const resolver_dr_srv_ts3 = new DomainRootResolveMethod(resolver_srv_ts3); + +export function supported() { return true; } + +export async function resolve_address(address: ServerAddress, _options?: ResolveOptions) : Promise { + const options = Object.assign({}, default_options); + Object.assign(options, _options); + + const resolver = new TeaSpeakDNSResolve(address); + + resolver.register_resolver(resolver_srv_ts); + resolver.register_resolver(resolver_srv_ts3); + //TODO: TSDNS somehow? + + resolver.register_resolver(resolver_dr_srv_ts, resolver_srv_ts); + resolver.register_resolver(resolver_dr_srv_ts3, resolver_srv_ts3); + + resolver.register_resolver(resolver_ip_v4, resolver_srv_ts, resolver_srv_ts3); + resolver.register_resolver(resolver_ip_v6, resolver_ip_v4); + + const response = await resolver.resolve(options.timeout || 5000); + return { + target_ip: response.host, + target_port: response.port + }; +} \ No newline at end of file diff --git a/web/js/dns_impl.ts b/web/js/dns_impl.ts deleted file mode 100644 index b2460161..00000000 --- a/web/js/dns_impl.ts +++ /dev/null @@ -1,478 +0,0 @@ -namespace dns { - export enum RRType { - A = 1, // a host address,[RFC1035], - NS = 2, // an authoritative name server,[RFC1035], - MD = 3, // a mail destination (OBSOLETE - use MX),[RFC1035], - MF = 4, // a mail forwarder (OBSOLETE - use MX),[RFC1035], - CNAME = 5, // the canonical name for an alias,[RFC1035], - SOA = 6, // marks the start of a zone of authority,[RFC1035], - MB = 7, // a mailbox domain name (EXPERIMENTAL),[RFC1035], - MG = 8, // a mail group member (EXPERIMENTAL),[RFC1035], - MR = 9, // a mail rename domain name (EXPERIMENTAL),[RFC1035], - NULL_ = 10, // a null RR (EXPERIMENTAL),[RFC1035], - WKS = 11, // a well known service description,[RFC1035], - PTR = 12, // a domain name pointer,[RFC1035], - HINFO = 13, // host information,[RFC1035], - MINFO = 14, // mailbox or mail list information,[RFC1035], - MX = 15, // mail exchange,[RFC1035], - TXT = 16, // text strings,[RFC1035], - RP = 17, // for Responsible Person,[RFC1183], - AFSDB = 18, // for AFS Data Base location,[RFC1183][RFC5864], - X25 = 19, // for X.25 PSDN address,[RFC1183], - ISDN = 20, // for ISDN address,[RFC1183], - RT = 21, // for Route Through,[RFC1183], - NSAP = 22, // "for NSAP address, NSAP style A record",[RFC1706], - NSAP_PTR = 23, // "for domain name pointer, NSAP style",[RFC1348][RFC1637][RFC1706], - SIG = 24, // for security signature,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2931][RFC3110][RFC3008], - KEY = 25, // for security key,[RFC4034][RFC3755][RFC2535][RFC2536][RFC2537][RFC2539][RFC3008][RFC3110], - PX = 26, // X.400 mail mapping information,[RFC2163], - GPOS = 27, // Geographical Position,[RFC1712], - AAAA = 28, // IP6 Address,[RFC3596], - LOC = 29, // Location Information,[RFC1876], - NXT = 30, // Next Domain (OBSOLETE),[RFC3755][RFC2535], - EID = 31, // Endpoint Identifier,[Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt], - NIMLOC = 32, // Nimrod Locator,[1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt], - SRV = 33, // Server Selection,[1][RFC2782], - ATMA = 34, // ATM Address,"[ ATM Forum Technical Committee, ""ATM Name System, V2.0"", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]", - NAPTR = 35, // Naming Authority Pointer,[RFC2915][RFC2168][RFC3403], - KX = 36, // Key Exchanger,[RFC2230], - CERT = 37, //CERT, // [RFC4398], - A6 = 38, // A6 (OBSOLETE - use AAAA),[RFC3226][RFC2874][RFC6563], - DNAME = 39, //DNAME, // [RFC6672], - SINK = 40, //SINK, // [Donald_E_Eastlake][http://tools.ietf.org/html/draft-eastlake-kitchen-sink], - OPT = 41, //OPT, // [RFC6891][RFC3225], - APL = 42, //APL, // [RFC3123], - DS = 43, // Delegation Signer,[RFC4034][RFC3658], - SSHFP = 44, // SSH Key Fingerprint,[RFC4255], - IPSECKEY = 45, //IPSECKEY, // [RFC4025], - RRSIG = 46, //RRSIG, // [RFC4034][RFC3755], - NSEC = 47, //NSEC, // [RFC4034][RFC3755], - DNSKEY = 48, //DNSKEY, // [RFC4034][RFC3755], - DHCID = 49, //DHCID, // [RFC4701], - NSEC3 = 50, //NSEC3, // [RFC5155], - NSEC3PARAM = 51, //NSEC3PARAM, // [RFC5155], - TLSA = 52, //TLSA, // [RFC6698], - SMIMEA = 53, // S/MIME cert association,[RFC8162],SMIMEA/smimea-completed-template - Unassigned = 54, // , - HIP = 55, // Host Identity Protocol,[RFC8005], - NINFO = 56, //NINFO [Jim_Reid], // NINFO/ninfo-completed-template - RKEY = 57, //RKEY [Jim_Reid], // RKEY/rkey-completed-template - TALINK = 58, // Trust Anchor LINK,[Wouter_Wijngaards],TALINK/talink-completed-template - CDS = 59, // Child DS,[RFC7344],CDS/cds-completed-template - CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS,[RFC7344], - OPENPGPKEY = 61, // OpenPGP Key,[RFC7929],OPENPGPKEY/openpgpkey-completed-template - CSYNC = 62, // Child-To-Parent Synchronization,[RFC7477], - ZONEMD = 63, // message digest for DNS zone,[draft-wessels-dns-zone-digest],ZONEMD/zonemd-completed-template - //Unassigned = 64-98, - SPF = 99, // [RFC7208], - UINFO = 100, // [IANA-Reserved], - UID = 101, // [IANA-Reserved], - GID = 102, // [IANA-Reserved], - UNSPEC = 103, // [IANA-Reserved], - NID = 104, //[RFC6742], // ILNP/nid-completed-template - L32 = 105, //[RFC6742], // ILNP/l32-completed-template - L64 = 106, //[RFC6742], // ILNP/l64-completed-template - LP = 107, //[RFC6742], // ILNP/lp-completed-template - EUI48 = 108, // an EUI-48 address,[RFC7043],EUI48/eui48-completed-template - EUI64 = 109, // an EUI-64 address,[RFC7043],EUI64/eui64-completed-template - //Unassigned = 110-248, // , - TKEY = 249, // Transaction Key,[RFC2930], - TSIG = 250, // Transaction Signature,[RFC2845], - IXFR = 251, // incremental transfer,[RFC1995], - AXFR = 252, // transfer of an entire zone,[RFC1035][RFC5936], - MAILB = 253, // "mailbox-related RRs (MB, MG or MR)",[RFC1035], - MAILA = 254, // mail agent RRs (OBSOLETE - see MX),[RFC1035], - ANY = 255, // A request for some or all records the server has available,[RFC1035][RFC6895][RFC8482], - URI = 256, //URI [RFC7553], // URI/uri-completed-template - CAA = 257, // Certification Authority Restriction,[RFC-ietf-lamps-rfc6844bis-07],CAA/caa-completed-template - AVC = 258, // Application Visibility and Control,[Wolfgang_Riedel],AVC/avc-completed-template - DOA = 259, // Digital Object Architecture,[draft-durand-doa-over-dns],DOA/doa-completed-template - AMTRELAY = 260, // Automatic Multicast Tunneling Relay,[draft-ietf-mboned-driad-amt-discovery],AMTRELAY/amtrelay-completed-template - //Unassigned = 261-32767, - TA = 32768, // DNSSEC Trust Authorities,"[Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, - // Information Networking Institute, Carnegie Mellon University, April 2004.]", - DLV = 32769, // DNSSEC Lookaside Validation,[RFC4431], - //Unassigned = 32770-65279,, // , - //Private use,65280-65534,,,, - Reserved = 65535, - } - export enum ErrorCode { - NOERROR = 0, - FORMERR = 1, - SERVFAIL = 2, - NXDOMAIN = 3, - NOTIMP = 4, - REFUSED = 5, - YXDOMAIN = 6, - XRRSET = 7, - NOTAUTH = 8, - NOTZONE = 9 - } - - interface DNSAnswer { - name: string; - type: RRType; - TTL: null; - data: string; - } - - interface DNSQuery { - name: string; - type: RRType; - } - - interface DNSResponse { - Status: ErrorCode; - Comment: string; - - TC: boolean; /* truncated */ - RD: true; - RA: true; - AD: boolean; /* DNSSEC valid */ - CD: boolean; /* client DNSSEC disabled */ - - Question: DNSQuery[]; - Answer?: DNSAnswer[]; - Authority?: DNSAnswer[]; - Additional: any[]; - } - - export async function resolve(address: string, type: RRType) : Promise { - const parameters = {}; - parameters["name"] = address; - parameters["type"] = type; - parameters["cd"] = false; /* check disabled */ - parameters["do"] = true; /* DNSSEC info */ - - const parameter_string = Object.keys(parameters).reduceRight((a, b) => a + "&" + b + "=" + encodeURIComponent(parameters[b])); - const response = await fetch("https://dns.google/resolve?" + parameter_string, { - method: "GET" - }); - if(response.status !== 200) - throw response.statusText || tr("server returned ") + response.status; - - let response_string = "unknown"; - let response_data: DNSResponse; - try { - response_string = await response.text(); - response_data = JSON.parse(response_string); - } catch(ex) { - log.error(LogCategory.DNS, tr("Failed to parse response data: %o. Data: %s"), ex, response_string); - throw "failed to parse response"; - } - - if(response_data.TC) - throw "truncated response"; - - if(response_data.Status !== ErrorCode.NOERROR) { - if(response_data.Status === ErrorCode.NXDOMAIN) - return []; - throw "dns error code " + response_data.Status; - } - - log.trace(LogCategory.DNS, tr("Result for query %s (%s): %o"), address, RRType[type], response_data); - - if(!response_data.Answer) return []; - return response_data.Answer.filter(e => (e.name === address || e.name === address + ".") && e.type === type); - } - - type Address = { host: string, port: number }; - - interface DNSResolveMethod { - name() : string; - resolve(address: Address) : Promise
; - } - - class IPResolveMethod implements DNSResolveMethod { - readonly v6: boolean; - - constructor(v6: boolean) { - this.v6 = v6; - } - - - name(): string { - return "ip v" + (this.v6 ? "6" : "4") + " resolver"; - } - - resolve(address: Address): Promise
{ - return resolve(address.host, this.v6 ? RRType.AAAA : RRType.A).then(e => { - if(!e.length) return undefined; - - return { - host: e[0].data, - port: address.port - } - }); - } - } - - type ParsedSVRRecord = { - target: string; - port: number; - - priority: number; - weight: number; - } - class SRVResolveMethod implements DNSResolveMethod { - readonly application: string; - - constructor(app: string) { - this.application = app; - } - - name(): string { - return "srv resolve [" + this.application + "]"; - } - - resolve(address: Address): Promise
{ - return resolve((this.application ? this.application + "." : "") + address.host, RRType.SRV).then(e => { - if(!e) return undefined; - - const records: {[key: number]:ParsedSVRRecord[]} = {}; - for(const record of e) { - const parts = record.data.split(" "); - if(parts.length !== 4) { - log.warn(LogCategory.DNS, tr("Failed to parse SRV record %s. Invalid split length."), record); - continue; - } - - const priority = parseInt(parts[0]); - const weight = parseInt(parts[1]); - const port = parseInt(parts[2]); - - if((priority < 0 || priority > 65535) || (weight < 0 || weight > 65535) || (port < 0 || port > 65535)) { - log.warn(LogCategory.DNS, tr("Failed to parse SRV record %s. Malformed data."), record); - continue; - } - - (records[priority] || (records[priority] = [])).push({ - priority: priority, - weight: weight, - port: port, - target: parts[3] - }); - } - - /* get the record with the highest priority */ - const priority_strings = Object.keys(records); - if(!priority_strings.length) return undefined; - - let highest_priority: ParsedSVRRecord[]; - for(const priority_str of priority_strings) { - if(!highest_priority || !highest_priority.length) - highest_priority = records[priority_str]; - - if(highest_priority[0].priority < parseInt(priority_str)) - highest_priority = records[priority_str]; - } - - if(!highest_priority.length) return undefined; - - /* select randomly one record */ - let record: ParsedSVRRecord; - const max_weight = highest_priority.map(e => e.weight).reduce((a, b) => a + b, 0); - if(max_weight == 0) record = highest_priority[Math.floor(Math.random() * highest_priority.length)]; - else { - let rnd = Math.random() * max_weight; - for(let i = 0; i < highest_priority.length; i++) { - rnd -= highest_priority[i].weight; - if(rnd > 0) continue; - - record = highest_priority[i]; - break; - } - } - if(!record) /* shall never happen */ - record = highest_priority[0]; - return { - host: record.target, - port: record.port == 0 ? address.port : record.port - }; - }); - } - } - - class SRV_IPResolveMethod implements DNSResolveMethod { - readonly srv_resolver: DNSResolveMethod; - readonly ipv4_resolver: IPResolveMethod; - readonly ipv6_resolver: IPResolveMethod; - - constructor(srv_resolver: DNSResolveMethod, ipv4_resolver: IPResolveMethod, ipv6_resolver: IPResolveMethod) { - this.srv_resolver = srv_resolver; - this.ipv4_resolver = ipv4_resolver; - this.ipv6_resolver = ipv6_resolver; - } - - name(): string { - return "srv ip resolver [" + this.srv_resolver.name() + "; " + this.ipv4_resolver.name() + "; " + this.ipv6_resolver.name() + "]"; - } - - resolve(address: Address): Promise
{ - return this.srv_resolver.resolve(address).then(e => { - if(!e) return undefined; - - return this.ipv4_resolver.resolve(e).catch(() => this.ipv6_resolver.resolve(e)); - }); - } - } - - class DomainRootResolveMethod implements DNSResolveMethod { - readonly resolver: DNSResolveMethod; - - constructor(resolver: DNSResolveMethod) { - this.resolver = resolver; - } - - name(): string { - return "domain-root [" + this.resolver.name() + "]"; - } - - resolve(address: Address): Promise
{ - const parts = address.host.split("."); - if(parts.length < 3) return undefined; - - return this.resolver.resolve({ - host: parts.slice(-2).join("."), - port: address.port - }); - } - } - - class TeaSpeakDNSResolve { - readonly address: Address; - private resolvers: {[key: string]:{resolver: DNSResolveMethod, after: string[]}} = {}; - private resolving = false; - private timeout; - - private callback_success; - private callback_fail; - - private finished_resolvers: string[]; - private resolving_resolvers: string[]; - - constructor(addr: Address) { - this.address = addr; - } - - register_resolver(resolver: DNSResolveMethod, ...after: (string | DNSResolveMethod)[]) { - if(this.resolving) throw tr("resolver is already resolving"); - - this.resolvers[resolver.name()] = { resolver: resolver, after: after.map(e => typeof e === "string" ? e : e.name()) }; - } - - resolve(timeout: number) : Promise
{ - if(this.resolving) throw tr("already resolving"); - this.resolving = true; - - this.finished_resolvers = []; - this.resolving_resolvers = []; - - const cleanup = () => { - clearTimeout(this.timeout); - this.resolving = false; - }; - - this.timeout = setTimeout(() => { - this.callback_fail(tr("timeout")); - }, timeout); - log.trace(LogCategory.DNS, tr("Start resolving %s:%d"), this.address.host, this.address.port); - - return new Promise
((resolve, reject) => { - this.callback_success = data => { - cleanup(); - resolve(data); - }; - - this.callback_fail = error => { - cleanup(); - reject(error); - }; - - this.invoke_resolvers(); - }); - } - - private invoke_resolvers() { - let invoke_count = 0; - - _main_loop: - for(const resolver_name of Object.keys(this.resolvers)) { - if(this.resolving_resolvers.findIndex(e => e === resolver_name) !== -1) continue; - if(this.finished_resolvers.findIndex(e => e === resolver_name) !== -1) continue; - - const resolver = this.resolvers[resolver_name]; - for(const after of resolver.after) - if(this.finished_resolvers.findIndex(e => e === after) === -1) continue _main_loop; - - invoke_count++; - log.trace(LogCategory.DNS, tr(" Executing resolver %s"), resolver_name); - - this.resolving_resolvers.push(resolver_name); - resolver.resolver.resolve(this.address).then(result => { - if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */ - this.finished_resolvers.push(resolver_name); - - if(!result) { - log.trace(LogCategory.DNS, tr(" Resolver %s returned an empty response."), resolver_name); - this.invoke_resolvers(); - return; - } - - log.trace(LogCategory.DNS, tr(" Successfully resolved address %s:%d to %s:%d via resolver %s"), - this.address.host, this.address.port, - result.host, result.port, - resolver_name); - this.callback_success(result); - }).catch(error => { - if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */ - this.finished_resolvers.push(resolver_name); - - log.trace(LogCategory.DNS, tr(" Resolver %s ran into an error: %o"), resolver_name, error); - this.invoke_resolvers(); - }).then(() => { - this.resolving_resolvers.remove(resolver_name); - if(!this.resolving_resolvers.length && this.resolving) - this.invoke_resolvers(); - }); - } - - if(invoke_count === 0 && !this.resolving_resolvers.length && this.resolving) - this.callback_fail("no response"); - } - } - - const resolver_ip_v4 = new IPResolveMethod(false); - const resolver_ip_v6 = new IPResolveMethod(true); - - const resolver_srv_ts = new SRV_IPResolveMethod(new SRVResolveMethod("_ts._udp"), resolver_ip_v4, resolver_ip_v6); - const resolver_srv_ts3 = new SRV_IPResolveMethod(new SRVResolveMethod("_ts3._udp"), resolver_ip_v4, resolver_ip_v6); - - const resolver_dr_srv_ts = new DomainRootResolveMethod(resolver_srv_ts); - const resolver_dr_srv_ts3 = new DomainRootResolveMethod(resolver_srv_ts3); - - export function supported() { return true; } - - export async function resolve_address(address: ServerAddress, _options?: ResolveOptions) : Promise { - const options = Object.assign({}, default_options); - Object.assign(options, _options); - - const resolver = new TeaSpeakDNSResolve(address); - - resolver.register_resolver(resolver_srv_ts); - resolver.register_resolver(resolver_srv_ts3); - //TODO: TSDNS somehow? - - resolver.register_resolver(resolver_dr_srv_ts, resolver_srv_ts); - resolver.register_resolver(resolver_dr_srv_ts3, resolver_srv_ts3); - - resolver.register_resolver(resolver_ip_v4, resolver_srv_ts, resolver_srv_ts3); - resolver.register_resolver(resolver_ip_v6, resolver_ip_v4); - - const response = await resolver.resolve(options.timeout || 5000); - return { - target_ip: response.host, - target_port: response.port - }; - } -} \ No newline at end of file diff --git a/web/js/index.ts b/web/js/index.ts index e69de29b..fa00a902 100644 --- a/web/js/index.ts +++ b/web/js/index.ts @@ -0,0 +1 @@ +const tc = require("tc-shared/main"); \ No newline at end of file diff --git a/web/js/ppt.ts b/web/js/ppt.ts new file mode 100644 index 00000000..6dd864e0 --- /dev/null +++ b/web/js/ppt.ts @@ -0,0 +1,151 @@ +import {EventType, KeyEvent, KeyHook, SpecialKey} from "tc-shared/PPTListener"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; + +interface WebKeyEvent extends KeyEvent { + canceled: boolean; +} + +let key_listener: ((_: KeyEvent) => any)[] = []; + +function listener_key(type: EventType, event: KeyboardEvent) { + const key_event = { + type: type, + + key: event.key, + key_code: event.code, + + key_ctrl: event.ctrlKey, + key_shift: event.shiftKey, + key_alt: event.altKey, + key_windows: event.metaKey, + + canceled: event.defaultPrevented + } as WebKeyEvent; + //console.debug("Trigger key event %o", key_event); + + for(const listener of key_listener) + listener(key_event); + + if(key_event.canceled) + event.preventDefault(); +} + +const proxy_key_press = event => listener_key(EventType.KEY_PRESS, event); +const proxy_key_release = event => listener_key(EventType.KEY_RELEASE, event); +const proxy_key_typed = event => listener_key(EventType.KEY_TYPED, event); + +export function initialize() : Promise { + document.addEventListener('keypress', proxy_key_typed); + document.addEventListener('keydown', proxy_key_press); + document.addEventListener('keyup', proxy_key_release); + window.addEventListener('blur', listener_blur); + + register_key_listener(listener_hook); + return Promise.resolve(); +} + +export function finalize() { + document.removeEventListener("keypress", proxy_key_typed); + document.removeEventListener("keydown", proxy_key_press); + document.removeEventListener("keyup", proxy_key_release); + window.removeEventListener('blur', listener_blur); + + unregister_key_listener(listener_hook); +} + +export function register_key_listener(listener: (_: KeyEvent) => any) { + key_listener.push(listener); +} + +export function unregister_key_listener(listener: (_: KeyEvent) => any) { + key_listener.remove(listener); +} + + +let key_hooks: KeyHook[] = []; + +interface CurrentState { + keys: {[code: string]:KeyEvent}; + special: { [key:number]:boolean }; +} +let current_state: CurrentState = { + special: [] +} as any; + +let key_hooks_active: KeyHook[] = []; + +function listener_blur() { + current_state.special[SpecialKey.ALT] = false; + current_state.special[SpecialKey.CTRL] = false; + current_state.special[SpecialKey.SHIFT] = false; + current_state.special[SpecialKey.WINDOWS] = false; + + for(const code of Object.keys(current_state)) + if(code !== "special") + delete current_state[code]; + + for(const hook of key_hooks_active) + hook.callback_release(); + key_hooks_active = []; +} + +function listener_hook(event: KeyEvent) { + if(event.type == EventType.KEY_TYPED) + return; + + let old_hooks = [...key_hooks_active]; + let new_hooks = []; + + current_state.special[SpecialKey.ALT] = event.key_alt; + current_state.special[SpecialKey.CTRL] = event.key_ctrl; + current_state.special[SpecialKey.SHIFT] = event.key_shift; + current_state.special[SpecialKey.WINDOWS] = event.key_windows; + + current_state[event.key_code] = undefined; + if(event.type == EventType.KEY_PRESS) { + current_state[event.key_code] = event; + + for(const hook of key_hooks) { + if(hook.key_code !== event.key_code) continue; + if(hook.key_alt != event.key_alt) continue; + if(hook.key_ctrl != event.key_ctrl) continue; + if(hook.key_shift != event.key_shift) continue; + if(hook.key_windows != event.key_windows) continue; + + new_hooks.push(hook); + if(!old_hooks.remove(hook) && hook.callback_press) { + hook.callback_press(); + log.trace(LogCategory.GENERAL, tr("Trigger key press for %o!"), hook); + } + } + } + + //We have a new situation + for(const hook of old_hooks) { + //Do not test for meta key states because they could differ in a key release event + if(hook.key_code === event.key_code) { + if(hook.callback_release) { + hook.callback_release(); + log.trace(LogCategory.GENERAL, tr("Trigger key release for %o!"), hook); + } + } else { + new_hooks.push(hook); + } + } + key_hooks_active = new_hooks; +} + +export function register_key_hook(hook: KeyHook) { + key_hooks.push(hook); +} + +export function unregister_key_hook(hook: KeyHook) { + key_hooks.remove(hook); +} + +export function key_pressed(code: string | SpecialKey) : boolean { + if(typeof(code) === 'string') + return typeof current_state[code] !== "undefined"; + return current_state.special[code]; +} \ No newline at end of file diff --git a/web/js/voice/AudioResampler.ts b/web/js/voice/AudioResampler.ts index 1092eea1..5d833300 100644 --- a/web/js/voice/AudioResampler.ts +++ b/web/js/voice/AudioResampler.ts @@ -1,4 +1,7 @@ -class AudioResampler { +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; + +export class AudioResampler { targetSampleRate: number; private _use_promise: boolean; diff --git a/web/js/voice/JavascriptRecorder.ts b/web/js/voice/JavascriptRecorder.ts deleted file mode 100644 index 57121118..00000000 --- a/web/js/voice/JavascriptRecorder.ts +++ /dev/null @@ -1,852 +0,0 @@ -/// - -interface MediaStream { - stop(); -} - -namespace audio { - export namespace recorder { - let _queried_devices: JavascriptInputDevice[]; - let _queried_permissioned: boolean = false; - - export interface JavascriptInputDevice extends InputDevice { - device_id: string; - group_id: string; - } - - async function query_devices() { - const general_supported = !!getUserMediaFunctionPromise(); - - try { - const context = player.context(); - const devices = await navigator.mediaDevices.enumerateDevices(); - - _queried_permissioned = false; - if(devices.filter(e => !!e.label).length > 0) - _queried_permissioned = true; - - _queried_devices = devices.filter(e => e.kind === "audioinput").map((e: MediaDeviceInfo): JavascriptInputDevice => { - return { - channels: context ? context.destination.channelCount : 2, - sample_rate: context ? context.sampleRate : 44100, - - default_input: e.deviceId == "default", - - driver: "WebAudio", - name: e.label || "device-id{" + e.deviceId+ "}", - - supported: general_supported, - - device_id: e.deviceId, - group_id: e.groupId, - - unique_id: e.deviceId - } - }); - if(_queried_devices.length > 0 && _queried_devices.filter(e => e.default_input).length == 0) - _queried_devices[0].default_input = true; - } catch(error) { - log.error(LogCategory.AUDIO, tr("Failed to query microphone devices (%o)"), error); - _queried_devices = []; - } - } - - export function devices() : InputDevice[] { - if(typeof(_queried_devices) === "undefined") - query_devices(); - - return _queried_devices || []; - } - - - export function device_refresh_available() : boolean { return true; } - export function refresh_devices() : Promise { return query_devices(); } - - export function create_input() : AbstractInput { return new JavascriptInput(); } - - export async function create_levelmeter(device: InputDevice) : Promise { - const meter = new JavascriptLevelmeter(device as any); - await meter.initialize(); - return meter; - } - - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - function: async () => { query_devices(); }, /* May wait for it? */ - priority: 10, - name: "query media devices" - }); - - export namespace filter { - export abstract class JAbstractFilter implements Filter { - type; - - source_node: AudioNode; - audio_node: NodeType; - - context: AudioContext; - enabled: boolean = false; - - active: boolean = false; /* if true the filter filters! */ - callback_active_change: (new_state: boolean) => any; - - paused: boolean = true; - - abstract initialize(context: AudioContext, source_node: AudioNode); - abstract finalize(); - - /* whatever the input has been paused and we don't expect any input */ - abstract set_pause(flag: boolean); - - is_enabled(): boolean { - return this.enabled; - } - } - - export class JThresholdFilter extends JAbstractFilter implements ThresholdFilter { - public static update_task_interval = 20; /* 20ms */ - - type = Type.THRESHOLD; - callback_level?: (value: number) => any; - - private _threshold = 50; - - private _update_task: any; - private _analyser: AnalyserNode; - private _analyse_buffer: Uint8Array; - - private _silence_count = 0; - private _margin_frames = 5; - - private _current_level = 0; - private _smooth_release = 0; - private _smooth_attack = 0; - - finalize() { - this.set_pause(true); - - if(this.source_node) { - try { this.source_node.disconnect(this._analyser) } catch (error) {} - try { this.source_node.disconnect(this.audio_node) } catch (error) {} - } - - this._analyser = undefined; - this.source_node = undefined; - this.audio_node = undefined; - this.context = undefined; - } - - initialize(context: AudioContext, source_node: AudioNode) { - this.context = context; - this.source_node = source_node; - - this.audio_node = context.createGain(); - this._analyser = context.createAnalyser(); - - const optimal_ftt_size = Math.ceil((source_node.context || context).sampleRate * (JThresholdFilter.update_task_interval / 1000)); - const base2_ftt = Math.pow(2, Math.ceil(Math.log2(optimal_ftt_size))); - this._analyser.fftSize = base2_ftt; - - if(!this._analyse_buffer || this._analyse_buffer.length < this._analyser.fftSize) - this._analyse_buffer = new Uint8Array(this._analyser.fftSize); - - this.active = false; - this.audio_node.gain.value = 1; - - this.source_node.connect(this.audio_node); - this.source_node.connect(this._analyser); - - /* force update paused state */ - this.set_pause(!(this.paused = !this.paused)); - } - - get_margin_frames(): number { return this._margin_frames; } - set_margin_frames(value: number) { - this._margin_frames = value; - } - - get_attack_smooth(): number { - return this._smooth_attack; - } - - get_release_smooth(): number { - return this._smooth_release; - } - - set_attack_smooth(value: number) { - this._smooth_attack = value; - } - - set_release_smooth(value: number) { - this._smooth_release = value; - } - - get_threshold(): number { - return this._threshold; - } - - set_threshold(value: number): Promise { - this._threshold = value; - return Promise.resolve(); - } - - public static process(buffer: Uint8Array, ftt_size: number, previous: number, smooth: number) { - let level; - { - let total = 0, float, rms; - - for(let index = 0; index < ftt_size; index++) { - float = ( buffer[index++] / 0x7f ) - 1; - total += (float * float); - } - rms = Math.sqrt(total / ftt_size); - let db = 20 * ( Math.log(rms) / Math.log(10) ); - // sanity check - - db = Math.max(-192, Math.min(db, 0)); - level = 100 + ( db * 1.92 ); - } - - return previous * smooth + level * (1 - smooth); - } - - private _analyse() { - this._analyser.getByteTimeDomainData(this._analyse_buffer); - - let smooth; - if(this._silence_count == 0) - smooth = this._smooth_release; - else - smooth = this._smooth_attack; - - this._current_level = JThresholdFilter.process(this._analyse_buffer, this._analyser.fftSize, this._current_level, smooth); - - this._update_gain_node(); - if(this.callback_level) - this.callback_level(this._current_level); - } - - private _update_gain_node() { - let state; - if(this._current_level > this._threshold) { - this._silence_count = 0; - state = true; - } else { - state = this._silence_count++ < this._margin_frames; - } - if(state) { - this.audio_node.gain.value = 1; - if(this.active) { - this.active = false; - this.callback_active_change(false); - } - } else { - this.audio_node.gain.value = 0; - if(!this.active) { - this.active = true; - this.callback_active_change(true); - } - } - } - - set_pause(flag: boolean) { - if(flag === this.paused) return; - this.paused = flag; - - if(this.paused) { - clearInterval(this._update_task); - this._update_task = undefined; - - if(this.active) { - this.active = false; - this.callback_active_change(false); - } - } else { - if(!this._update_task && this._analyser) - this._update_task = setInterval(() => this._analyse(), JThresholdFilter.update_task_interval); - } - } - } - - export class JStateFilter extends JAbstractFilter implements StateFilter { - type = Type.STATE; - - finalize() { - if(this.source_node) { - try { this.source_node.disconnect(this.audio_node) } catch (error) {} - } - - this.source_node = undefined; - this.audio_node = undefined; - this.context = undefined; - } - - initialize(context: AudioContext, source_node: AudioNode) { - this.context = context; - this.source_node = source_node; - - this.audio_node = context.createGain(); - this.audio_node.gain.value = this.active ? 0 : 1; - - this.source_node.connect(this.audio_node); - } - - is_active(): boolean { - return this.active; - } - - set_state(state: boolean): Promise { - if(this.active === state) - return Promise.resolve(); - - this.active = state; - if(this.audio_node) - this.audio_node.gain.value = state ? 0 : 1; - this.callback_active_change(state); - return Promise.resolve(); - } - - set_pause(flag: boolean) { - this.paused = flag; - } - } - } - - class JavascriptInput implements AbstractInput { - private _state: InputState = InputState.PAUSED; - private _current_device: JavascriptInputDevice | undefined; - private _current_consumer: InputConsumer; - - private _current_stream: MediaStream; - private _current_audio_stream: MediaStreamAudioSourceNode; - - private _audio_context: AudioContext; - private _source_node: AudioNode; /* last node which could be connected to the target; target might be the _consumer_node */ - private _consumer_callback_node: ScriptProcessorNode; - private readonly _consumer_audio_callback; - private _volume_node: GainNode; - private _mute_node: GainNode; - - private _filters: filter.Filter[] = []; - private _filter_active: boolean = false; - - private _volume: number = 1; - - callback_begin: () => any = undefined; - callback_end: () => any = undefined; - - constructor() { - player.on_ready(() => this._audio_initialized()); - this._consumer_audio_callback = this._audio_callback.bind(this); - } - - private _audio_initialized() { - this._audio_context = player.context(); - if(!this._audio_context) - return; - - this._mute_node = this._audio_context.createGain(); - this._mute_node.gain.value = 0; - this._mute_node.connect(this._audio_context.destination); - - this._consumer_callback_node = this._audio_context.createScriptProcessor(1024 * 4); - this._consumer_callback_node.connect(this._mute_node); - - this._volume_node = this._audio_context.createGain(); - this._volume_node.gain.value = this._volume; - - this._initialize_filters(); - if(this._state === InputState.INITIALIZING) - this.start(); - } - - private _initialize_filters() { - const filters = this._filters as any as filter.JAbstractFilter[]; - for(const filter of filters) { - if(filter.is_enabled()) - filter.finalize(); - } - - if(this._audio_context && this._volume_node) { - const active_filter = filters.filter(e => e.is_enabled()); - let stream: AudioNode = this._volume_node; - for(const f of active_filter) { - f.initialize(this._audio_context, stream); - stream = f.audio_node; - } - this._switch_source_node(stream); - } - } - - private _audio_callback(event: AudioProcessingEvent) { - if(!this._current_consumer || this._current_consumer.type !== InputConsumerType.CALLBACK) - return; - - const callback = this._current_consumer as CallbackInputConsumer; - if(callback.callback_audio) - callback.callback_audio(event.inputBuffer); - if(callback.callback_buffer) { - log.warn(LogCategory.AUDIO, tr("AudioInput has callback buffer, but this isn't supported yet!")); - } - } - - current_state() : InputState { return this._state; }; - - private _start_promise: Promise; - async start() : Promise { - if(this._start_promise) { - try { - await this._start_promise; - if(this._state != InputState.PAUSED) - return; - } catch(error) { - log.debug(LogCategory.AUDIO, tr("JavascriptInput:start() Start promise await resulted in an error: %o"), error); - } - } - - return await (this._start_promise = this._start()); - } - - /* request permission for devices only one per time! */ - private static _running_request: Promise; - static async request_media_stream(device_id: string, group_id: string) : Promise { - while(this._running_request) { - try { - await this._running_request; - } catch(error) { } - } - const promise = (this._running_request = this.request_media_stream0(device_id, group_id)); - try { - return await this._running_request; - } finally { - if(this._running_request === promise) - this._running_request = undefined; - } - } - - static async request_media_stream0(device_id: string, group_id: string) : Promise { - const media_function = getUserMediaFunctionPromise(); - if(!media_function) return InputStartResult.ENOTSUPPORTED; - - try { - log.info(LogCategory.AUDIO, tr("Requesting a microphone stream for device %s in group %s"), device_id, group_id); - - const audio_constrains: MediaTrackConstraints = {}; - audio_constrains.deviceId = device_id; - audio_constrains.groupId = group_id; - - audio_constrains.echoCancellation = true; - /* may supported */ (audio_constrains as any).autoGainControl = true; - /* may supported */ (audio_constrains as any).noiseSuppression = true; - /* disabled because most the time we get a OverconstrainedError */ //audio_constrains.sampleSize = {min: 420, max: 960 * 10, ideal: 960}; - - const stream = await media_function({audio: audio_constrains, video: undefined}); - if(!_queried_permissioned) query_devices(); /* we now got permissions, requery devices */ - return stream; - } catch(error) { - if('name' in error) { - if(error.name === "NotAllowedError") { - //createErrorModal(tr("Failed to create microphone"), tr("Microphone recording failed. Please allow TeaWeb access to your microphone")).open(); - //FIXME: Move this to somewhere else! - - log.warn(LogCategory.AUDIO, tr("Microphone request failed (No permissions). Browser message: %o"), error.message); - return InputStartResult.ENOTALLOWED; - } else { - log.warn(LogCategory.AUDIO, tr("Microphone request failed. Request resulted in error: %o: %o"), error.name, error); - } - } else { - log.warn(LogCategory.AUDIO, tr("Failed to initialize recording stream (%o)"), error); - } - return InputStartResult.EUNKNOWN; - } - } - - private async _start() : Promise { - try { - if(this._state != InputState.PAUSED) - throw tr("recorder already started"); - - this._state = InputState.INITIALIZING; - if(!this._current_device) - throw tr("invalid device"); - - if(!this._audio_context) { - debugger; - throw tr("missing audio context"); - } - - const _result = await JavascriptInput.request_media_stream(this._current_device.device_id, this._current_device.group_id); - if(!(_result instanceof MediaStream)) { - this._state = InputState.PAUSED; - return _result; - } - this._current_stream = _result; - - for(const f of this._filters) - if(f.is_enabled() && f instanceof filter.JAbstractFilter) - f.set_pause(false); - this._consumer_callback_node.addEventListener('audioprocess', this._consumer_audio_callback); - - this._current_audio_stream = this._audio_context.createMediaStreamSource(this._current_stream); - this._current_audio_stream.connect(this._volume_node); - this._state = InputState.RECORDING; - return InputStartResult.EOK; - } catch(error) { - if(this._state == InputState.INITIALIZING) { - this._state = InputState.PAUSED; - } - throw error; - } finally { - this._start_promise = undefined; - } - } - - async stop() { - /* await all starts */ - try { - if(this._start_promise) - await this._start_promise; - } catch(error) {} - - this._state = InputState.PAUSED; - if(this._current_audio_stream) - this._current_audio_stream.disconnect(); - - if(this._current_stream) { - if(this._current_stream.stop) - this._current_stream.stop(); - else - this._current_stream.getTracks().forEach(value => { - value.stop(); - }); - } - - this._current_stream = undefined; - this._current_audio_stream = undefined; - for(const f of this._filters) - if(f.is_enabled() && f instanceof filter.JAbstractFilter) - f.set_pause(true); - if(this._consumer_callback_node) - this._consumer_callback_node.removeEventListener('audioprocess', this._consumer_audio_callback); - return undefined; - } - - - current_device(): InputDevice | undefined { - return this._current_device; - } - - async set_device(device: InputDevice | undefined) { - if(this._current_device === device) - return; - - - const saved_state = this._state; - try { - await this.stop(); - } catch(error) { - log.warn(LogCategory.AUDIO, tr("Failed to stop previous record session (%o)"), error); - } - - this._current_device = device as any; /* TODO: Test for device_id and device_group */ - if(!device) { - this._state = saved_state === InputState.PAUSED ? InputState.PAUSED : InputState.DRY; - return; - } - - if(saved_state !== InputState.PAUSED) { - try { - await this.start() - } catch(error) { - log.warn(LogCategory.AUDIO, tr("Failed to start new recording stream (%o)"), error); - throw "failed to start record"; - } - } - return; - } - - - get_filter(type: filter.Type): filter.Filter | undefined { - for(const filter of this._filters) - if(filter.type == type) - return filter; - - let new_filter: filter.JAbstractFilter; - switch (type) { - case filter.Type.STATE: - new_filter = new filter.JStateFilter(); - break; - case filter.Type.VOICE_LEVEL: - throw "voice filter isn't supported!"; - case filter.Type.THRESHOLD: - new_filter = new filter.JThresholdFilter(); - break; - default: - throw "invalid filter type, or type isn't implemented! (" + type + ")"; - } - - new_filter.callback_active_change = () => this._recalculate_filter_status(); - this._filters.push(new_filter as any); - this.enable_filter(type); - return new_filter as any; - } - - supports_filter(type: audio.recorder.filter.Type) : boolean { - switch (type) { - case audio.recorder.filter.Type.THRESHOLD: - case audio.recorder.filter.Type.STATE: - return true; - default: - return false; - } - } - - private find_filter(type: filter.Type) : filter.JAbstractFilter | undefined { - for(const filter of this._filters) - if(filter.type == type) - return filter as any; - return undefined; - } - - clear_filter() { - for(const _filter of this._filters) { - if(!_filter.is_enabled()) - continue; - - const c_filter = _filter as any as filter.JAbstractFilter; - c_filter.finalize(); - c_filter.enabled = false; - } - - this._initialize_filters(); - this._recalculate_filter_status(); - } - - disable_filter(type: filter.Type) { - const filter = this.find_filter(type); - if(!filter) return; - - /* test if the filter is active */ - if(!filter.is_enabled()) - return; - - filter.enabled = false; - filter.set_pause(true); - filter.finalize(); - this._initialize_filters(); - this._recalculate_filter_status(); - } - - enable_filter(type: filter.Type) { - const filter = this.get_filter(type) as any as filter.JAbstractFilter; - if(filter.is_enabled()) - return; - - filter.enabled = true; - filter.set_pause(typeof this._current_audio_stream !== "object"); - this._initialize_filters(); - this._recalculate_filter_status(); - } - - private _recalculate_filter_status() { - let filtered = this._filters.filter(e => e.is_enabled()).filter(e => (e as any as filter.JAbstractFilter).active).length > 0; - if(filtered === this._filter_active) - return; - - this._filter_active = filtered; - if(filtered) { - if(this.callback_end) - this.callback_end(); - } else { - if(this.callback_begin) - this.callback_begin(); - } - } - - current_consumer(): InputConsumer | undefined { - return this._current_consumer; - } - - async set_consumer(consumer: InputConsumer) { - if(this._current_consumer) { - if(this._current_consumer.type == InputConsumerType.NODE) { - if(this._source_node) - (this._current_consumer as NodeInputConsumer).callback_disconnect(this._source_node) - } else if(this._current_consumer.type === InputConsumerType.CALLBACK) { - if(this._source_node) - this._source_node.disconnect(this._consumer_callback_node); - } - } - - if(consumer) { - if(consumer.type == InputConsumerType.CALLBACK) { - if(this._source_node) - this._source_node.connect(this._consumer_callback_node); - } else if(consumer.type == InputConsumerType.NODE) { - if(this._source_node) - (consumer as NodeInputConsumer).callback_node(this._source_node); - } else { - throw "native callback consumers are not supported!"; - } - } - this._current_consumer = consumer; - } - - private _switch_source_node(new_node: AudioNode) { - if(this._current_consumer) { - if(this._current_consumer.type == InputConsumerType.NODE) { - const node_consumer = this._current_consumer as NodeInputConsumer; - if(this._source_node) - node_consumer.callback_disconnect(this._source_node); - if(new_node) - node_consumer.callback_node(new_node); - } else if(this._current_consumer.type == InputConsumerType.CALLBACK) { - this._source_node.disconnect(this._consumer_callback_node); - if(new_node) - new_node.connect(this._consumer_callback_node); - } - } - this._source_node = new_node; - } - - get_volume(): number { - return this._volume; - } - - set_volume(volume: number) { - if(volume === this._volume) - return; - this._volume = volume; - this._volume_node.gain.value = volume; - } - } - - class JavascriptLevelmeter implements LevelMeter { - private static _instances: JavascriptLevelmeter[] = []; - private static _update_task: number; - - readonly _device: JavascriptInputDevice; - - private _callback: (num: number) => any; - - private _context: AudioContext; - private _gain_node: GainNode; - private _source_node: MediaStreamAudioSourceNode; - private _analyser_node: AnalyserNode; - - private _media_stream: MediaStream; - - private _analyse_buffer: Uint8Array; - - private _current_level = 0; - - constructor(device: JavascriptInputDevice) { - this._device = device; - } - - async initialize() { - try { - await new Promise((resolve, reject) => { - const timeout = setTimeout(reject, 5000); - player.on_ready(() => { - clearTimeout(timeout); - resolve(); - }); - }); - } catch(error) { - throw tr("audio context timeout"); - } - this._context = player.context(); - if(!this._context) throw tr("invalid context"); - - this._gain_node = this._context.createGain(); - this._gain_node.gain.setValueAtTime(0, 0); - - /* analyser node */ - this._analyser_node = this._context.createAnalyser(); - - const optimal_ftt_size = Math.ceil(this._context.sampleRate * (filter.JThresholdFilter.update_task_interval / 1000)); - this._analyser_node.fftSize = Math.pow(2, Math.ceil(Math.log2(optimal_ftt_size))); - - if(!this._analyse_buffer || this._analyse_buffer.length < this._analyser_node.fftSize) - this._analyse_buffer = new Uint8Array(this._analyser_node.fftSize); - - /* starting stream */ - const _result = await JavascriptInput.request_media_stream(this._device.device_id, this._device.group_id); - if(!(_result instanceof MediaStream)){ - if(_result === InputStartResult.ENOTALLOWED) - throw tr("No permissions"); - if(_result === InputStartResult.ENOTSUPPORTED) - throw tr("Not supported"); - if(_result === InputStartResult.EBUSY) - throw tr("Device busy"); - if(_result === InputStartResult.EUNKNOWN) - throw tr("an error occurred"); - throw _result; - } - this._media_stream = _result; - - this._source_node = this._context.createMediaStreamSource(this._media_stream); - this._source_node.connect(this._analyser_node); - this._analyser_node.connect(this._gain_node); - this._gain_node.connect(this._context.destination); - - JavascriptLevelmeter._instances.push(this); - if(JavascriptLevelmeter._instances.length == 1) { - clearInterval(JavascriptLevelmeter._update_task); - JavascriptLevelmeter._update_task = setInterval(() => JavascriptLevelmeter._analyse_all(), filter.JThresholdFilter.update_task_interval) as any; - } - } - - destory() { - JavascriptLevelmeter._instances.remove(this); - if(JavascriptLevelmeter._instances.length == 0) { - clearInterval(JavascriptLevelmeter._update_task); - JavascriptLevelmeter._update_task = 0; - } - - if(this._source_node) { - this._source_node.disconnect(); - this._source_node = undefined; - } - if(this._media_stream) { - if(this._media_stream.stop) - this._media_stream.stop(); - else - this._media_stream.getTracks().forEach(value => { - value.stop(); - }); - this._media_stream = undefined; - } - if(this._gain_node) { - this._gain_node.disconnect(); - this._gain_node = undefined; - } - if(this._analyser_node) { - this._analyser_node.disconnect(); - this._analyser_node = undefined; - } - } - - device(): audio.recorder.InputDevice { - return this._device; - } - - set_observer(callback: (value: number) => any) { - this._callback = callback; - } - - private static _analyse_all() { - for(const instance of [...this._instances]) - instance._analyse(); - } - - private _analyse() { - this._analyser_node.getByteTimeDomainData(this._analyse_buffer); - - this._current_level = filter.JThresholdFilter.process(this._analyse_buffer, this._analyser_node.fftSize, this._current_level, .75); - if(this._callback) - this._callback(this._current_level); - } - } - } -} \ No newline at end of file diff --git a/web/js/voice/VoiceClient.ts b/web/js/voice/VoiceClient.ts index c54584c4..3bc565fc 100644 --- a/web/js/voice/VoiceClient.ts +++ b/web/js/voice/VoiceClient.ts @@ -1,236 +1,239 @@ -/// +import {voice} from "tc-shared/connection/ConnectionBase"; +import VoiceClient = voice.VoiceClient; +import PlayerState = voice.PlayerState; +import {CodecClientCache} from "../codec/Codec"; +import * as aplayer from "../audio/player"; +import {LogCategory} from "tc-shared/log"; +import * as log from "tc-shared/log"; +import LatencySettings = voice.LatencySettings; -namespace audio { - export namespace js { - export class VoiceClientController implements connection.voice.VoiceClient { - callback_playback: () => any; - callback_state_changed: (new_state: connection.voice.PlayerState) => any; - callback_stopped: () => any; - client_id: number; +export class VoiceClientController implements VoiceClient { + callback_playback: () => any; + callback_state_changed: (new_state: PlayerState) => any; + callback_stopped: () => any; + client_id: number; - speakerContext: AudioContext; - private _player_state: connection.voice.PlayerState = connection.voice.PlayerState.STOPPED; - private _codecCache: CodecClientCache[] = []; + speakerContext: AudioContext; + private _player_state: PlayerState = PlayerState.STOPPED; + private _codecCache: CodecClientCache[] = []; - private _time_index: number = 0; - private _latency_buffer_length: number = 3; - private _buffer_timeout: NodeJS.Timer; + private _time_index: number = 0; + private _latency_buffer_length: number = 3; + private _buffer_timeout: NodeJS.Timer; - private _buffered_samples: AudioBuffer[] = []; - private _playing_nodes: AudioBufferSourceNode[] = []; + private _buffered_samples: AudioBuffer[] = []; + private _playing_nodes: AudioBufferSourceNode[] = []; - private _volume: number = 1; - allowBuffering: boolean = true; + private _volume: number = 1; + allowBuffering: boolean = true; - constructor(client_id: number) { - this.client_id = client_id; + constructor(client_id: number) { + this.client_id = client_id; - audio.player.on_ready(() => this.speakerContext = audio.player.context()); - } + aplayer.on_ready(() => this.speakerContext = aplayer.context()); + } - public initialize() { } + public initialize() { } - public close(){ } + public close(){ } - playback_buffer(buffer: AudioBuffer) { - if(!buffer) { - log.warn(LogCategory.VOICE, tr("[AudioController] Got empty or undefined buffer! Dropping it")); - return; - } + playback_buffer(buffer: AudioBuffer) { + if(!buffer) { + log.warn(LogCategory.VOICE, tr("[AudioController] Got empty or undefined buffer! Dropping it")); + return; + } - if(!this.speakerContext) { - log.warn(LogCategory.VOICE, tr("[AudioController] Failed to replay audio. Global audio context not initialized yet!")); - return; - } + if(!this.speakerContext) { + log.warn(LogCategory.VOICE, tr("[AudioController] Failed to replay audio. Global audio context not initialized yet!")); + return; + } - if (buffer.sampleRate != this.speakerContext.sampleRate) - log.warn(LogCategory.VOICE, tr("[AudioController] Source sample rate isn't equal to playback sample rate! (%o | %o)"), buffer.sampleRate, this.speakerContext.sampleRate); + if (buffer.sampleRate != this.speakerContext.sampleRate) + log.warn(LogCategory.VOICE, tr("[AudioController] Source sample rate isn't equal to playback sample rate! (%o | %o)"), buffer.sampleRate, this.speakerContext.sampleRate); - this.apply_volume_to_buffer(buffer); + this.apply_volume_to_buffer(buffer); - this._buffered_samples.push(buffer); - if(this._player_state == connection.voice.PlayerState.STOPPED || this._player_state == connection.voice.PlayerState.STOPPING) { - log.info(LogCategory.VOICE, tr("[Audio] Starting new playback")); - this.set_state(connection.voice.PlayerState.PREBUFFERING); - } + this._buffered_samples.push(buffer); + if(this._player_state == PlayerState.STOPPED || this._player_state == PlayerState.STOPPING) { + log.info(LogCategory.VOICE, tr("[Audio] Starting new playback")); + this.set_state(PlayerState.PREBUFFERING); + } - switch (this._player_state) { - case connection.voice.PlayerState.PREBUFFERING: - case connection.voice.PlayerState.BUFFERING: - this.reset_buffer_timeout(true); //Reset timeout, we got a new buffer - if(this._buffered_samples.length <= this._latency_buffer_length) { - if(this._player_state == connection.voice.PlayerState.BUFFERING) { - if(this.allowBuffering) - break; - } else - break; - } - if(this._player_state == connection.voice.PlayerState.PREBUFFERING) { - log.info(LogCategory.VOICE, tr("[Audio] Prebuffering succeeded (Replaying now)")); - if(this.callback_playback) - this.callback_playback(); - } else if(this.allowBuffering) { - log.info(LogCategory.VOICE, tr("[Audio] Buffering succeeded (Replaying now)")); - } - this._player_state = connection.voice.PlayerState.PLAYING; - case connection.voice.PlayerState.PLAYING: - this.replay_queue(); - break; - default: + switch (this._player_state) { + case PlayerState.PREBUFFERING: + case PlayerState.BUFFERING: + this.reset_buffer_timeout(true); //Reset timeout, we got a new buffer + if(this._buffered_samples.length <= this._latency_buffer_length) { + if(this._player_state == PlayerState.BUFFERING) { + if(this.allowBuffering) + break; + } else break; } - } - - private replay_queue() { - let buffer: AudioBuffer; - while((buffer = this._buffered_samples.pop_front())) { - if(this._playing_nodes.length >= this._latency_buffer_length * 1.5 + 3) { - log.info(LogCategory.VOICE, tr("Dropping buffer because playing queue grows to much")); - continue; /* drop the data (we're behind) */ - } - if(this._time_index < this.speakerContext.currentTime) - this._time_index = this.speakerContext.currentTime; - - const player = this.speakerContext.createBufferSource(); - player.buffer = buffer; - - player.onended = () => this.on_buffer_replay_finished(player); - this._playing_nodes.push(player); - - player.connect(audio.player.destination()); - player.start(this._time_index); - this._time_index += buffer.duration; + if(this._player_state == PlayerState.PREBUFFERING) { + log.info(LogCategory.VOICE, tr("[Audio] Prebuffering succeeded (Replaying now)")); + if(this.callback_playback) + this.callback_playback(); + } else if(this.allowBuffering) { + log.info(LogCategory.VOICE, tr("[Audio] Buffering succeeded (Replaying now)")); } + this._player_state = PlayerState.PLAYING; + case PlayerState.PLAYING: + this.replay_queue(); + break; + default: + break; + } + } + + private replay_queue() { + let buffer: AudioBuffer; + while((buffer = this._buffered_samples.pop_front())) { + if(this._playing_nodes.length >= this._latency_buffer_length * 1.5 + 3) { + log.info(LogCategory.VOICE, tr("Dropping buffer because playing queue grows to much")); + continue; /* drop the data (we're behind) */ } + if(this._time_index < this.speakerContext.currentTime) + this._time_index = this.speakerContext.currentTime; - private on_buffer_replay_finished(node: AudioBufferSourceNode) { - this._playing_nodes.remove(node); - this.test_buffer_queue(); - } + const player = this.speakerContext.createBufferSource(); + player.buffer = buffer; - stopAudio(now: boolean = false) { - this._player_state = connection.voice.PlayerState.STOPPING; - if(now) { - this._player_state = connection.voice.PlayerState.STOPPED; - this._buffered_samples = []; + player.onended = () => this.on_buffer_replay_finished(player); + this._playing_nodes.push(player); - for(const entry of this._playing_nodes) - entry.stop(0); - this._playing_nodes = []; + player.connect(aplayer.destination()); + player.start(this._time_index); + this._time_index += buffer.duration; + } + } - if(this.callback_stopped) - this.callback_stopped(); - } else { - this.test_buffer_queue(); /* test if we're not already done */ - this.replay_queue(); /* flush the queue */ - } - } + private on_buffer_replay_finished(node: AudioBufferSourceNode) { + this._playing_nodes.remove(node); + this.test_buffer_queue(); + } - private test_buffer_queue() { - if(this._buffered_samples.length == 0 && this._playing_nodes.length == 0) { - if(this._player_state != connection.voice.PlayerState.STOPPING && this._player_state != connection.voice.PlayerState.STOPPED) { - if(this._player_state == connection.voice.PlayerState.BUFFERING) - return; //We're already buffering + stopAudio(now: boolean = false) { + this._player_state = PlayerState.STOPPING; + if(now) { + this._player_state = PlayerState.STOPPED; + this._buffered_samples = []; - this._player_state = connection.voice.PlayerState.BUFFERING; - if(!this.allowBuffering) - log.warn(LogCategory.VOICE, tr("[Audio] Detected a buffer underflow!")); - this.reset_buffer_timeout(true); - } else { - this._player_state = connection.voice.PlayerState.STOPPED; - if(this.callback_stopped) - this.callback_stopped(); - } - } - } + for(const entry of this._playing_nodes) + entry.stop(0); + this._playing_nodes = []; - private reset_buffer_timeout(restart: boolean) { - if(this._buffer_timeout) - clearTimeout(this._buffer_timeout); + if(this.callback_stopped) + this.callback_stopped(); + } else { + this.test_buffer_queue(); /* test if we're not already done */ + this.replay_queue(); /* flush the queue */ + } + } - if(restart) - this._buffer_timeout = setTimeout(() => { - if(this._player_state == connection.voice.PlayerState.PREBUFFERING || this._player_state == connection.voice.PlayerState.BUFFERING) { - log.warn(LogCategory.VOICE, tr("[Audio] Buffering exceeded timeout. Flushing and stopping replay")); - this.stopAudio(); - } - this._buffer_timeout = undefined; - }, 1000); - } + private test_buffer_queue() { + if(this._buffered_samples.length == 0 && this._playing_nodes.length == 0) { + if(this._player_state != PlayerState.STOPPING && this._player_state != PlayerState.STOPPED) { + if(this._player_state == PlayerState.BUFFERING) + return; //We're already buffering - private apply_volume_to_buffer(buffer: AudioBuffer) { - if(this._volume == 1) - return; - - for(let channel = 0; channel < buffer.numberOfChannels; channel++) { - let data = buffer.getChannelData(channel); - for(let sample = 0; sample < data.length; sample++) { - let lane = data[sample]; - lane *= this._volume; - data[sample] = lane; - } - } - } - - private set_state(state: connection.voice.PlayerState) { - if(this._player_state == state) - return; - - this._player_state = state; - if(this.callback_state_changed) - this.callback_state_changed(this._player_state); - } - - get_codec_cache(codec: number) : CodecClientCache { - while(this._codecCache.length <= codec) - this._codecCache.push(new CodecClientCache()); - - return this._codecCache[codec]; - } - - get_state(): connection.voice.PlayerState { - return this._player_state; - } - - get_volume(): number { - return this._volume; - } - - set_volume(volume: number): void { - if(this._volume == volume) - return; - - this._volume = volume; - - /* apply the volume to all other buffers */ - for(const buffer of this._buffered_samples) - this.apply_volume_to_buffer(buffer); - } - - abort_replay() { - this.stopAudio(true); - } - - latency_settings(settings?: connection.voice.LatencySettings): connection.voice.LatencySettings { - throw "not supported"; - } - - reset_latency_settings() { - throw "not supported"; - } - - support_latency_settings(): boolean { - return false; - } - - support_flush(): boolean { - return false; - } - - flush() { - throw "not supported"; + this._player_state = PlayerState.BUFFERING; + if(!this.allowBuffering) + log.warn(LogCategory.VOICE, tr("[Audio] Detected a buffer underflow!")); + this.reset_buffer_timeout(true); + } else { + this._player_state = PlayerState.STOPPED; + if(this.callback_stopped) + this.callback_stopped(); } } } + + private reset_buffer_timeout(restart: boolean) { + if(this._buffer_timeout) + clearTimeout(this._buffer_timeout); + + if(restart) + this._buffer_timeout = setTimeout(() => { + if(this._player_state == PlayerState.PREBUFFERING || this._player_state == PlayerState.BUFFERING) { + log.warn(LogCategory.VOICE, tr("[Audio] Buffering exceeded timeout. Flushing and stopping replay")); + this.stopAudio(); + } + this._buffer_timeout = undefined; + }, 1000); + } + + private apply_volume_to_buffer(buffer: AudioBuffer) { + if(this._volume == 1) + return; + + for(let channel = 0; channel < buffer.numberOfChannels; channel++) { + let data = buffer.getChannelData(channel); + for(let sample = 0; sample < data.length; sample++) { + let lane = data[sample]; + lane *= this._volume; + data[sample] = lane; + } + } + } + + private set_state(state: PlayerState) { + if(this._player_state == state) + return; + + this._player_state = state; + if(this.callback_state_changed) + this.callback_state_changed(this._player_state); + } + + get_codec_cache(codec: number) : CodecClientCache { + while(this._codecCache.length <= codec) + this._codecCache.push(new CodecClientCache()); + + return this._codecCache[codec]; + } + + get_state(): PlayerState { + return this._player_state; + } + + get_volume(): number { + return this._volume; + } + + set_volume(volume: number): void { + if(this._volume == volume) + return; + + this._volume = volume; + + /* apply the volume to all other buffers */ + for(const buffer of this._buffered_samples) + this.apply_volume_to_buffer(buffer); + } + + abort_replay() { + this.stopAudio(true); + } + + latency_settings(settings?: LatencySettings): LatencySettings { + throw "not supported"; + } + + reset_latency_settings() { + throw "not supported"; + } + + support_latency_settings(): boolean { + return false; + } + + support_flush(): boolean { + return false; + } + + flush() { + throw "not supported"; + } } \ No newline at end of file diff --git a/web/js/voice/VoiceHandler.ts b/web/js/voice/VoiceHandler.ts index a834e6dd..7d84850a 100644 --- a/web/js/voice/VoiceHandler.ts +++ b/web/js/voice/VoiceHandler.ts @@ -1,666 +1,683 @@ -/// -/// +import * as log from "tc-shared/log"; +import * as loader from "tc-loader"; +import * as aplayer from "../audio/player"; +import * as elog from "tc-shared/ui/frames/server_log"; +import {BasicCodec} from "../codec/BasicCodec"; +import {CodecType} from "../codec/Codec"; +import {LogCategory} from "tc-shared/log"; +import {createErrorModal} from "tc-shared/ui/elements/Modal"; +import {CodecWrapperWorker} from "../codec/CodecWrapperWorker"; +import {ServerConnection} from "../connection/ServerConnection"; +import {voice} from "tc-shared/connection/ConnectionBase"; +import AbstractVoiceConnection = voice.AbstractVoiceConnection; +import {RecorderProfile} from "tc-shared/voice/RecorderProfile"; +import {VoiceClientController} from "./VoiceClient"; +import {settings} from "tc-shared/settings"; +import {CallbackInputConsumer, InputConsumerType, NodeInputConsumer} from "tc-shared/voice/RecorderBase"; +import VoiceClient = voice.VoiceClient; -namespace audio { - export namespace js { - export namespace codec { - class CacheEntry { - instance: BasicCodec; - owner: number; +export namespace codec { + class CacheEntry { + instance: BasicCodec; + owner: number; - last_access: number; - } + last_access: number; + } - export class CodecPool { - codecIndex: number; - name: string; - type: CodecType; + export function codec_supported(type: CodecType) { + return type == CodecType.OPUS_MUSIC || type == CodecType.OPUS_VOICE; + } - entries: CacheEntry[] = []; - maxInstances: number = 2; + export class CodecPool { + codecIndex: number; + name: string; + type: CodecType; - private _supported: boolean = true; + entries: CacheEntry[] = []; + maxInstances: number = 2; - initialize(cached: number) { - /* test if we're able to use this codec */ - const dummy_client_id = 0xFFEF; + private _supported: boolean = true; - this.ownCodec(dummy_client_id, _ => {}).then(codec => { - log.info(LogCategory.VOICE, tr("Release again! (%o)"), codec); - this.releaseCodec(dummy_client_id); - }).catch(error => { - if(this._supported) { - log.warn(LogCategory.VOICE, tr("Disabling codec support for "), this.name); - createErrorModal(tr("Could not load codec driver"), tr("Could not load or initialize codec ") + this.name + "
" + - "Error: " + JSON.stringify(error) + "").open(); - log.error(LogCategory.VOICE, tr("Failed to initialize the opus codec. Error: %o"), error); - } else { - log.debug(LogCategory.VOICE, tr("Failed to initialize already disabled codec. Error: %o"), error); - } - this._supported = false; - }); - } + initialize(cached: number) { + /* test if we're able to use this codec */ + const dummy_client_id = 0xFFEF; - supported() { return this._supported; } - - ownCodec?(clientId: number, callback_encoded: (buffer: Uint8Array) => any, create: boolean = true) : Promise { - return new Promise((resolve, reject) => { - if(!this._supported) { - reject(tr("unsupported codec!")); - return; - } - - let free_slot = 0; - for(let index = 0; index < this.entries.length; index++) { - if(this.entries[index].owner == clientId) { - this.entries[index].last_access = Date.now(); - if(this.entries[index].instance.initialized()) - resolve(this.entries[index].instance); - else { - this.entries[index].instance.initialise().then((flag) => { - //TODO test success flag - this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject); - }).catch(error => { - log.error(LogCategory.VOICE, tr("Could not initialize codec!\nError: %o"), error); - reject(typeof(error) === 'string' ? error : tr("Could not initialize codec!")); - }); - } - return; - } else if(this.entries[index].owner == 0) { - free_slot = index; - } - } - - if(!create) { - resolve(undefined); - return; - } - - if(free_slot == 0){ - free_slot = this.entries.length; - let entry = new CacheEntry(); - entry.instance = audio.codec.new_instance(this.type); - this.entries.push(entry); - } - this.entries[free_slot].owner = clientId; - this.entries[free_slot].last_access = new Date().getTime(); - this.entries[free_slot].instance.on_encoded_data = callback_encoded; - if(this.entries[free_slot].instance.initialized()) - this.entries[free_slot].instance.reset(); - else { - this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject); - return; - } - resolve(this.entries[free_slot].instance); - }); - } - - releaseCodec(clientId: number) { - for(let index = 0; index < this.entries.length; index++) - if(this.entries[index].owner == clientId) this.entries[index].owner = 0; - } - - constructor(index: number, name: string, type: CodecType){ - this.codecIndex = index; - this.name = name; - this.type = type; - - this._supported = this.type !== undefined && audio.codec.supported(this.type); - } - } - } - - export enum VoiceEncodeType { - JS_ENCODE, - NATIVE_ENCODE - } - - export class VoiceConnection extends connection.voice.AbstractVoiceConnection { - readonly connection: connection.ServerConnection; - - rtcPeerConnection: RTCPeerConnection; - dataChannel: RTCDataChannel; - - private _type: VoiceEncodeType = VoiceEncodeType.NATIVE_ENCODE; - - /* - * To ensure we're not sending any audio because the settings activates the input, - * we self mute the audio stream - */ - local_audio_mute: GainNode; - local_audio_stream: MediaStreamAudioDestinationNode; - - static codec_pool: codec.CodecPool[]; - - static codecSupported(type: number) : boolean { - return this.codec_pool && this.codec_pool.length > type && this.codec_pool[type].supported(); - } - - private voice_packet_id: number = 0; - private chunkVPacketId: number = 0; - private send_task: NodeJS.Timer; - - private _audio_source: RecorderProfile; - private _audio_clients: audio.js.VoiceClientController[] = []; - - private _encoder_codec: number = 5; - - constructor(connection: connection.ServerConnection) { - super(connection); - this.connection = connection; - - this._type = settings.static_global("voice_connection_type", this._type); - } - - destroy() { - clearInterval(this.send_task); - this.drop_rtp_session(); - this.acquire_voice_recorder(undefined, true).catch(error => { - log.warn(LogCategory.VOICE, tr("Failed to release voice recorder: %o"), error); - }).then(() => { - for(const client of this._audio_clients) { - client.abort_replay(); - client.callback_playback = undefined; - client.callback_state_changed = undefined; - client.callback_stopped = undefined; - } - this._audio_clients = undefined; - this._audio_source = undefined; - }); - } - - static native_encoding_supported() : boolean { - const context = window.webkitAudioContext || window.AudioContext; - if(!context) - return false; - - if(!context.prototype.createMediaStreamDestination) - return false; //Required, but not available within edge - - return true; - } - - static javascript_encoding_supported() : boolean { - if(!window.RTCPeerConnection) - return false; - if(!RTCPeerConnection.prototype.createDataChannel) - return false; - return true; - } - - current_encoding_supported() : boolean { - switch (this._type) { - case VoiceEncodeType.JS_ENCODE: - return audio.js.VoiceConnection.javascript_encoding_supported(); - case VoiceEncodeType.NATIVE_ENCODE: - return audio.js.VoiceConnection.native_encoding_supported(); - } - return false; - } - - private setup_native() { - log.info(LogCategory.VOICE, tr("Setting up native voice stream!")); - if(!audio.js.VoiceConnection.native_encoding_supported()) { - log.warn(LogCategory.VOICE, tr("Native codec isn't supported!")); - return; - } - - if(!this.local_audio_stream) { - this.local_audio_stream = audio.player.context().createMediaStreamDestination(); - } - if(!this.local_audio_mute) { - this.local_audio_mute = audio.player.context().createGain(); - this.local_audio_mute.connect(this.local_audio_stream); - this.local_audio_mute.gain.value = 1; - } - } - - private setup_js() { - if(!audio.js.VoiceConnection.javascript_encoding_supported()) return; - if(!this.send_task) - this.send_task = setInterval(this.send_next_voice_packet.bind(this), 20); /* send all 20ms out voice packets */ - } - - async acquire_voice_recorder(recorder: RecorderProfile | undefined, enforce?: boolean) { - if(this._audio_source === recorder && !enforce) - return; - - if(recorder) - await recorder.unmount(); - - if(this._audio_source) - await this._audio_source.unmount(); - - this.handle_local_voice_ended(); - this._audio_source = recorder; - - if(recorder) { - recorder.current_handler = this.connection.client; - - recorder.callback_unmount = this.on_recorder_yield.bind(this); - recorder.callback_start = this.handle_local_voice_started.bind(this); - recorder.callback_stop = this.handle_local_voice_ended.bind(this); - - if(this._type == VoiceEncodeType.NATIVE_ENCODE) { - if(!this.local_audio_stream) - this.setup_native(); /* requires initialized audio */ - - await recorder.input.set_consumer({ - type: audio.recorder.InputConsumerType.NODE, - callback_node: node => { - if(!this.local_audio_stream || !this.local_audio_mute) - return; - - node.connect(this.local_audio_mute); - }, - callback_disconnect: node => { - if(!this.local_audio_mute) - return; - - node.disconnect(this.local_audio_mute); - } - } as audio.recorder.NodeInputConsumer); - } else { - await recorder.input.set_consumer({ - type: audio.recorder.InputConsumerType.CALLBACK, - callback_audio: buffer => this.handle_local_voice(buffer, false) - } as audio.recorder.CallbackInputConsumer); - } - } - this.connection.client.update_voice_status(undefined); - } - - get_encoder_type() : VoiceEncodeType { return this._type; } - set_encoder_type(target: VoiceEncodeType) { - if(target == this._type) return; - this._type = target; - - if(this._type == VoiceEncodeType.NATIVE_ENCODE) - this.setup_native(); - else - this.setup_js(); - this.start_rtc_session(); - } - - voice_playback_support() : boolean { - return this.dataChannel && this.dataChannel.readyState == "open"; - } - - voice_send_support() : boolean { - if(this._type == VoiceEncodeType.NATIVE_ENCODE) - return audio.js.VoiceConnection.native_encoding_supported() && this.rtcPeerConnection.getLocalStreams().length > 0; - else - return this.voice_playback_support(); - } - - private voice_send_queue: {data: Uint8Array, codec: number}[] = []; - handleEncodedVoicePacket(data: Uint8Array, codec: number){ - this.voice_send_queue.push({data: data, codec: codec}); - } - - private send_next_voice_packet() { - const buffer = this.voice_send_queue.pop_front(); - if(!buffer) - return; - this.send_voice_packet(buffer.data, buffer.codec); - } - - send_voice_packet(encoded_data: Uint8Array, codec: number) { - if(this.dataChannel) { - this.voice_packet_id++; - if(this.voice_packet_id > 65535) - this.voice_packet_id = 0; - - let packet = new Uint8Array(encoded_data.byteLength + 5); - packet[0] = this.chunkVPacketId++ < 5 ? 1 : 0; //Flag header - packet[1] = 0; //Flag fragmented - packet[2] = (this.voice_packet_id >> 8) & 0xFF; //HIGHT (voiceID) - packet[3] = (this.voice_packet_id >> 0) & 0xFF; //LOW (voiceID) - packet[4] = codec; //Codec - packet.set(encoded_data, 5); - try { - this.dataChannel.send(packet); - } catch (error) { - log.warn(LogCategory.VOICE, tr("Failed to send voice packet. Error: %o"), error); - } + this.ownCodec(dummy_client_id, _ => {}).then(codec => { + log.info(LogCategory.VOICE, tr("Release again! (%o)"), codec); + this.releaseCodec(dummy_client_id); + }).catch(error => { + if(this._supported) { + log.warn(LogCategory.VOICE, tr("Disabling codec support for "), this.name); + createErrorModal(tr("Could not load codec driver"), tr("Could not load or initialize codec ") + this.name + "
" + + "Error: " + JSON.stringify(error) + "").open(); + log.error(LogCategory.VOICE, tr("Failed to initialize the opus codec. Error: %o"), error); } else { - log.warn(LogCategory.VOICE, tr("Could not transfer audio (not connected)")); + log.debug(LogCategory.VOICE, tr("Failed to initialize already disabled codec. Error: %o"), error); } - } + this._supported = false; + }); + } - private _audio_player_waiting = false; - start_rtc_session() { - if(!audio.player.initialized()) { - log.info(LogCategory.VOICE, tr("Audio player isn't initialized yet. Waiting for gesture.")); - if(!this._audio_player_waiting) { - this._audio_player_waiting = true; - audio.player.on_ready(() => this.start_rtc_session()); - } + supported() { return this._supported; } + + ownCodec?(clientId: number, callback_encoded: (buffer: Uint8Array) => any, create: boolean = true) : Promise { + return new Promise((resolve, reject) => { + if(!this._supported) { + reject(tr("unsupported codec!")); return; } - if(!this.current_encoding_supported()) - return false; - - if(this._type == VoiceEncodeType.NATIVE_ENCODE) - this.setup_native(); - else - this.setup_js(); - - this.drop_rtp_session(); - this._ice_use_cache = true; - - - let config: RTCConfiguration = {}; - config.iceServers = []; - config.iceServers.push({ urls: 'stun:stun.l.google.com:19302' }); - this.rtcPeerConnection = new RTCPeerConnection(config); - const dataChannelConfig = { ordered: true, maxRetransmits: 0 }; - - this.dataChannel = this.rtcPeerConnection.createDataChannel('main', dataChannelConfig); - this.dataChannel.onmessage = this.on_data_channel_message.bind(this); - this.dataChannel.onopen = this.on_data_channel.bind(this); - this.dataChannel.binaryType = "arraybuffer"; - - let sdpConstraints : RTCOfferOptions = {}; - sdpConstraints.offerToReceiveAudio = this._type == VoiceEncodeType.NATIVE_ENCODE; - sdpConstraints.offerToReceiveVideo = false; - sdpConstraints.voiceActivityDetection = true; - - this.rtcPeerConnection.onicecandidate = this.on_local_ice_candidate.bind(this); - if(this.local_audio_stream) { //May a typecheck? - this.rtcPeerConnection.addStream(this.local_audio_stream.stream); - log.info(LogCategory.VOICE, tr("Adding native audio stream (%o)!"), this.local_audio_stream.stream); - } - - this.rtcPeerConnection.createOffer(sdpConstraints) - .then(offer => this.on_local_offer_created(offer)) - .catch(error => { - log.error(LogCategory.VOICE, tr("Could not create ice offer! error: %o"), error); - }); - } - - drop_rtp_session() { - if(this.dataChannel) { - this.dataChannel.close(); - this.dataChannel = undefined; - } - - if(this.rtcPeerConnection) { - this.rtcPeerConnection.close(); - this.rtcPeerConnection = undefined; - } - - this._ice_use_cache = true; - this._ice_cache = []; - - this.connection.client.update_voice_status(undefined); - } - - private _ice_use_cache: boolean = true; - private _ice_cache: any[] = []; - handleControlPacket(json) { - if(json["request"] === "answer") { - const session_description = new RTCSessionDescription(json["msg"]); - log.info(LogCategory.VOICE, tr("Received answer to our offer. Answer: %o"), session_description); - this.rtcPeerConnection.setRemoteDescription(session_description).then(() => { - log.info(LogCategory.VOICE, tr("Answer applied successfully. Applying ICE candidates (%d)."), this._ice_cache.length); - this._ice_use_cache = false; - for(let msg of this._ice_cache) { - this.rtcPeerConnection.addIceCandidate(new RTCIceCandidate(msg)).catch(error => { - log.info(LogCategory.VOICE, tr("Failed to add remote cached ice candidate %s: %o"), msg, error); + let free_slot = 0; + for(let index = 0; index < this.entries.length; index++) { + if(this.entries[index].owner == clientId) { + this.entries[index].last_access = Date.now(); + if(this.entries[index].instance.initialized()) + resolve(this.entries[index].instance); + else { + this.entries[index].instance.initialise().then((flag) => { + //TODO test success flag + this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject); + }).catch(error => { + log.error(LogCategory.VOICE, tr("Could not initialize codec!\nError: %o"), error); + reject(typeof(error) === 'string' ? error : tr("Could not initialize codec!")); }); } - this._ice_cache = []; - }).catch(error => { - log.info(LogCategory.VOICE, tr("Failed to apply remote description: %o"), error); //FIXME error handling! - }); - } else if(json["request"] === "ice") { - if(!this._ice_use_cache) { - log.info(LogCategory.VOICE, tr("Add remote ice! (%o)"), json["msg"]); - this.rtcPeerConnection.addIceCandidate(new RTCIceCandidate(json["msg"])).catch(error => { - log.info(LogCategory.VOICE, tr("Failed to add remote ice candidate %s: %o"), json["msg"], error); - }); - } else { - log.info(LogCategory.VOICE, tr("Cache remote ice! (%o)"), json["msg"]); - this._ice_cache.push(json["msg"]); - } - } else if(json["request"] == "status") { - if(json["state"] == "failed") { - const chandler = this.connection.client; - chandler.log.log(log.server.Type.CONNECTION_VOICE_SETUP_FAILED, { - reason: json["reason"], - reconnect_delay: json["allow_reconnect"] ? 1 : 0 - }); - log.error(LogCategory.NETWORKING, tr("Failed to setup voice bridge (%s). Allow reconnect: %s"), json["reason"], json["allow_reconnect"]); - if(json["allow_reconnect"] == true) { - this.start_rtc_session(); - } - //TODO handle fail specially when its not allowed to reconnect + return; + } else if(this.entries[index].owner == 0) { + free_slot = index; } } + + if(!create) { + resolve(undefined); + return; + } + + if(free_slot == 0){ + free_slot = this.entries.length; + let entry = new CacheEntry(); + entry.instance = new CodecWrapperWorker(this.type); + this.entries.push(entry); + } + this.entries[free_slot].owner = clientId; + this.entries[free_slot].last_access = new Date().getTime(); + this.entries[free_slot].instance.on_encoded_data = callback_encoded; + if(this.entries[free_slot].instance.initialized()) + this.entries[free_slot].instance.reset(); + else { + this.ownCodec(clientId, callback_encoded, false).then(resolve).catch(reject); + return; + } + resolve(this.entries[free_slot].instance); + }); + } + + releaseCodec(clientId: number) { + for(let index = 0; index < this.entries.length; index++) + if(this.entries[index].owner == clientId) this.entries[index].owner = 0; + } + + constructor(index: number, name: string, type: CodecType){ + this.codecIndex = index; + this.name = name; + this.type = type; + + this._supported = this.type !== undefined && codec_supported(this.type); + } + } +} + +export enum VoiceEncodeType { + JS_ENCODE, + NATIVE_ENCODE +} + +export class VoiceConnection extends AbstractVoiceConnection { + readonly connection: ServerConnection; + + rtcPeerConnection: RTCPeerConnection; + dataChannel: RTCDataChannel; + + private _type: VoiceEncodeType = VoiceEncodeType.NATIVE_ENCODE; + + /* + * To ensure we're not sending any audio because the settings activates the input, + * we self mute the audio stream + */ + local_audio_mute: GainNode; + local_audio_stream: MediaStreamAudioDestinationNode; + + static codec_pool: codec.CodecPool[]; + + static codecSupported(type: number) : boolean { + return this.codec_pool && this.codec_pool.length > type && this.codec_pool[type].supported(); + } + + private voice_packet_id: number = 0; + private chunkVPacketId: number = 0; + private send_task: NodeJS.Timer; + + private _audio_source: RecorderProfile; + private _audio_clients: VoiceClientController[] = []; + + private _encoder_codec: number = 5; + + constructor(connection: ServerConnection) { + super(connection); + this.connection = connection; + + this._type = settings.static_global("voice_connection_type", this._type); + } + + destroy() { + clearInterval(this.send_task); + this.drop_rtp_session(); + this.acquire_voice_recorder(undefined, true).catch(error => { + log.warn(LogCategory.VOICE, tr("Failed to release voice recorder: %o"), error); + }).then(() => { + for(const client of this._audio_clients) { + client.abort_replay(); + client.callback_playback = undefined; + client.callback_state_changed = undefined; + client.callback_stopped = undefined; } + this._audio_clients = undefined; + this._audio_source = undefined; + }); + } - private on_local_ice_candidate(event: RTCPeerConnectionIceEvent) { - if (event) { - //if(event.candidate && event.candidate.protocol !== "udp") - // return; + static native_encoding_supported() : boolean { + const context = window.webkitAudioContext || window.AudioContext; + if(!context) + return false; - log.info(LogCategory.VOICE, tr("Gathered local ice candidate %o."), event.candidate); - if(event.candidate) { - this.connection.sendData(JSON.stringify({ - type: 'WebRTC', - request: "ice", - msg: event.candidate, - })); - } else { - this.connection.sendData(JSON.stringify({ - type: 'WebRTC', - request: "ice_finish" - })); + if(!context.prototype.createMediaStreamDestination) + return false; //Required, but not available within edge + + return true; + } + + static javascript_encoding_supported() : boolean { + if(!window.RTCPeerConnection) + return false; + if(!RTCPeerConnection.prototype.createDataChannel) + return false; + return true; + } + + current_encoding_supported() : boolean { + switch (this._type) { + case VoiceEncodeType.JS_ENCODE: + return VoiceConnection.javascript_encoding_supported(); + case VoiceEncodeType.NATIVE_ENCODE: + return VoiceConnection.native_encoding_supported(); + } + return false; + } + + private setup_native() { + log.info(LogCategory.VOICE, tr("Setting up native voice stream!")); + if(!VoiceConnection.native_encoding_supported()) { + log.warn(LogCategory.VOICE, tr("Native codec isn't supported!")); + return; + } + + if(!this.local_audio_stream) { + this.local_audio_stream = aplayer.context().createMediaStreamDestination(); + } + if(!this.local_audio_mute) { + this.local_audio_mute = aplayer.context().createGain(); + this.local_audio_mute.connect(this.local_audio_stream); + this.local_audio_mute.gain.value = 1; + } + } + + private setup_js() { + if(!VoiceConnection.javascript_encoding_supported()) return; + if(!this.send_task) + this.send_task = setInterval(this.send_next_voice_packet.bind(this), 20); /* send all 20ms out voice packets */ + } + + async acquire_voice_recorder(recorder: RecorderProfile | undefined, enforce?: boolean) { + if(this._audio_source === recorder && !enforce) + return; + + if(recorder) + await recorder.unmount(); + + if(this._audio_source) + await this._audio_source.unmount(); + + this.handle_local_voice_ended(); + this._audio_source = recorder; + + if(recorder) { + recorder.current_handler = this.connection.client; + + recorder.callback_unmount = this.on_recorder_yield.bind(this); + recorder.callback_start = this.handle_local_voice_started.bind(this); + recorder.callback_stop = this.handle_local_voice_ended.bind(this); + + if(this._type == VoiceEncodeType.NATIVE_ENCODE) { + if(!this.local_audio_stream) + this.setup_native(); /* requires initialized audio */ + + await recorder.input.set_consumer({ + type: InputConsumerType.NODE, + callback_node: node => { + if(!this.local_audio_stream || !this.local_audio_mute) + return; + + node.connect(this.local_audio_mute); + }, + callback_disconnect: node => { + if(!this.local_audio_mute) + return; + + node.disconnect(this.local_audio_mute); } - } - } - - private on_local_offer_created(localSession) { - log.info(LogCategory.VOICE, tr("Local offer created. Setting up local description. (%o)"), localSession); - this.rtcPeerConnection.setLocalDescription(localSession).then(() => { - log.info(LogCategory.VOICE, tr("Offer applied successfully. Sending offer to server.")); - this.connection.sendData(JSON.stringify({type: 'WebRTC', request: "create", msg: localSession})); - }).catch(error => { - log.info(LogCategory.VOICE, tr("Failed to apply local description: %o"), error); - //FIXME error handling - }); - } - - private on_data_channel(channel) { - log.info(LogCategory.VOICE, tr("Got new data channel! (%s)"), this.dataChannel.readyState); - - this.connection.client.update_voice_status(); - } - - private on_data_channel_message(message: MessageEvent) { - const chandler = this.connection.client; - if(chandler.client_status.output_muted) /* we dont need to do anything with sound playback when we're not listening to it */ - return; - - let bin = new Uint8Array(message.data); - let clientId = bin[2] << 8 | bin[3]; - let packetId = bin[0] << 8 | bin[1]; - let codec = bin[4]; - //log.info(LogCategory.VOICE, "Client id " + clientId + " PacketID " + packetId + " Codec: " + codec); - let client = this.find_client(clientId); - if(!client) { - log.error(LogCategory.VOICE, tr("Having voice from unknown audio client? (ClientID: %o)"), clientId); - return; - } - - let codec_pool = VoiceConnection.codec_pool[codec]; - if(!codec_pool) { - log.error(LogCategory.VOICE, tr("Could not playback codec %o"), codec); - return; - } - - let encodedData; - if(message.data.subarray) - encodedData = message.data.subarray(5); - else encodedData = new Uint8Array(message.data, 5); - - if(encodedData.length == 0) { - client.stopAudio(); - codec_pool.releaseCodec(clientId); - } else { - codec_pool.ownCodec(clientId, e => this.handleEncodedVoicePacket(e, codec), true) - .then(decoder => decoder.decodeSamples(client.get_codec_cache(codec), encodedData)) - .then(buffer => client.playback_buffer(buffer)).catch(error => { - log.error(LogCategory.VOICE, tr("Could not playback client's (%o) audio (%o)"), clientId, error); - if(error instanceof Error) - log.error(LogCategory.VOICE, error.stack); - }); - } - } - - private handle_local_voice(data: AudioBuffer, head: boolean) { - const chandler = this.connection.client; - if(!chandler.connected) - return false; - - if(chandler.client_status.input_muted) - return false; - - if(head) - this.chunkVPacketId = 0; - - let client = this.find_client(chandler.clientId); - if(!client) { - log.error(LogCategory.VOICE, tr("Tried to send voice data, but local client hasn't a voice client handle")); - return; - } - - const codec = this._encoder_codec; - VoiceConnection.codec_pool[codec] - .ownCodec(chandler.getClientId(), e => this.handleEncodedVoicePacket(e, codec), true) - .then(encoder => encoder.encodeSamples(client.get_codec_cache(codec), data)); - } - - private handle_local_voice_ended() { - const chandler = this.connection.client; - const ch = chandler.getClient(); - if(ch) ch.speaking = false; - - if(!chandler.connected) - return false; - if(chandler.client_status.input_muted) - return false; - log.info(LogCategory.VOICE, tr("Local voice ended")); - - if(this.dataChannel && this._encoder_codec >= 0) - this.send_voice_packet(new Uint8Array(0), this._encoder_codec); - } - - private handle_local_voice_started() { - const chandler = this.connection.client; - if(chandler.client_status.input_muted) { - /* evail hack due to the settings :D */ - log.warn(LogCategory.VOICE, tr("Received local voice started event, even thou we're muted! Do not send any voice.")); - if(this.local_audio_mute) - this.local_audio_mute.gain.value = 0; - return; - } - if(this.local_audio_mute) - this.local_audio_mute.gain.value = 1; - log.info(LogCategory.VOICE, tr("Local voice started")); - - const ch = chandler.getClient(); - if(ch) ch.speaking = true; - } - - private on_recorder_yield() { - log.info(LogCategory.VOICE, "Lost recorder!"); - this._audio_source = undefined; - this.acquire_voice_recorder(undefined, true); /* we can ignore the promise because we should finish this directly */ - } - - connected(): boolean { - return typeof(this.dataChannel) !== "undefined" && this.dataChannel.readyState === "open"; - } - - voice_recorder(): RecorderProfile { - return this._audio_source; - } - - available_clients(): connection.voice.VoiceClient[] { - return this._audio_clients; - } - - find_client(client_id: number) : audio.js.VoiceClientController | undefined { - for(const client of this._audio_clients) - if(client.client_id === client_id) - return client; - return undefined; - } - - unregister_client(client: connection.voice.VoiceClient): Promise { - if(!(client instanceof audio.js.VoiceClientController)) - throw "Invalid client type"; - - this._audio_clients.remove(client); - return Promise.resolve(); - } - - register_client(client_id: number): connection.voice.VoiceClient { - const client = new audio.js.VoiceClientController(client_id); - this._audio_clients.push(client); - return client; - } - - decoding_supported(codec: number): boolean { - return VoiceConnection.codecSupported(codec); - } - - encoding_supported(codec: number): boolean { - return VoiceConnection.codecSupported(codec); - } - - get_encoder_codec(): number { - return this._encoder_codec; - } - - set_encoder_codec(codec: number) { - this._encoder_codec = codec; + } as NodeInputConsumer); + } else { + await recorder.input.set_consumer({ + type: InputConsumerType.CALLBACK, + callback_audio: buffer => this.handle_local_voice(buffer, false) + } as CallbackInputConsumer); } } + this.connection.client.update_voice_status(undefined); + } + + get_encoder_type() : VoiceEncodeType { return this._type; } + set_encoder_type(target: VoiceEncodeType) { + if(target == this._type) return; + this._type = target; + + if(this._type == VoiceEncodeType.NATIVE_ENCODE) + this.setup_native(); + else + this.setup_js(); + this.start_rtc_session(); + } + + voice_playback_support() : boolean { + return this.dataChannel && this.dataChannel.readyState == "open"; + } + + voice_send_support() : boolean { + if(this._type == VoiceEncodeType.NATIVE_ENCODE) + return VoiceConnection.native_encoding_supported() && this.rtcPeerConnection.getLocalStreams().length > 0; + else + return this.voice_playback_support(); + } + + private voice_send_queue: {data: Uint8Array, codec: number}[] = []; + handleEncodedVoicePacket(data: Uint8Array, codec: number){ + this.voice_send_queue.push({data: data, codec: codec}); + } + + private send_next_voice_packet() { + const buffer = this.voice_send_queue.pop_front(); + if(!buffer) + return; + this.send_voice_packet(buffer.data, buffer.codec); + } + + send_voice_packet(encoded_data: Uint8Array, codec: number) { + if(this.dataChannel) { + this.voice_packet_id++; + if(this.voice_packet_id > 65535) + this.voice_packet_id = 0; + + let packet = new Uint8Array(encoded_data.byteLength + 5); + packet[0] = this.chunkVPacketId++ < 5 ? 1 : 0; //Flag header + packet[1] = 0; //Flag fragmented + packet[2] = (this.voice_packet_id >> 8) & 0xFF; //HIGHT (voiceID) + packet[3] = (this.voice_packet_id >> 0) & 0xFF; //LOW (voiceID) + packet[4] = codec; //Codec + packet.set(encoded_data, 5); + try { + this.dataChannel.send(packet); + } catch (error) { + log.warn(LogCategory.VOICE, tr("Failed to send voice packet. Error: %o"), error); + } + } else { + log.warn(LogCategory.VOICE, tr("Could not transfer audio (not connected)")); + } + } + + private _audio_player_waiting = false; + start_rtc_session() { + if(!aplayer.initialized()) { + log.info(LogCategory.VOICE, tr("Audio player isn't initialized yet. Waiting for gesture.")); + if(!this._audio_player_waiting) { + this._audio_player_waiting = true; + aplayer.on_ready(() => this.start_rtc_session()); + } + return; + } + + if(!this.current_encoding_supported()) + return false; + + if(this._type == VoiceEncodeType.NATIVE_ENCODE) + this.setup_native(); + else + this.setup_js(); + + this.drop_rtp_session(); + this._ice_use_cache = true; + + + let config: RTCConfiguration = {}; + config.iceServers = []; + config.iceServers.push({ urls: 'stun:stun.l.google.com:19302' }); + this.rtcPeerConnection = new RTCPeerConnection(config); + const dataChannelConfig = { ordered: true, maxRetransmits: 0 }; + + this.dataChannel = this.rtcPeerConnection.createDataChannel('main', dataChannelConfig); + this.dataChannel.onmessage = this.on_data_channel_message.bind(this); + this.dataChannel.onopen = this.on_data_channel.bind(this); + this.dataChannel.binaryType = "arraybuffer"; + + let sdpConstraints : RTCOfferOptions = {}; + sdpConstraints.offerToReceiveAudio = this._type == VoiceEncodeType.NATIVE_ENCODE; + sdpConstraints.offerToReceiveVideo = false; + sdpConstraints.voiceActivityDetection = true; + + this.rtcPeerConnection.onicecandidate = this.on_local_ice_candidate.bind(this); + if(this.local_audio_stream) { //May a typecheck? + this.rtcPeerConnection.addStream(this.local_audio_stream.stream); + log.info(LogCategory.VOICE, tr("Adding native audio stream (%o)!"), this.local_audio_stream.stream); + } + + this.rtcPeerConnection.createOffer(sdpConstraints) + .then(offer => this.on_local_offer_created(offer)) + .catch(error => { + log.error(LogCategory.VOICE, tr("Could not create ice offer! error: %o"), error); + }); + } + + drop_rtp_session() { + if(this.dataChannel) { + this.dataChannel.close(); + this.dataChannel = undefined; + } + + if(this.rtcPeerConnection) { + this.rtcPeerConnection.close(); + this.rtcPeerConnection = undefined; + } + + this._ice_use_cache = true; + this._ice_cache = []; + + this.connection.client.update_voice_status(undefined); + } + + private _ice_use_cache: boolean = true; + private _ice_cache: any[] = []; + handleControlPacket(json) { + if(json["request"] === "answer") { + const session_description = new RTCSessionDescription(json["msg"]); + log.info(LogCategory.VOICE, tr("Received answer to our offer. Answer: %o"), session_description); + this.rtcPeerConnection.setRemoteDescription(session_description).then(() => { + log.info(LogCategory.VOICE, tr("Answer applied successfully. Applying ICE candidates (%d)."), this._ice_cache.length); + this._ice_use_cache = false; + for(let msg of this._ice_cache) { + this.rtcPeerConnection.addIceCandidate(new RTCIceCandidate(msg)).catch(error => { + log.info(LogCategory.VOICE, tr("Failed to add remote cached ice candidate %s: %o"), msg, error); + }); + } + this._ice_cache = []; + }).catch(error => { + log.info(LogCategory.VOICE, tr("Failed to apply remote description: %o"), error); //FIXME error handling! + }); + } else if(json["request"] === "ice") { + if(!this._ice_use_cache) { + log.info(LogCategory.VOICE, tr("Add remote ice! (%o)"), json["msg"]); + this.rtcPeerConnection.addIceCandidate(new RTCIceCandidate(json["msg"])).catch(error => { + log.info(LogCategory.VOICE, tr("Failed to add remote ice candidate %s: %o"), json["msg"], error); + }); + } else { + log.info(LogCategory.VOICE, tr("Cache remote ice! (%o)"), json["msg"]); + this._ice_cache.push(json["msg"]); + } + } else if(json["request"] == "status") { + if(json["state"] == "failed") { + const chandler = this.connection.client; + chandler.log.log(elog.Type.CONNECTION_VOICE_SETUP_FAILED, { + reason: json["reason"], + reconnect_delay: json["allow_reconnect"] ? 1 : 0 + }); + log.error(LogCategory.NETWORKING, tr("Failed to setup voice bridge (%s). Allow reconnect: %s"), json["reason"], json["allow_reconnect"]); + if(json["allow_reconnect"] == true) { + this.start_rtc_session(); + } + //TODO handle fail specially when its not allowed to reconnect + } + } + } + + private on_local_ice_candidate(event: RTCPeerConnectionIceEvent) { + if (event) { + //if(event.candidate && event.candidate.protocol !== "udp") + // return; + + log.info(LogCategory.VOICE, tr("Gathered local ice candidate %o."), event.candidate); + if(event.candidate) { + this.connection.sendData(JSON.stringify({ + type: 'WebRTC', + request: "ice", + msg: event.candidate, + })); + } else { + this.connection.sendData(JSON.stringify({ + type: 'WebRTC', + request: "ice_finish" + })); + } + } + } + + private on_local_offer_created(localSession) { + log.info(LogCategory.VOICE, tr("Local offer created. Setting up local description. (%o)"), localSession); + this.rtcPeerConnection.setLocalDescription(localSession).then(() => { + log.info(LogCategory.VOICE, tr("Offer applied successfully. Sending offer to server.")); + this.connection.sendData(JSON.stringify({type: 'WebRTC', request: "create", msg: localSession})); + }).catch(error => { + log.info(LogCategory.VOICE, tr("Failed to apply local description: %o"), error); + //FIXME error handling + }); + } + + private on_data_channel(channel) { + log.info(LogCategory.VOICE, tr("Got new data channel! (%s)"), this.dataChannel.readyState); + + this.connection.client.update_voice_status(); + } + + private on_data_channel_message(message: MessageEvent) { + const chandler = this.connection.client; + if(chandler.client_status.output_muted) /* we dont need to do anything with sound playback when we're not listening to it */ + return; + + let bin = new Uint8Array(message.data); + let clientId = bin[2] << 8 | bin[3]; + let packetId = bin[0] << 8 | bin[1]; + let codec = bin[4]; + //log.info(LogCategory.VOICE, "Client id " + clientId + " PacketID " + packetId + " Codec: " + codec); + let client = this.find_client(clientId); + if(!client) { + log.error(LogCategory.VOICE, tr("Having voice from unknown audio client? (ClientID: %o)"), clientId); + return; + } + + let codec_pool = VoiceConnection.codec_pool[codec]; + if(!codec_pool) { + log.error(LogCategory.VOICE, tr("Could not playback codec %o"), codec); + return; + } + + let encodedData; + if(message.data.subarray) + encodedData = message.data.subarray(5); + else encodedData = new Uint8Array(message.data, 5); + + if(encodedData.length == 0) { + client.stopAudio(); + codec_pool.releaseCodec(clientId); + } else { + codec_pool.ownCodec(clientId, e => this.handleEncodedVoicePacket(e, codec), true) + .then(decoder => decoder.decodeSamples(client.get_codec_cache(codec), encodedData)) + .then(buffer => client.playback_buffer(buffer)).catch(error => { + log.error(LogCategory.VOICE, tr("Could not playback client's (%o) audio (%o)"), clientId, error); + if(error instanceof Error) + log.error(LogCategory.VOICE, error.stack); + }); + } + } + + private handle_local_voice(data: AudioBuffer, head: boolean) { + const chandler = this.connection.client; + if(!chandler.connected) + return false; + + if(chandler.client_status.input_muted) + return false; + + if(head) + this.chunkVPacketId = 0; + + let client = this.find_client(chandler.clientId); + if(!client) { + log.error(LogCategory.VOICE, tr("Tried to send voice data, but local client hasn't a voice client handle")); + return; + } + + const codec = this._encoder_codec; + VoiceConnection.codec_pool[codec] + .ownCodec(chandler.getClientId(), e => this.handleEncodedVoicePacket(e, codec), true) + .then(encoder => encoder.encodeSamples(client.get_codec_cache(codec), data)); + } + + private handle_local_voice_ended() { + const chandler = this.connection.client; + const ch = chandler.getClient(); + if(ch) ch.speaking = false; + + if(!chandler.connected) + return false; + if(chandler.client_status.input_muted) + return false; + log.info(LogCategory.VOICE, tr("Local voice ended")); + + if(this.dataChannel && this._encoder_codec >= 0) + this.send_voice_packet(new Uint8Array(0), this._encoder_codec); + } + + private handle_local_voice_started() { + const chandler = this.connection.client; + if(chandler.client_status.input_muted) { + /* evail hack due to the settings :D */ + log.warn(LogCategory.VOICE, tr("Received local voice started event, even thou we're muted! Do not send any voice.")); + if(this.local_audio_mute) + this.local_audio_mute.gain.value = 0; + return; + } + if(this.local_audio_mute) + this.local_audio_mute.gain.value = 1; + log.info(LogCategory.VOICE, tr("Local voice started")); + + const ch = chandler.getClient(); + if(ch) ch.speaking = true; + } + + private on_recorder_yield() { + log.info(LogCategory.VOICE, "Lost recorder!"); + this._audio_source = undefined; + this.acquire_voice_recorder(undefined, true); /* we can ignore the promise because we should finish this directly */ + } + + connected(): boolean { + return typeof(this.dataChannel) !== "undefined" && this.dataChannel.readyState === "open"; + } + + voice_recorder(): RecorderProfile { + return this._audio_source; + } + + available_clients(): VoiceClient[] { + return this._audio_clients; + } + + find_client(client_id: number) : VoiceClientController | undefined { + for(const client of this._audio_clients) + if(client.client_id === client_id) + return client; + return undefined; + } + + unregister_client(client: VoiceClient): Promise { + if(!(client instanceof VoiceClientController)) + throw "Invalid client type"; + + this._audio_clients.remove(client); + return Promise.resolve(); + } + + register_client(client_id: number): VoiceClient { + const client = new VoiceClientController(client_id); + this._audio_clients.push(client); + return client; + } + + decoding_supported(codec: number): boolean { + return VoiceConnection.codecSupported(codec); + } + + encoding_supported(codec: number): boolean { + return VoiceConnection.codecSupported(codec); + } + + get_encoder_codec(): number { + return this._encoder_codec; + } + + set_encoder_codec(codec: number) { + this._encoder_codec = codec; } } /* funny fact that typescript dosn't find this */ -interface RTCPeerConnection { - addStream(stream: MediaStream): void; - getLocalStreams(): MediaStream[]; - getStreamById(streamId: string): MediaStream | null; - removeStream(stream: MediaStream): void; - createOffer(successCallback?: RTCSessionDescriptionCallback, failureCallback?: RTCPeerConnectionErrorCallback, options?: RTCOfferOptions): Promise; +declare global { + interface RTCPeerConnection { + addStream(stream: MediaStream): void; + getLocalStreams(): MediaStream[]; + getStreamById(streamId: string): MediaStream | null; + removeStream(stream: MediaStream): void; + createOffer(successCallback?: RTCSessionDescriptionCallback, failureCallback?: RTCPeerConnectionErrorCallback, options?: RTCOfferOptions): Promise; + } } loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { priority: 10, function: async () => { - audio.player.on_ready(() => { + aplayer.on_ready(() => { log.info(LogCategory.VOICE, tr("Initializing voice handler after AudioController has been initialized!")); - audio.js.VoiceConnection.codec_pool = [ - new audio.js.codec.CodecPool(0, tr("Speex Narrowband"), CodecType.SPEEX_NARROWBAND), - new audio.js.codec.CodecPool(1, tr("Speex Wideband"), CodecType.SPEEX_WIDEBAND), - new audio.js.codec.CodecPool(2, tr("Speex Ultra Wideband"), CodecType.SPEEX_ULTRA_WIDEBAND), - new audio.js.codec.CodecPool(3, tr("CELT Mono"), CodecType.CELT_MONO), - new audio.js.codec.CodecPool(4, tr("Opus Voice"), CodecType.OPUS_VOICE), - new audio.js.codec.CodecPool(5, tr("Opus Music"), CodecType.OPUS_MUSIC) + VoiceConnection.codec_pool = [ + new codec.CodecPool(0, tr("Speex Narrowband"), CodecType.SPEEX_NARROWBAND), + new codec.CodecPool(1, tr("Speex Wideband"), CodecType.SPEEX_WIDEBAND), + new codec.CodecPool(2, tr("Speex Ultra Wideband"), CodecType.SPEEX_ULTRA_WIDEBAND), + new codec.CodecPool(3, tr("CELT Mono"), CodecType.CELT_MONO), + new codec.CodecPool(4, tr("Opus Voice"), CodecType.OPUS_VOICE), + new codec.CodecPool(5, tr("Opus Music"), CodecType.OPUS_MUSIC) ]; - audio.js.VoiceConnection.codec_pool[4].initialize(2); - audio.js.VoiceConnection.codec_pool[5].initialize(2); + VoiceConnection.codec_pool[4].initialize(2); + VoiceConnection.codec_pool[5].initialize(2); }); }, name: "registering codec initialisation" diff --git a/webpack.config.js b/webpack.config.js index ca37ea76..34b9cd30 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,16 +1,28 @@ const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const CircularDependencyPlugin = require('circular-dependency-plugin') const isDevelopment = process.env.NODE_ENV === 'development'; module.exports = { - entry: './shared/js/main.ts', + entry: { + //"shared-app": "./shared/js/main.ts" + "shared-app": "./web/js/index.ts" + }, devtool: 'inline-source-map', mode: "development", plugins: [ new MiniCssExtractPlugin({ filename: isDevelopment ? '[name].css' : '[name].[hash].css', chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }), + /* + new CircularDependencyPlugin({ + //exclude: /a\.js|node_modules/, + failOnError: true, + allowAsyncCycles: false, + cwd: process.cwd(), }) + */ ], module: { rules: [ @@ -42,7 +54,7 @@ module.exports = { { loader: 'ts-loader', options: { - //transpileOnly: true + transpileOnly: true } } ] @@ -54,11 +66,17 @@ module.exports = { alias: { "tc-shared": path.resolve(__dirname, "shared/js"), "tc-backend": path.resolve(__dirname, "web/js") - } + //"tc-backend": path.resolve(__dirname, "shared/backend.d"), + }, + }, + externals: { + "tc-loader": "umd loader" }, output: { - filename: 'bundle.js', + filename: 'shared-app.js', path: path.resolve(__dirname, 'dist'), + libraryTarget: "umd", + library: "shared" }, optimization: { /* From 8f80ba42e8e67336d8e55add14e7e6b462fa35b1 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 30 Mar 2020 18:16:03 +0200 Subject: [PATCH 07/23] Updated the opus build and enforced as emscripten web build --- asm/.gitignore | 4 +++- asm/CMakeLists.txt | 4 ++-- asm/build.sh | 20 ++++++++++++++++++++ asm/libraries/opus | 2 +- asm/make_opus.sh | 35 ++++++++++++++++++++++++++++------- 5 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 asm/build.sh diff --git a/asm/.gitignore b/asm/.gitignore index c9ac95d3..706d8e4c 100644 --- a/asm/.gitignore +++ b/asm/.gitignore @@ -1,2 +1,4 @@ generated/ -build/ \ No newline at end of file +build_/ +libraries/opus/build_ +libraries/opus/out \ No newline at end of file diff --git a/asm/CMakeLists.txt b/asm/CMakeLists.txt index 9b17feb1..23aca041 100644 --- a/asm/CMakeLists.txt +++ b/asm/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_C_COMPILER "emcc") set(CMAKE_C_LINK_EXECUTABLE "emcc") set(CMAKE_CXX_FLAGS "-O3 --llvm-lto 1 --memory-init-file 0 -s WASM=1 -s ASSERTIONS=1") # -s ALLOW_MEMORY_GROWTH=1 -O3 set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]'") # +set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]' -s ENVIRONMENT='web'") # #add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/generated/") @@ -18,4 +18,4 @@ include_directories(libraries/opus/include/) add_definitions(-DLTM_DESC) add_executable(TeaWeb-Worker-Codec-Opus src/opus.cpp) -target_link_libraries(TeaWeb-Worker-Codec-Opus ${CMAKE_CURRENT_SOURCE_DIR}/libraries/opus/.libs/libopus.a) +target_link_libraries(TeaWeb-Worker-Codec-Opus ${CMAKE_CURRENT_SOURCE_DIR}/libraries/opus/out/lib/libopus.a) diff --git a/asm/build.sh b/asm/build.sh new file mode 100644 index 00000000..b6365d56 --- /dev/null +++ b/asm/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +[[ ! -d libraries/opus/out/ ]] && { echo "Missing opus build. Please build it before!"; exit 1; } +[[ ! -f libraries/opus/out/lib/libopus.a ]] && { echo "Missing opus static library. Please unsure your opus build was successfull."; exit 1; } + +[[ -d build_ ]] && { + rm -r build_ || { echo "failed to remove old build directory"; exit 1; } +} +mkdir build_ || exit 1 +cd build_ || exit 1 + +emcmake cmake .. || { + echo "Failed to execute cmake" + exit 1 +} + +emmake make || { + echo "Failed to build file" + exit 1 +} \ No newline at end of file diff --git a/asm/libraries/opus b/asm/libraries/opus index 655cc54c..923bebde 160000 --- a/asm/libraries/opus +++ b/asm/libraries/opus @@ -1 +1 @@ -Subproject commit 655cc54c564b84ef2827f0b2152ce3811046201e +Subproject commit 923bebde197f42ba8e55cb055dce3ff22bbea54d diff --git a/asm/make_opus.sh b/asm/make_opus.sh index b3efd4bf..d2b64667 100755 --- a/asm/make_opus.sh +++ b/asm/make_opus.sh @@ -1,11 +1,32 @@ #!/usr/bin/env bash -base_dir=`pwd` -cd "$(dirname $0)/libraries/opus/" +cd "$(dirname $0)/libraries/opus/" || { echo "Failed to enter the opus directory."; exit 1; } -git checkout v1.1.2 -./autogen.sh -emconfigure ./configure --disable-extra-programs --disable-doc --disable-rtcd -emmake make +[[ -d build_ ]] && { + rm -r build_ || { echo "failed to remove old build directory"; exit 1; } +} +mkdir build_ || exit 1 +cd build_ || exit 1 -cd ${base_dir} \ No newline at end of file +# Native SIMD isn't supported yet by most browsers (only experimental) +# So there is no need to build with that, it will make stuff even worse +simd_flags="-DOPUS_X86_MAY_HAVE_AVX=OFF -DOPUS_X86_MAY_HAVE_SSE4_1=OFF -DOPUS_X86_MAY_HAVE_SSE2=OFF -DOPUS_X86_MAY_HAVE_SSE=OFF" +emcmake cmake .. -DCMAKE_INSTALL_PREFIX="$(pwd)/../out/" -DOPUS_STACK_PROTECTOR=OFF ${simd_flags} || { + echo "failed to execute cmake" + exit 1 +} + +emmake make || { + echo "failed to build opus" + exit 1 +} +emmake make install || { + echo "failed to \"install\" opus" + exit 1 +} + +# Old: +#git checkout v1.1.2 +#./autogen.sh +#emconfigure ./configure --disable-extra-programs --disable-doc --disable-rtcd +#emmake make \ No newline at end of file From c2fa1badcb6a32db656df9bb8525d9c2c1435612 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 00:43:14 +0200 Subject: [PATCH 08/23] Using correct target --- asm/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asm/CMakeLists.txt b/asm/CMakeLists.txt index 23aca041..0e25ff90 100644 --- a/asm/CMakeLists.txt +++ b/asm/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_C_COMPILER "emcc") set(CMAKE_C_LINK_EXECUTABLE "emcc") set(CMAKE_CXX_FLAGS "-O3 --llvm-lto 1 --memory-init-file 0 -s WASM=1 -s ASSERTIONS=1") # -s ALLOW_MEMORY_GROWTH=1 -O3 set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]' -s ENVIRONMENT='web'") # +set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]' -s ENVIRONMENT='worker'") # #add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/generated/") From 956f778c01309c0aafc7f53a56c22fcdd2c7eed2 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 01:27:59 +0200 Subject: [PATCH 09/23] Made the opus replay working again --- asm/CMakeLists.txt | 2 +- asm/init.js | 2 + asm/src/opus.cpp | 9 +- file.ts | 192 ++- loader/app/app.ts | 110 +- loader/app/certaccept.ts | 2 +- loader/app/index.ts | 2 +- loader/app/{ => loader}/loader.ts | 383 +---- loader/app/loader/script_loader.ts | 121 ++ loader/app/loader/style_loader.ts | 142 ++ loader/app/loader/template_loader.ts | 90 ++ loader/app/loader/utils.ts | 65 + loader/exports/loader.d.ts | 6 - loader/webpack.config.js | 4 +- package-lock.json | 1343 +++++++++++++++++ package.json | 11 +- shared/popup/certaccept/js/main.ts | 1 + tsconfig.json | 1 + .../codec/{CodecWrapperRaw.ts => CodecRaw.ts} | 2 +- web/js/codec/CodecWrapperWorker.ts | 313 ++-- web/js/workers/codec/CodecWorker.ts | 188 ++- web/js/workers/codec/OpusCodec.ts | 165 +- web/js/workers/codec/index.ts | 1 + web/js/workers/tsconfig_worker_codec.json | 16 - webpack.config.js | 55 +- webpack/ManifestPlugin.ts | 49 + 26 files changed, 2434 insertions(+), 841 deletions(-) create mode 100644 asm/init.js rename loader/app/{ => loader}/loader.ts (50%) create mode 100644 loader/app/loader/script_loader.ts create mode 100644 loader/app/loader/style_loader.ts create mode 100644 loader/app/loader/template_loader.ts create mode 100644 loader/app/loader/utils.ts rename web/js/codec/{CodecWrapperRaw.ts => CodecRaw.ts} (96%) create mode 100644 web/js/workers/codec/index.ts delete mode 100644 web/js/workers/tsconfig_worker_codec.json create mode 100644 webpack/ManifestPlugin.ts diff --git a/asm/CMakeLists.txt b/asm/CMakeLists.txt index 0e25ff90..f82daa65 100644 --- a/asm/CMakeLists.txt +++ b/asm/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_C_COMPILER "emcc") set(CMAKE_C_LINK_EXECUTABLE "emcc") set(CMAKE_CXX_FLAGS "-O3 --llvm-lto 1 --memory-init-file 0 -s WASM=1 -s ASSERTIONS=1") # -s ALLOW_MEMORY_GROWTH=1 -O3 set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXE_LINKER_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"Pointer_stringify\"]' -s ENVIRONMENT='worker'") # +set(CMAKE_EXE_LINKER_FLAGS "-s EXPORTED_FUNCTIONS='[\"_malloc\", \"_free\"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]' -s ENVIRONMENT='worker' --pre-js ${CMAKE_SOURCE_DIR}/init.js") # #add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/generated/") diff --git a/asm/init.js b/asm/init.js new file mode 100644 index 00000000..48cb420e --- /dev/null +++ b/asm/init.js @@ -0,0 +1,2 @@ +for(const callback of Array.isArray(self.__init_em_module) ? self.__init_em_module : []) + callback(Module); \ No newline at end of file diff --git a/asm/src/opus.cpp b/asm/src/opus.cpp index f10d40de..854ad3e7 100644 --- a/asm/src/opus.cpp +++ b/asm/src/opus.cpp @@ -23,6 +23,7 @@ extern "C" { "Memory allocation has failed" //-7 (OPUS_ALLOC_FAIL) }; + EMSCRIPTEN_KEEPALIVE inline const char* opus_error_message(int error) { error = abs(error); if(error > 0 && error <= 7) return opus_errors[error - 1]; @@ -59,11 +60,13 @@ extern "C" { INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_COMPLEXITY(1)); //INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_BITRATE(4740)); + /* //This method is obsolete! EM_ASM( printMessageToServerTab('Encoder initialized!'); printMessageToServerTab(' Comprexity: 1'); printMessageToServerTab(' Bitrate: 4740'); ); + */ return true; } @@ -98,9 +101,11 @@ extern "C" { auto result = opus_encode_float(handle->encoder, (float*) buffer, length / handle->channelCount, buffer, maxLength); if(result < 0) return result; auto end = currentMillies(); + /* //This message is obsolete EM_ASM({ - printMessageToServerTab("codec_opus_encode(...) tooks " + $0 + "ms to execute!"); - }, end - begin); + printMessageToServerTab("codec_opus_encode(...) tooks " + $0 + "ms to execute!"); + }, end - begin); + */ return result; } diff --git a/file.ts b/file.ts index 249d9c91..4086a735 100644 --- a/file.ts +++ b/file.ts @@ -463,7 +463,7 @@ const WEB_APP_FILE_LIST = [ "path": "./", "local-path": "./shared/html/" }, - { /* javascript loader for releases */ + { /* javascript files as manifest.json */ "type": "js", "search-pattern": /.*$/, "build-target": "dev|rel", @@ -471,7 +471,14 @@ const WEB_APP_FILE_LIST = [ "path": "js/", "local-path": "./dist/" }, + { /* loader javascript file */ + "type": "js", + "search-pattern": /.*$/, + "build-target": "dev|rel", + "path": "js/", + "local-path": "./loader/dist/" + }, { /* shared javascript files (WebRTC adapter) */ "type": "js", "search-pattern": /.*\.js$/, @@ -661,41 +668,49 @@ namespace generator { return result.digest("hex"); } + const rreaddir = async p => { + const result = []; + try { + const files = await fs.readdir(p); + for(const file of files) { + const file_path = path.join(p, file); + + const info = await fs.stat(file_path); + if(info.isDirectory()) { + result.push(...await rreaddir(file_path)); + } else { + result.push(file_path); + } + } + } catch(error) { + if(error.code === "ENOENT") + return []; + throw error; + } + return result; + }; + + function file_matches_options(file: ProjectResource, options: SearchOptions) { + if(typeof file["web-only"] === "boolean" && file["web-only"] && options.target !== "web") + return false; + + if(typeof file["client-only"] === "boolean" && file["client-only"] && options.target !== "client") + return false; + + if(typeof file["serve-only"] === "boolean" && file["serve-only"] && !options.serving) + return false; + + if(!file["build-target"].split("|").find(e => e === options.mode)) + return false; + + return !(Array.isArray(file["req-parm"]) && file["req-parm"].find(e => !options.parameter.find(p => p.toLowerCase() === e.toLowerCase()))); + } + export async function search_files(files: ProjectResource[], options: SearchOptions) : Promise { const result: Entry[] = []; - const rreaddir = async p => { - const result = []; - try { - const files = await fs.readdir(p); - for(const file of files) { - const file_path = path.join(p, file); - - const info = await fs.stat(file_path); - if(info.isDirectory()) { - result.push(...await rreaddir(file_path)); - } else { - result.push(file_path); - } - } - } catch(error) { - if(error.code === "ENOENT") - return []; - throw error; - } - return result; - }; - for(const file of files) { - if(typeof file["web-only"] === "boolean" && file["web-only"] && options.target !== "web") - continue; - if(typeof file["client-only"] === "boolean" && file["client-only"] && options.target !== "client") - continue; - if(typeof file["serve-only"] === "boolean" && file["serve-only"] && !options.serving) - continue; - if(!file["build-target"].split("|").find(e => e === options.mode)) - continue; - if(Array.isArray(file["req-parm"]) && file["req-parm"].find(e => !options.parameter.find(p => p.toLowerCase() === e.toLowerCase()))) + if(!file_matches_options(file, options)) continue; const normal_local = path.normalize(path.join(options.source_path, file["local-path"])); @@ -723,23 +738,52 @@ namespace generator { return result; } + + export async function search_http_file(files: ProjectResource[], target_file: string, options: SearchOptions) : Promise { + for(const file of files) { + if(!file_matches_options(file, options)) + continue; + + if(file.path !== "./" && !target_file.startsWith("/" + file.path.replace(/\\/g, "/"))) + continue; + + const normal_local = path.normalize(path.join(options.source_path, file["local-path"])); + const files: string[] = await rreaddir(normal_local); + for(const f of files) { + const local_name = f.substr(normal_local.length); + if(!local_name.match(file["search-pattern"]) && !local_name.replace("\\\\", "/").match(file["search-pattern"])) + continue; + + if(typeof(file["search-exclude"]) !== "undefined" && f.match(file["search-exclude"])) + continue; + + if("/" + path.join(file.path, local_name).replace(/\\/g, "/") === target_file) + return f; + } + } + + return undefined; + } } namespace server { + import SearchOptions = generator.SearchOptions; export type Options = { port: number; php: string; + + search_options: SearchOptions; } const exec: (command: string) => Promise<{ stdout: string, stderr: string }> = util.promisify(cp.exec); - let files: (generator.Entry & { http_path: string; })[] = []; + let files: ProjectResource[] = []; let server: http.Server; let php: string; - export async function launch(_files: generator.Entry[], options: Options) { - //Don't use this check anymore, because we're searching within the PATH variable - //if(!await fs.exists(options.php) || !(await fs.stat(options.php)).isFile()) - // throw "invalid php interpreter (not found)"; + let options: Options; + export async function launch(_files: ProjectResource[], options_: Options) { + options = options_; + files = _files; try { const info = await exec(options.php + " --version"); @@ -763,17 +807,6 @@ namespace server { resolve(); }); }); - - files = _files.map(e =>{ - return { - type: e.type, - name: e.name, - hash: e.hash, - local_path: e.local_path, - target_path: e.target_path, - http_path: "/" + e.target_path.replace(/\\/g, "/") - } - }); } export async function shutdown() { @@ -817,8 +850,8 @@ namespace server { }); } - function serve_file(pathname: string, query: any, response: http.ServerResponse) { - const file = files.find(e => e.http_path === pathname); + async function serve_file(pathname: string, query: any, response: http.ServerResponse) { + const file = await generator.search_http_file(files, pathname, options.search_options); if(!file) { console.log("[SERVER] Client requested unknown file %s", pathname); response.writeHead(404); @@ -827,13 +860,13 @@ namespace server { return; } - let type = mt.lookup(path.extname(file.local_path)) || "text/html"; - console.log("[SERVER] Serving file %s (%s) (%s)", file.target_path, type, file.local_path); - if(path.extname(file.local_path) === ".php") { - serve_php(file.local_path, query, response); + let type = mt.lookup(path.extname(file)) || "text/html"; + console.log("[SERVER] Serving file %s", file, type); + if(path.extname(file) === ".php") { + serve_php(file, query, response); return; } - const fis = fs.createReadStream(file.local_path); + const fis = fs.createReadStream(file); response.writeHead(200, "success", { "Content-Type": type + "; charset=utf-8" @@ -846,23 +879,22 @@ namespace server { fis.on("data", data => response.write(data)); } - function handle_api_request(request: http.IncomingMessage, response: http.ServerResponse, url: url_utils.UrlWithParsedQuery) { + async function handle_api_request(request: http.IncomingMessage, response: http.ServerResponse, url: url_utils.UrlWithParsedQuery) { if(url.query["type"] === "files") { response.writeHead(200, { "info-version": 1 }); response.write("type\thash\tpath\tname\n"); - for(const file of files) - if(file.http_path.endsWith(".php")) - response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.http_path) + "\t" + path.basename(file.http_path, ".php") + ".html" + "\n"); + for(const file of await generator.search_files(files, options.search_options)) + if(file.name.endsWith(".php")) + response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.target_path) + "\t" + path.basename(file.name, ".php") + ".html" + "\n"); else - response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.http_path) + "\t" + path.basename(file.http_path) + "\n"); + response.write(file.type + "\t" + file.hash + "\t" + path.dirname(file.target_path) + "\t" + file.name + "\n"); response.end(); return; } else if(url.query["type"] === "file") { let p = path.join(url.query["path"] as string, url.query["name"] as string).replace(/\\/g, "/"); if(p.endsWith(".html")) { - const np = p.substr(0, p.length - 5) + ".php"; - if(files.find(e => e.http_path == np) && !files.find(e => e.http_path == p)) - p = np; + const np = await generator.search_http_file(files, p, options.search_options); + if(np) p = np; } serve_file(p, url.query, response); return; @@ -1041,17 +1073,16 @@ function php_exe() : string { } async function main_serve(target: "client" | "web", mode: "rel" | "dev", port: number) { - const files = await generator.search_files(target === "client" ? CLIENT_APP_FILE_LIST : WEB_APP_FILE_LIST, { - source_path: __dirname, - parameter: [], - target: target, - mode: mode, - serving: true - }); - - await server.launch(files, { + await server.launch(target === "client" ? CLIENT_APP_FILE_LIST : WEB_APP_FILE_LIST, { port: port, php: php_exe(), + search_options: { + source_path: __dirname, + parameter: [], + target: target, + mode: mode, + serving: true + } }); console.log("Server started on %d", port); @@ -1060,14 +1091,6 @@ async function main_serve(target: "client" | "web", mode: "rel" | "dev", port: n } async function main_develop(node: boolean, target: "client" | "web", port: number, flags: string[]) { - const files = await generator.search_files(target === "client" ? CLIENT_APP_FILE_LIST : WEB_APP_FILE_LIST, { - source_path: __dirname, - parameter: [], - target: target, - mode: "dev", - serving: true - }); - const tscwatcher = new watcher.TSCWatcher(); try { if(flags.indexOf("--no-tsc") == -1) @@ -1079,9 +1102,16 @@ async function main_develop(node: boolean, target: "client" | "web", port: numbe await sasswatcher.start(); try { - await server.launch(files, { + await server.launch(target === "client" ? CLIENT_APP_FILE_LIST : WEB_APP_FILE_LIST, { port: port, php: php_exe(), + search_options: { + source_path: __dirname, + parameter: [], + target: target, + mode: "dev", + serving: true + } }); } catch(error) { console.error("Failed to start server: %o", error instanceof Error ? error.message : error); diff --git a/loader/app/app.ts b/loader/app/app.ts index b1c5d540..4847410a 100644 --- a/loader/app/app.ts +++ b/loader/app/app.ts @@ -1,4 +1,4 @@ -import * as loader from "./loader"; +import * as loader from "./loader/loader"; declare global { interface Window { @@ -8,6 +8,11 @@ declare global { const node_require: typeof require = window.require; +function cache_tag() { + const ui = ui_version(); + return "?_ts=" + (!!ui && ui !== "unknown" ? ui : Date.now()); +} + let _ui_version; export function ui_version() { if(typeof(_ui_version) !== "string") { @@ -22,6 +27,15 @@ export function ui_version() { return _ui_version; } +interface Manifest { + version: number; + + chunks: {[key: string]: { + hash: string, + file: string + }[]}; +} + /* all javascript loaders */ const loader_javascript = { detect_type: async () => { @@ -72,7 +86,7 @@ const loader_javascript = { }, load_scripts: async () => { if(!window.require) { - await loader.load_script(["vendor/jquery/jquery.min.js"]); + await loader.scripts.load(["vendor/jquery/jquery.min.js"], { cache_tag: cache_tag() }); } else { /* loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { @@ -84,45 +98,41 @@ const loader_javascript = { }); */ } - await loader.load_script(["vendor/DOMPurify/purify.min.js"]); + await loader.scripts.load(["vendor/DOMPurify/purify.min.js"], { cache_tag: cache_tag() }); - await loader.load_script("vendor/jsrender/jsrender.min.js"); - await loader.load_scripts([ + await loader.scripts.load("vendor/jsrender/jsrender.min.js", { cache_tag: cache_tag() }); + await loader.scripts.load_multiple([ ["vendor/xbbcode/src/parser.js"], ["vendor/moment/moment.js"], ["vendor/twemoji/twemoji.min.js", ""], /* empty string means not required */ ["vendor/highlight/highlight.pack.js", ""], /* empty string means not required */ ["vendor/remarkable/remarkable.min.js", ""], /* empty string means not required */ ["adapter/adapter-latest.js", "https://webrtc.github.io/adapter/adapter-latest.js"] - ]); - await loader.load_scripts([ - ["vendor/emoji-picker/src/jquery.lsxemojipicker.js"] - ]); + ], { + cache_tag: cache_tag(), + max_parallel_requests: -1 + }); - if(!loader.version().debug_mode) { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "scripts release", - priority: 20, - function: loader_javascript.load_release - }); - } else { - loader.register_task(loader.Stage.JAVASCRIPT, { - name: "scripts debug", - priority: 20, - function: loader_javascript.load_scripts_debug - }); + await loader.scripts.load("vendor/emoji-picker/src/jquery.lsxemojipicker.js", { cache_tag: cache_tag() }); + + let manifest: Manifest; + try { + const response = await fetch("js/manifest.json"); + if(!response.ok) throw response.status + " " + response.statusText; + + manifest = await response.json(); + } catch(error) { + console.error("Failed to load javascript manifest: %o", error); + loader.critical_error("Failed to load manifest.json", error); + throw "failed to load manifest.json"; } - }, - load_scripts_debug: async () => { - await loader.load_scripts(["js/shared-app.js"]) - }, - load_release: async () => { - console.log("Load for release!"); + if(manifest.version !== 1) + throw "invalid manifest version"; - await loader.load_scripts([ - //Load general API's - ["js/client.min.js", "js/client.js"] - ]); + await loader.scripts.load_multiple(manifest.chunks["shared-app"].map(e => "js/" + e.file), { + cache_tag: undefined, + max_parallel_requests: -1 + }); } }; @@ -149,15 +159,20 @@ const loader_webassembly = { const loader_style = { load_style: async () => { - await loader.load_styles([ + const options = { + cache_tag: cache_tag(), + max_parallel_requests: -1 + }; + + await loader.style.load_multiple([ "vendor/xbbcode/src/xbbcode.css" - ]); - await loader.load_styles([ + ], options); + await loader.style.load_multiple([ "vendor/emoji-picker/src/jquery.lsxemojipicker.css" - ]); - await loader.load_styles([ + ], options); + await loader.style.load_multiple([ ["vendor/highlight/styles/darcula.css", ""], /* empty string means not required */ - ]); + ], options); if(loader.version().debug_mode) { await loader_style.load_style_debug(); @@ -167,7 +182,7 @@ const loader_style = { }, load_style_debug: async () => { - await loader.load_styles([ + await loader.style.load_multiple([ "css/static/main.css", "css/static/main-layout.css", "css/static/helptag.css", @@ -218,14 +233,20 @@ const loader_style = { "css/static/htmltags.css", "css/static/hostbanner.css", "css/static/menu-bar.css" - ]); + ], { + cache_tag: cache_tag(), + max_parallel_requests: -1 + }); }, load_style_release: async () => { - await loader.load_styles([ + await loader.style.load_multiple([ "css/static/base.css", "css/static/main.css", - ]); + ], { + cache_tag: cache_tag(), + max_parallel_requests: -1 + }); } }; @@ -313,11 +334,14 @@ loader.register_task(loader.Stage.STYLE, { loader.register_task(loader.Stage.TEMPLATES, { name: "templates", function: async () => { - await loader.load_templates([ + await loader.templates.load_multiple([ "templates.html", "templates/modal/musicmanage.html", "templates/modal/newcomer.html", - ]); + ], { + cache_tag: cache_tag(), + max_parallel_requests: -1 + }); }, priority: 10 }); diff --git a/loader/app/certaccept.ts b/loader/app/certaccept.ts index 7c12bac7..f3da64e9 100644 --- a/loader/app/certaccept.ts +++ b/loader/app/certaccept.ts @@ -1,4 +1,4 @@ -import * as loader from "./loader"; +import * as loader from "./loader/loader"; let is_debug = false; diff --git a/loader/app/index.ts b/loader/app/index.ts index 536f7ee7..403f20b7 100644 --- a/loader/app/index.ts +++ b/loader/app/index.ts @@ -1,5 +1,5 @@ import * as loader from "./app"; -import * as loader_base from "./loader"; +import * as loader_base from "./loader/loader"; export = loader_base; loader.run(); \ No newline at end of file diff --git a/loader/app/loader.ts b/loader/app/loader/loader.ts similarity index 50% rename from loader/app/loader.ts rename to loader/app/loader/loader.ts index 2df47c4f..4faaebcc 100644 --- a/loader/app/loader.ts +++ b/loader/app/loader/loader.ts @@ -1,5 +1,8 @@ -import {AppVersion} from "../exports/loader"; -import {type} from "os"; +import {AppVersion} from "tc-loader"; +import {LoadSyntaxError, script_name} from "./utils"; +import * as script_loader from "./script_loader"; +import * as style_loader from "./style_loader"; +import * as template_loader from "./template_loader"; declare global { interface Window { @@ -205,10 +208,6 @@ export async function execute() { } } - /* cleanup */ - { - _script_promises = {}; - } if(config.verbose) console.debug("[loader] finished loader. (Total time: %dms)", Date.now() - load_begin); } export function execute_managed() { @@ -233,347 +232,32 @@ export function execute_managed() { }); } -export type DependSource = { - url: string; - depends: string[]; -} -export type SourcePath = string | DependSource | string[]; - -function script_name(path: SourcePath, html: boolean) { - if(Array.isArray(path)) { - let buffer = ""; - let _or = " or "; - for(let entry of path) - buffer += _or + script_name(entry, html); - return buffer.slice(_or.length); - } else if(typeof(path) === "string") - return html ? "" + path + "" : path; - else - return html ? "" + path.url + "" : path.url; -} - -class SyntaxError { - source: any; - - constructor(source: any) { - this.source = source; - } -} - -let _script_promises: {[key: string]: Promise} = {}; -export async function load_script(path: SourcePath) : Promise { - if(Array.isArray(path)) { //We have some fallback - return load_script(path[0]).catch(error => { - console.log(typeof error + " - " + (error instanceof SyntaxError)); - if(error instanceof SyntaxError) - return Promise.reject(error); - - if(path.length > 1) - return load_script(path.slice(1)); - - return Promise.reject(error); - }); - } else { - const source = typeof(path) === "string" ? {url: path, depends: []} : path; - if(source.url.length == 0) return Promise.resolve(); - - return _script_promises[source.url] = (async () => { - /* await depends */ - for(const depend of source.depends) { - if(!_script_promises[depend]) - throw "Missing dependency " + depend; - await _script_promises[depend]; - } - - const tag: HTMLScriptElement = document.createElement("script"); - - await new Promise((resolve, reject) => { - let error = false; - const error_handler = (event: ErrorEvent) => { - if(event.filename == tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error - if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); - window.removeEventListener('error', error_handler as any); - - reject(new SyntaxError(event.error)); - event.preventDefault(); - error = true; - } - }; - window.addEventListener('error', error_handler as any); - - const cleanup = () => { - tag.onerror = undefined; - tag.onload = undefined; - - clearTimeout(timeout_handle); - window.removeEventListener('error', error_handler as any); - }; - const timeout_handle = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 5000); - tag.type = "application/javascript"; - tag.async = true; - tag.defer = true; - tag.onerror = error => { - cleanup(); - tag.remove(); - reject(error); - }; - tag.onload = () => { - cleanup(); - - if(config.verbose) console.debug("Script %o loaded", path); - setTimeout(resolve, 100); - }; - - document.getElementById("scripts").appendChild(tag); - - tag.src = source.url + (cache_tag || ""); - }); - })(); - } -} - -export async function load_scripts(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - script: SourcePath, - error: any - }[] = []; - - for(const script of paths) - promises.push(load_script(script).catch(error => { - errors.push({ - script: script, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if(config.error) { - console.error("Failed to load the following scripts:"); - for(const script of errors) { - const sname = script_name(script.script, false); - if(script.error instanceof SyntaxError) { - const source = script.error.source as Error; - if(source.name === "TypeError") { - let prefix = ""; - while(prefix.length < sname.length + 7) prefix += " "; - console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n")); - } else { - console.log(" - %s: %o", sname, source); - } - } else { - console.log(" - %s: %o", sname, script.error); - } - } - } - - critical_error("Failed to load script " + script_name(errors[0].script, true) + "
" + "View the browser console for more information!"); - throw "failed to load script " + script_name(errors[0].script, false); - } -} - -export async function load_style(path: SourcePath) : Promise { - if(Array.isArray(path)) { //We have some fallback - return load_style(path[0]).catch(error => { - if(error instanceof SyntaxError) - return Promise.reject(error.source); - - if(path.length > 1) - return load_script(path.slice(1)); - - return Promise.reject(error); - }); - } else { - if(!path) { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - const tag: HTMLLinkElement = document.createElement("link"); - - let error = false; - const error_handler = (event: ErrorEvent) => { - if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); - if(event.filename == tag.href) { //FIXME! - window.removeEventListener('error', error_handler as any); - - reject(new SyntaxError(event.error)); - event.preventDefault(); - error = true; - } - }; - window.addEventListener('error', error_handler as any); - - tag.type = "text/css"; - tag.rel = "stylesheet"; - - const cleanup = () => { - tag.onerror = undefined; - tag.onload = undefined; - - clearTimeout(timeout_handle); - window.removeEventListener('error', error_handler as any); - }; - - const timeout_handle = setTimeout(() => { - cleanup(); - reject("timeout"); - }, 5000); - - tag.onerror = error => { - cleanup(); - tag.remove(); - if(config.error) - console.error("File load error for file %s: %o", path, error); - reject("failed to load file " + path); - }; - tag.onload = () => { - cleanup(); - { - const css: CSSStyleSheet = tag.sheet as CSSStyleSheet; - const rules = css.cssRules; - const rules_remove: number[] = []; - const rules_add: string[] = []; - - for(let index = 0; index < rules.length; index++) { - const rule = rules.item(index); - let rule_text = rule.cssText; - - if(rule.cssText.indexOf("%%base_path%%") != -1) { - rules_remove.push(index); - rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname)); - } - } - - for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) { - if(css.removeRule) - css.removeRule(index); - else - css.deleteRule(index); - } - for(const rule of rules_add) - css.insertRule(rule, rules_remove[0]); - } - - if(config.verbose) console.debug("Style sheet %o loaded", path); - setTimeout(resolve, 100); - }; - - document.getElementById("style").appendChild(tag); - tag.href = path + (cache_tag || ""); - }); - } -} - -export async function load_styles(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - sheet: SourcePath, - error: any - }[] = []; - - for(const sheet of paths) - promises.push(load_style(sheet).catch(error => { - errors.push({ - sheet: sheet, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if(config.error) { - console.error("Failed to load the following style sheet:"); - for(const sheet of errors) - console.log(" - %o: %o", sheet.sheet, sheet.error); - } - - critical_error("Failed to load style sheet " + script_name(errors[0].sheet, true) + "
" + "View the browser console for more information!"); - throw "failed to load style sheet " + script_name(errors[0].sheet, false); - } -} - -export async function load_template(path: SourcePath) : Promise { - try { - const response = await $.ajax(path + (cache_tag || "")); - - let node = document.createElement("html"); - node.innerHTML = response; - let tags: HTMLCollection; - if(node.getElementsByTagName("body").length > 0) - tags = node.getElementsByTagName("body")[0].children; - else - tags = node.children; - - let root = document.getElementById("templates"); - if(!root) { - critical_error("Failed to find template tag!"); - throw "Failed to find template tag"; - } - while(tags.length > 0){ - let tag = tags.item(0); - root.appendChild(tag); - - } - } catch(error) { - let msg; - if('responseText' in error) - msg = error.responseText; - else if(error instanceof Error) - msg = error.message; - - critical_error("failed to load template " + script_name(path, true), msg); - throw "template error"; - } -} - -export async function load_templates(paths: SourcePath[]) : Promise { - const promises: Promise[] = []; - const errors: { - template: SourcePath, - error: any - }[] = []; - - for(const template of paths) - promises.push(load_template(template).catch(error => { - errors.push({ - template: template, - error: error - }); - return Promise.resolve(); - })); - - await Promise.all([...promises]); - - if(errors.length > 0) { - if (config.error) { - console.error("Failed to load the following templates:"); - for (const sheet of errors) - console.log(" - %s: %o", script_name(sheet.template, false), sheet.error); - } - - critical_error("Failed to load template " + script_name(errors[0].template, true) + "
" + "View the browser console for more information!"); - throw "failed to load template " + script_name(errors[0].template, false); +let _fadeout_warned; +export function hide_overlay() { + if(typeof($) === "undefined") { + if(!_fadeout_warned) + console.warn("Could not fadeout loader screen. Missing jquery functions."); + _fadeout_warned = true; + return; } + const animation_duration = 750; + + $(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, animation_duration); + $(".loader .half").animate({width: 0}, animation_duration, () => { + $(".loader").detach(); + }); } +/* versions management */ let version_: AppVersion; export function version() : AppVersion { return version_; } export function set_version(version: AppVersion) { version_ = version; } -export type ErrorHandler = (message: string, detail: string) => void; +/* critical error handler */ +export type ErrorHandler = (message: string, detail: string) => void; let _callback_critical_error: ErrorHandler; let _callback_critical_called: boolean = false; - export function critical_error(message: string, detail?: string) { if(_callback_critical_called) { console.warn("[CRITICAL] %s", message); @@ -603,29 +287,24 @@ export function critical_error(message: string, detail?: string) { tag.classList.add("shown"); } - export function critical_error_handler(handler?: ErrorHandler, override?: boolean) : ErrorHandler { if((typeof(handler) === "object" && handler !== _callback_critical_error) || override) _callback_critical_error = handler; return _callback_critical_error; } -let _fadeout_warned; -export function hide_overlay() { - if(typeof($) === "undefined") { - if(!_fadeout_warned) - console.warn("Could not fadeout loader screen. Missing jquery functions."); - _fadeout_warned = true; - return; - } - const animation_duration = 750; - - $(".loader .bookshelf_wrapper").animate({top: 0, opacity: 0}, animation_duration); - $(".loader .half").animate({width: 0}, animation_duration, () => { - $(".loader").detach(); - }); +/* loaders */ +export type DependSource = { + url: string; + depends: string[]; } +export type SourcePath = string | DependSource | string[]; +export const scripts = script_loader; +export const style = style_loader; +export const templates = template_loader; + +/* Hello World message */ { const hello_world = () => { diff --git a/loader/app/loader/script_loader.ts b/loader/app/loader/script_loader.ts new file mode 100644 index 00000000..7c47ae26 --- /dev/null +++ b/loader/app/loader/script_loader.ts @@ -0,0 +1,121 @@ +import {config, critical_error, SourcePath} from "./loader"; +import {load_parallel, LoadSyntaxError, ParallelOptions, script_name} from "./utils"; + +let _script_promises: {[key: string]: Promise} = {}; + +function load_script_url(url: string) : Promise { + if(typeof _script_promises[url] === "object") + return _script_promises[url]; + + return (_script_promises[url] = new Promise((resolve, reject) => { + const script_tag: HTMLScriptElement = document.createElement("script"); + + let error = false; + const error_handler = (event: ErrorEvent) => { + if(event.filename == script_tag.src && event.message.indexOf("Illegal constructor") == -1) { //Our tag throw an uncaught error + if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); + window.removeEventListener('error', error_handler as any); + + reject(new LoadSyntaxError(event.error)); + event.preventDefault(); + error = true; + } + }; + window.addEventListener('error', error_handler as any); + + const cleanup = () => { + script_tag.onerror = undefined; + script_tag.onload = undefined; + + clearTimeout(timeout_handle); + window.removeEventListener('error', error_handler as any); + }; + const timeout_handle = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 5000); + script_tag.type = "application/javascript"; + script_tag.async = true; + script_tag.defer = true; + script_tag.onerror = error => { + cleanup(); + script_tag.remove(); + reject(error); + }; + script_tag.onload = () => { + cleanup(); + + if(config.verbose) console.debug("Script %o loaded", url); + setTimeout(resolve, 100); + }; + + document.getElementById("scripts").appendChild(script_tag); + + script_tag.src = url; + })).then(result => { + /* cleanup memory */ + _script_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */ + return _script_promises[url]; + }).catch(error => { + /* cleanup memory */ + _script_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */ + return _script_promises[url]; + }); +} + +export interface Options { + cache_tag?: string; +} + +export async function load(path: SourcePath, options: Options) : Promise { + if(Array.isArray(path)) { //We have fallback scripts + return load(path[0], options).catch(error => { + if(error instanceof LoadSyntaxError) + return Promise.reject(error); + + if(path.length > 1) + return load(path.slice(1), options); + + return Promise.reject(error); + }); + } else { + const source = typeof(path) === "string" ? {url: path, depends: []} : path; + if(source.url.length == 0) return Promise.resolve(); + + /* await depends */ + for(const depend of source.depends) { + if(!_script_promises[depend]) + throw "Missing dependency " + depend; + await _script_promises[depend]; + } + await load_script_url(source.url + (options.cache_tag || "")); + } +} + +type MultipleOptions = Options | ParallelOptions; +export async function load_multiple(paths: SourcePath[], options: MultipleOptions) : Promise { + const result = await load_parallel(paths, e => load(e, options), e => script_name(e, false), options); + if(result.failed.length > 0) { + if(config.error) { + console.error("Failed to load the following scripts:"); + for(const script of result.failed) { + const sname = script_name(script.request, false); + if(script.error instanceof LoadSyntaxError) { + const source = script.error.source as Error; + if(source.name === "TypeError") { + let prefix = ""; + while(prefix.length < sname.length + 7) prefix += " "; + console.log(" - %s: %s:\n%s", sname, source.message, source.stack.split("\n").map(e => prefix + e.trim()).slice(1).join("\n")); + } else { + console.log(" - %s: %o", sname, source); + } + } else { + console.log(" - %s: %o", sname, script.error); + } + } + } + + critical_error("Failed to load script " + script_name(result.failed[0].request, true) + "
" + "View the browser console for more information!"); + throw "failed to load script " + script_name(result.failed[0].request, false); + } +} \ No newline at end of file diff --git a/loader/app/loader/style_loader.ts b/loader/app/loader/style_loader.ts new file mode 100644 index 00000000..7eb84153 --- /dev/null +++ b/loader/app/loader/style_loader.ts @@ -0,0 +1,142 @@ +import {config, critical_error, SourcePath} from "./loader"; +import {load_parallel, LoadSyntaxError, ParallelOptions, script_name} from "./utils"; + +let _style_promises: {[key: string]: Promise} = {}; + +function load_style_url(url: string) : Promise { + if(typeof _style_promises[url] === "object") + return _style_promises[url]; + + return (_style_promises[url] = new Promise((resolve, reject) => { + const tag: HTMLLinkElement = document.createElement("link"); + + let error = false; + const error_handler = (event: ErrorEvent) => { + if(config.verbose) console.log("msg: %o, url: %o, line: %o, col: %o, error: %o", event.message, event.filename, event.lineno, event.colno, event.error); + if(event.filename == tag.href) { //FIXME! + window.removeEventListener('error', error_handler as any); + + reject(new SyntaxError(event.error)); + event.preventDefault(); + error = true; + } + }; + window.addEventListener('error', error_handler as any); + + tag.type = "text/css"; + tag.rel = "stylesheet"; + + const cleanup = () => { + tag.onerror = undefined; + tag.onload = undefined; + + clearTimeout(timeout_handle); + window.removeEventListener('error', error_handler as any); + }; + + const timeout_handle = setTimeout(() => { + cleanup(); + reject("timeout"); + }, 5000); + + tag.onerror = error => { + cleanup(); + tag.remove(); + if(config.error) + console.error("File load error for file %s: %o", url, error); + reject("failed to load file " + url); + }; + tag.onload = () => { + cleanup(); + { + const css: CSSStyleSheet = tag.sheet as CSSStyleSheet; + const rules = css.cssRules; + const rules_remove: number[] = []; + const rules_add: string[] = []; + + for(let index = 0; index < rules.length; index++) { + const rule = rules.item(index); + let rule_text = rule.cssText; + + if(rule.cssText.indexOf("%%base_path%%") != -1) { + rules_remove.push(index); + rules_add.push(rule_text.replace("%%base_path%%", document.location.origin + document.location.pathname)); + } + } + + for(const index of rules_remove.sort((a, b) => b > a ? 1 : 0)) { + if(css.removeRule) + css.removeRule(index); + else + css.deleteRule(index); + } + for(const rule of rules_add) + css.insertRule(rule, rules_remove[0]); + } + + if(config.verbose) console.debug("Style sheet %o loaded", url); + setTimeout(resolve, 100); + }; + + document.getElementById("style").appendChild(tag); + tag.href = url; + })).then(result => { + /* cleanup memory */ + _style_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */ + return _style_promises[url]; + }).catch(error => { + /* cleanup memory */ + _style_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */ + return _style_promises[url]; + }); +} + +export interface Options { + cache_tag?: string; +} + +export async function load(path: SourcePath, options: Options) : Promise { + if(Array.isArray(path)) { //We have fallback scripts + return load(path[0], options).catch(error => { + if(error instanceof LoadSyntaxError) + return Promise.reject(error); + + if(path.length > 1) + return load(path.slice(1), options); + + return Promise.reject(error); + }); + } else { + const source = typeof(path) === "string" ? {url: path, depends: []} : path; + if(source.url.length == 0) return Promise.resolve(); + + /* await depends */ + for(const depend of source.depends) { + if(!_style_promises[depend]) + throw "Missing dependency " + depend; + await _style_promises[depend]; + } + await load_style_url(source.url + (options.cache_tag || "")); + } +} + +export type MultipleOptions = Options | ParallelOptions; +export async function load_multiple(paths: SourcePath[], options: MultipleOptions) : Promise { + const result = await load_parallel(paths, e => load(e, options), e => script_name(e, false), options); + if(result.failed.length > 0) { + if(config.error) { + console.error("Failed to load the following style sheets:"); + for(const style of result.failed) { + const sname = script_name(style.request, false); + if(style.error instanceof LoadSyntaxError) { + console.log(" - %s: %o", sname, style.error.source); + } else { + console.log(" - %s: %o", sname, style.error); + } + } + } + + critical_error("Failed to load style " + script_name(result.failed[0].request, true) + "
" + "View the browser console for more information!"); + throw "failed to load style " + script_name(result.failed[0].request, false); + } +} \ No newline at end of file diff --git a/loader/app/loader/template_loader.ts b/loader/app/loader/template_loader.ts new file mode 100644 index 00000000..7d503af4 --- /dev/null +++ b/loader/app/loader/template_loader.ts @@ -0,0 +1,90 @@ +import {config, critical_error, SourcePath} from "./loader"; +import {load_parallel, LoadSyntaxError, ParallelOptions, script_name} from "./utils"; + +let _template_promises: {[key: string]: Promise} = {}; + +function load_template_url(url: string) : Promise { + if(typeof _template_promises[url] === "object") + return _template_promises[url]; + + return (_template_promises[url] = (async () => { + const response = await $.ajax(url); + + let node = document.createElement("html"); + node.innerHTML = response; + let tags: HTMLCollection; + if(node.getElementsByTagName("body").length > 0) + tags = node.getElementsByTagName("body")[0].children; + else + tags = node.children; + + let root = document.getElementById("templates"); + if(!root) { + critical_error("Failed to find template tag!"); + throw "Failed to find template tag"; + } + while(tags.length > 0){ + let tag = tags.item(0); + root.appendChild(tag); + + } + })()).then(result => { + /* cleanup memory */ + _template_promises[url] = Promise.resolve(); /* this promise does not holds the whole script tag and other memory */ + return _template_promises[url]; + }).catch(error => { + /* cleanup memory */ + _template_promises[url] = Promise.reject(error); /* this promise does not holds the whole script tag and other memory */ + return _template_promises[url]; + }); +} + +export interface Options { + cache_tag?: string; +} + +export async function load(path: SourcePath, options: Options) : Promise { + if(Array.isArray(path)) { //We have fallback scripts + return load(path[0], options).catch(error => { + if(error instanceof LoadSyntaxError) + return Promise.reject(error); + + if(path.length > 1) + return load(path.slice(1), options); + + return Promise.reject(error); + }); + } else { + const source = typeof(path) === "string" ? {url: path, depends: []} : path; + if(source.url.length == 0) return Promise.resolve(); + + /* await depends */ + for(const depend of source.depends) { + if(!_template_promises[depend]) + throw "Missing dependency " + depend; + await _template_promises[depend]; + } + await load_template_url(source.url + (options.cache_tag || "")); + } +} + +export type MultipleOptions = Options | ParallelOptions; +export async function load_multiple(paths: SourcePath[], options: MultipleOptions) : Promise { + const result = await load_parallel(paths, e => load(e, options), e => script_name(e, false), options); + if(result.failed.length > 0) { + if(config.error) { + console.error("Failed to load the following template files:"); + for(const style of result.failed) { + const sname = script_name(style.request, false); + if(style.error instanceof LoadSyntaxError) { + console.log(" - %s: %o", sname, style.error.source); + } else { + console.log(" - %s: %o", sname, style.error); + } + } + } + + critical_error("Failed to load template file " + script_name(result.failed[0].request, true) + "
" + "View the browser console for more information!"); + throw "failed to load template file " + script_name(result.failed[0].request, false); + } +} \ No newline at end of file diff --git a/loader/app/loader/utils.ts b/loader/app/loader/utils.ts new file mode 100644 index 00000000..fdab50f2 --- /dev/null +++ b/loader/app/loader/utils.ts @@ -0,0 +1,65 @@ +import {SourcePath} from "./loader"; +import {Options} from "./script_loader"; + +export class LoadSyntaxError { + readonly source: any; + constructor(source: any) { + this.source = source; + } +} + +export function script_name(path: SourcePath, html: boolean) { + if(Array.isArray(path)) { + let buffer = ""; + let _or = " or "; + for(let entry of path) + buffer += _or + script_name(entry, html); + return buffer.slice(_or.length); + } else if(typeof(path) === "string") + return html ? "" + path + "" : path; + else + return html ? "" + path.url + "" : path.url; +} + +export interface ParallelOptions extends Options { + max_parallel_requests?: number +} + +export interface ParallelResult { + succeeded: T[]; + failed: { + request: T, + error: T + }[], + + skipped: T[]; +} + +export async function load_parallel(requests: T[], executor: (_: T) => Promise, stringify: (_: T) => string, options: ParallelOptions) : Promise> { + const result: ParallelResult = { failed: [], succeeded: [], skipped: [] }; + const pending_requests = requests.slice(0).reverse(); /* we're only able to pop from the back */ + const current_requests = {}; + + while (pending_requests.length > 0) { + while(typeof options.max_parallel_requests !== "number" || options.max_parallel_requests <= 0 || Object.keys(current_requests).length < options.max_parallel_requests) { + const script = pending_requests.pop(); + const name = stringify(script); + + current_requests[name] = executor(script).catch(e => result.failed.push({ request: script, error: e })).then(() => { + delete current_requests[name]; + }); + if(pending_requests.length == 0) break; + } + + /* + * Wait 'till a new "slot" for downloading is free. + * This should also not throw because any errors will be caught before. + */ + await Promise.race(Object.keys(current_requests).map(e => current_requests[e])); + if(result.failed.length > 0) + break; /* finish loading the other requests and than show the error */ + } + await Promise.all(Object.keys(current_requests).map(e => current_requests[e])); + result.skipped.push(...pending_requests); + return result; +} \ No newline at end of file diff --git a/loader/exports/loader.d.ts b/loader/exports/loader.d.ts index a7a151ec..d947c9df 100644 --- a/loader/exports/loader.d.ts +++ b/loader/exports/loader.d.ts @@ -73,12 +73,6 @@ export type DependSource = { depends: string[]; } export type SourcePath = string | DependSource | string[]; -export function load_script(path: SourcePath) : Promise; -export function load_scripts(paths: SourcePath[]) : Promise; -export function load_style(path: SourcePath) : Promise; -export function load_styles(paths: SourcePath[]) : Promise; -export function load_template(path: SourcePath) : Promise; -export function load_templates(paths: SourcePath[]) : Promise; export type ErrorHandler = (message: string, detail: string) => void; export function critical_error(message: string, detail?: string); export function critical_error_handler(handler?: ErrorHandler, override?: boolean); diff --git a/loader/webpack.config.js b/loader/webpack.config.js index 075fb34c..baddf2d6 100644 --- a/loader/webpack.config.js +++ b/loader/webpack.config.js @@ -54,9 +54,9 @@ module.exports = { }, output: { filename: 'loader.js', - path: path.resolve(__dirname, '../dist'), + path: path.resolve(__dirname, 'dist'), library: "loader", - //libraryTarget: "umd" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system" + libraryTarget: "window" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system" }, optimization: { } }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index df6a8f9d..b30fd67e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/anymatch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", + "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", + "dev": true + }, "@types/emscripten": { "version": "1.39.2", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz", @@ -31,6 +37,23 @@ } } }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz", + "integrity": "sha512-q95SP4FdkmF0CwO0F2q0H6ZgudsApaY/yCtAQNRn1gduef5fGpyEphzy0YCq/N0UFvDSnLg5V8jFK/YGXlDiCw==", + "dev": true + }, "@types/jquery": { "version": "3.3.34", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.34.tgz", @@ -46,6 +69,12 @@ "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", "dev": true }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/moment": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@types/moment/-/moment-2.13.0.tgz", @@ -101,6 +130,52 @@ "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", "dev": true }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz", + "integrity": "sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz", + "integrity": "sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "@types/webpack": { + "version": "4.41.9", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.9.tgz", + "integrity": "sha512-R68AotLGtaVL6HGfZRvEyYKsWcMv0CBFfSr4gxoYzhMn3LnjLV/ksP4dNi9de8dVG+Dn/GuDr1NwB/sDApB3pA==", + "dev": true, + "requires": { + "@types/anymatch": "*", + "@types/node": "*", + "@types/tapable": "*", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "source-map": "^0.6.0" + } + }, + "@types/webpack-sources": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", + "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + } + }, "@types/websocket": { "version": "0.0.40", "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-0.0.40.tgz", @@ -304,12 +379,28 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, "acorn": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, + "acorn-walk": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "dev": true + }, "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", @@ -468,6 +559,12 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, "array-initial": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", @@ -528,6 +625,21 @@ } } }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", @@ -617,6 +729,12 @@ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", "dev": true }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -755,6 +873,18 @@ "tweetnacl": "^0.14.3" } }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -798,6 +928,38 @@ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "dev": true }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -931,6 +1093,12 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, "cacache": { "version": "12.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", @@ -1002,6 +1170,16 @@ } } }, + "camel-case": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", + "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "dev": true, + "requires": { + "pascal-case": "^3.1.1", + "tslib": "^1.10.0" + } + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -1045,6 +1223,12 @@ "supports-color": "^2.0.0" } }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -1077,6 +1261,15 @@ "tslib": "^1.9.0" } }, + "chunk-manifest-webpack-plugin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chunk-manifest-webpack-plugin/-/chunk-manifest-webpack-plugin-1.1.2.tgz", + "integrity": "sha1-szLvuXwaI7/Tyl3O4TqhIN9y31k=", + "dev": true, + "requires": { + "webpack-core": "^0.6.9" + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -1139,6 +1332,16 @@ "source-map": "~0.6.0" } }, + "clean-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==", + "dev": true, + "requires": { + "@types/webpack": "^4.4.31", + "del": "^4.1.1" + } + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -1320,6 +1523,21 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, "convert-hex": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/convert-hex/-/convert-hex-0.1.0.tgz", @@ -1341,6 +1559,18 @@ "integrity": "sha1-ec5BqbsNA7z3LNxqjzxW+7xkQQo=", "dev": true }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -1477,6 +1707,36 @@ } } }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + } + } + }, "css-tree": { "version": "1.0.0-alpha.29", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", @@ -1495,6 +1755,12 @@ } } }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1676,6 +1942,29 @@ } } }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1688,6 +1977,12 @@ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, "des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -1698,6 +1993,12 @@ "minimalistic-assert": "^1.0.0" } }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -1715,12 +2016,73 @@ "randombytes": "^2.0.0" } }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + }, + "domhandler": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", + "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz", + "integrity": "sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==", + "dev": true, + "requires": { + "dom-serializer": "^0.2.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0" + } + }, + "dot-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", + "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", + "dev": true, + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -1753,6 +2115,18 @@ "safer-buffer": "^2.1.0" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, "elliptic": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", @@ -1780,6 +2154,12 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1800,6 +2180,12 @@ "tapable": "^1.0.0" } }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -1818,6 +2204,36 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", @@ -1862,6 +2278,12 @@ "es6-symbol": "^3.1.1" } }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1893,6 +2315,12 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, "events": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", @@ -1966,6 +2394,70 @@ "homedir-polyfill": "^1.0.1" } }, + "exports-loader": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.7.0.tgz", + "integrity": "sha512-RKwCrO4A6IiKm0pG3c9V46JxIHcDplwwGJn6+JJ1RcVnh/WSGJa0xkmk5cRVtgOPzCAtTMGj2F7nluh9L0vpSA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "source-map": "0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", + "dev": true + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -2055,6 +2547,44 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, + "file-loader": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", + "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", + "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -2068,6 +2598,12 @@ "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", "dev": true }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", @@ -2081,6 +2617,21 @@ "repeat-string": "^1.5.2" } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -2466,6 +3017,12 @@ "mime-types": "^2.1.12" } }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -2475,6 +3032,12 @@ "map-cache": "^0.2.2" } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -3639,6 +4202,19 @@ "which": "^1.2.14" } }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "globule": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", @@ -3720,6 +4296,24 @@ "glogg": "^1.0.0" } }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -3736,6 +4330,15 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -3843,6 +4446,12 @@ "minimalistic-assert": "^1.0.1" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -3863,12 +4472,132 @@ "parse-passwd": "^1.0.0" } }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", "dev": true }, + "html-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-1.0.0.tgz", + "integrity": "sha512-acPyjP9Mo05jEbe/oejXu5gFwtKWdFewPoaVa47VCnHmRbR43jtdx/kPhPEbCBm2qWtIn+a8uTJAW4Ocnn4olw==", + "dev": true, + "requires": { + "html-minifier-terser": "^5.0.4", + "htmlparser2": "^4.1.0", + "loader-utils": "^2.0.0", + "parse-srcset": "^1.0.2", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "json5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", + "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, + "html-minifier-terser": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.0.5.tgz", + "integrity": "sha512-cBSFFghQh/uHcfSiL42KxxIRMF7A144+3E44xdlctIjxEmkEfCvouxNyFH2wysXk1fCGBPwtcr3hDWlGTfkDew==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + } + } + }, + "html-webpack-plugin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.3.tgz", + "integrity": "sha512-XCm5MGK6Yv/ey30fbhqjKIGm1r6G1HxmNorJ/xUdL/Zj3mOLtb9ZHEEIw0e4h3VzgyUrp8szCJh3VN9z1LNx7A==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^5.0.0", + "@types/tapable": "^1.0.5", + "@types/webpack": "^4.41.8", + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + } + }, + "htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -3886,6 +4615,15 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "icss-utils": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", @@ -3984,6 +4722,12 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -4024,6 +4768,12 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -4033,6 +4783,12 @@ "kind-of": "^3.0.2" } }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -4118,6 +4874,30 @@ "kind-of": "^3.0.2" } }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -4153,6 +4933,15 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -4168,6 +4957,15 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -4459,6 +5257,15 @@ "signal-exit": "^3.0.0" } }, + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -4872,6 +5679,12 @@ "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -4919,6 +5732,18 @@ } } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -4950,6 +5775,12 @@ "brorand": "^1.0.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", @@ -5160,6 +5991,12 @@ } } }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -5178,6 +6015,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "no-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", + "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "dev": true, + "requires": { + "lower-case": "^2.0.1", + "tslib": "^1.10.0" + } + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -5342,6 +6189,15 @@ "set-blocking": "~2.0.0" } }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -5381,6 +6237,12 @@ } } }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5445,6 +6307,28 @@ } } }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "object.map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", @@ -5514,6 +6398,15 @@ } } }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5523,6 +6416,12 @@ "wrappy": "1" } }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -5605,6 +6504,12 @@ "p-limit": "^2.0.0" } }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -5628,6 +6533,16 @@ "readable-stream": "^2.1.5" } }, + "param-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", + "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", + "dev": true, + "requires": { + "dot-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, "parse-asn1": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", @@ -5686,6 +6601,28 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", + "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", + "dev": true, + "requires": { + "no-case": "^3.0.3", + "tslib": "^1.10.0" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -5719,6 +6656,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -5746,6 +6689,12 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -5952,6 +6901,16 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -5986,6 +6945,16 @@ "react-is": "^16.8.1" } }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -6117,6 +7086,24 @@ "safe-buffer": "^5.1.0" } }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "react": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", @@ -6504,6 +7491,12 @@ "safe-regex": "^1.1.0" } }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", @@ -6531,6 +7524,77 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", @@ -6932,12 +7996,53 @@ "sver-compat": "^1.5.0" } }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "serialize-javascript": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6973,6 +8078,12 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "dev": true }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -7295,6 +8406,12 @@ } } }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, "stdout-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", @@ -7366,6 +8483,48 @@ "strip-ansi": "^3.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", + "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", + "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7594,6 +8753,12 @@ "through2": "^2.0.3" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -7619,6 +8784,12 @@ "glob": "^7.1.2" } }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, "ts-loader": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.2.tgz", @@ -7754,6 +8925,16 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -7847,6 +9028,12 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7961,6 +9148,28 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -7998,6 +9207,12 @@ "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", "dev": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -8800,6 +10015,64 @@ } } }, + "webpack-bundle-analyzer": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz", + "integrity": "sha512-Nfd8HDwfSx1xBwC+P8QMGvHAOITxNBSvu/J/mCJvOwv+G4VWkU7zir9SSenTtyCi0LnVtmsc7G5SZo1uV+bxRw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "webpack-cli": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", @@ -9086,6 +10359,58 @@ } } }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-manifest-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", + "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "dev": true, + "requires": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "object.entries": "^1.1.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", @@ -9129,6 +10454,15 @@ "errno": "~0.1.7" } }, + "worker-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-4.0.2.tgz", + "integrity": "sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -9145,6 +10479,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index b7c8f5ca..7008ffd1 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,17 @@ "@types/react-dom": "^16.9.5", "@types/sha256": "^0.2.0", "@types/websocket": "0.0.40", + "chunk-manifest-webpack-plugin": "^1.1.2", "clean-css": "^4.2.1", + "clean-webpack-plugin": "^3.0.0", "css-loader": "^3.4.2", "csso-cli": "^2.0.2", + "exports-loader": "^0.7.0", + "file-loader": "^6.0.0", "fs-extra": "latest", "gulp": "^4.0.2", + "html-loader": "^1.0.0", + "html-webpack-plugin": "^4.0.3", "mime-types": "^2.1.24", "mini-css-extract-plugin": "^0.9.0", "mkdirp": "^0.5.1", @@ -53,7 +59,10 @@ "typescript": "3.6.5", "wat2wasm": "^1.0.2", "webpack": "^4.42.1", - "webpack-cli": "^3.3.11" + "webpack-bundle-analyzer": "^3.6.1", + "webpack-cli": "^3.3.11", + "webpack-manifest-plugin": "^2.2.0", + "worker-plugin": "^4.0.2" }, "repository": { "type": "git", diff --git a/shared/popup/certaccept/js/main.ts b/shared/popup/certaccept/js/main.ts index 003129be..2d9abd48 100644 --- a/shared/popup/certaccept/js/main.ts +++ b/shared/popup/certaccept/js/main.ts @@ -1,5 +1,6 @@ import {settings, Settings} from "tc-shared/settings"; import * as loader from "tc-loader"; +import * as log from "tc-shared/log"; import {LogCategory} from "tc-shared/log"; import * as bipc from "tc-shared/BrowserIPC"; diff --git a/tsconfig.json b/tsconfig.json index 9bd65288..143b61c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "paths": { "*": ["shared/declarations/*"], "tc-shared/*": ["shared/js/*"], + "tc-backend/web/*": ["web/js/*"], /* specific web part */ "tc-backend/*": ["shared/backend.d/*"], "tc-loader": ["loader/exports/loader.d.ts"] } diff --git a/web/js/codec/CodecWrapperRaw.ts b/web/js/codec/CodecRaw.ts similarity index 96% rename from web/js/codec/CodecWrapperRaw.ts rename to web/js/codec/CodecRaw.ts index b6b591f1..df2bde16 100644 --- a/web/js/codec/CodecWrapperRaw.ts +++ b/web/js/codec/CodecRaw.ts @@ -1,6 +1,6 @@ import {BasicCodec} from "./BasicCodec"; -export class CodecWrapperRaw extends BasicCodec { +export class CodecRaw extends BasicCodec { converterRaw: any; converter: Uint8Array; bufferSize: number = 4096 * 4; diff --git a/web/js/codec/CodecWrapperWorker.ts b/web/js/codec/CodecWrapperWorker.ts index ea6c42ed..7350bbdb 100644 --- a/web/js/codec/CodecWrapperWorker.ts +++ b/web/js/codec/CodecWrapperWorker.ts @@ -1,135 +1,37 @@ import {BasicCodec} from "./BasicCodec"; import {CodecType} from "./Codec"; -import {LogCategory} from "tc-shared/log"; import * as log from "tc-shared/log"; -import {settings} from "tc-shared/settings"; +import {LogCategory} from "tc-shared/log"; + +interface ExecuteResult { + result?: any; + error?: string; + + success: boolean; + + timings: { + upstream: number; + downstream: number; + handle: number; + } +} export class CodecWrapperWorker extends BasicCodec { private _worker: Worker; - private _workerListener: {token: string, resolve: (data: any) => void}[] = []; - private _workerCallbackToken = "callback_token"; - private _workerTokeIndex: number = 0; - type: CodecType; - private _initialized: boolean = false; - private _workerCallbackResolve: () => any; - private _workerCallbackReject: ($: any) => any; + private _initialize_promise: Promise; - private _initializePromise: Promise; - name(): string { - return "Worker for " + CodecType[this.type] + " Channels " + this.channelCount; - } + private _token_index: number = 0; + readonly type: CodecType; - initialise() : Promise { - if(this._initializePromise) return this._initializePromise; - return this._initializePromise = this.spawnWorker().then(() => new Promise((resolve, reject) => { - const token = this.generateToken(); - this.sendWorkerMessage({ - command: "initialise", - type: this.type, - channelCount: this.channelCount, - token: token - }); + private pending_executes: {[key: string]: { + timeout?: any; - this._workerListener.push({ - token: token, - resolve: data => { - this._initialized = data["success"] == true; - if(data["success"] == true) - resolve(); - else - reject(data.message); - } - }) - })); - } + timestamp_send: number, - initialized() : boolean { - return this._initialized; - } - - deinitialise() { - this.sendWorkerMessage({ - command: "deinitialise" - }); - } - - decode(data: Uint8Array): Promise { - let token = this.generateToken(); - let result = new Promise((resolve, reject) => { - this._workerListener.push( - { - token: token, - resolve: (data) => { - if(data.success) { - let array = new Float32Array(data.dataLength); - for(let index = 0; index < array.length; index++) - array[index] = data.data[index]; - - let audioBuf = this._audioContext.createBuffer(this.channelCount, array.length / this.channelCount, this._codecSampleRate); - for (let channel = 0; channel < this.channelCount; channel++) { - for (let offset = 0; offset < audioBuf.length; offset++) { - audioBuf.getChannelData(channel)[offset] = array[channel + offset * this.channelCount]; - } - } - resolve(audioBuf); - } else { - reject(data.message); - } - } - } - ); - }); - this.sendWorkerMessage({ - command: "decodeSamples", - token: token, - data: data, - dataLength: data.length - }); - return result; - } - - encode(data: AudioBuffer) : Promise { - let token = this.generateToken(); - let result = new Promise((resolve, reject) => { - this._workerListener.push( - { - token: token, - resolve: (data) => { - if(data.success) { - let array = new Uint8Array(data.dataLength); - for(let index = 0; index < array.length; index++) - array[index] = data.data[index]; - resolve(array); - } else { - reject(data.message); - } - } - } - ); - }); - - let buffer = new Float32Array(this.channelCount * data.length); - for (let offset = 0; offset < data.length; offset++) { - for (let channel = 0; channel < this.channelCount; channel++) - buffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset]; - } - - this.sendWorkerMessage({ - command: "encodeSamples", - token: token, - data: buffer, - dataLength: buffer.length - }); - return result; - } - - reset() : boolean { - this.sendWorkerMessage({ - command: "reset" - }); - return true; - } + resolve: (_: ExecuteResult) => void; + reject: (_: any) => void; + }} = {}; constructor(type: CodecType) { super(48000); @@ -146,35 +48,92 @@ export class CodecWrapperWorker extends BasicCodec { } } - private generateToken() { - return this._workerTokeIndex++ + "_token"; + name(): string { + return "Worker for " + CodecType[this.type] + " Channels " + this.channelCount; } - private sendWorkerMessage(message: any, transfare?: any[]) { - message["timestamp"] = Date.now(); - this._worker.postMessage(message, transfare as any); + async initialise() : Promise { + if(this._initialized) return; + + this._initialize_promise = this.spawn_worker().then(() => this.execute("initialise", { + type: this.type, + channelCount: this.channelCount, + })).then(result => { + if(result.success) + return Promise.resolve(true); + + log.error(LogCategory.VOICE, tr("Failed to initialize codec %s: %s"), CodecType[this.type], result.error); + return Promise.reject(result.error); + }); + + this._initialized = true; + await this._initialize_promise; } - private onWorkerMessage(message: any) { + initialized() : boolean { + return this._initialized; + } + + deinitialise() { + this.execute("deinitialise", {}); + this._initialized = false; + this._initialize_promise = undefined; + } + + async decode(data: Uint8Array): Promise { + const result = await this.execute("decodeSamples", { data: data, length: data.length }); + if(result.timings.downstream > 5 || result.timings.upstream > 5 || result.timings.handle > 5) + log.warn(LogCategory.VOICE, tr("Worker message stock time: {downstream: %dms, handle: %dms, upstream: %dms}"), result.timings.downstream, result.timings.handle, result.timings.upstream); + + if(!result.success) throw result.error || tr("unknown decode error"); + + let array = new Float32Array(result.result.length); + for(let index = 0; index < array.length; index++) + array[index] = result.result.data[index]; + + let audioBuf = this._audioContext.createBuffer(this.channelCount, array.length / this.channelCount, this._codecSampleRate); + for (let channel = 0; channel < this.channelCount; channel++) { + for (let offset = 0; offset < audioBuf.length; offset++) { + audioBuf.getChannelData(channel)[offset] = array[channel + offset * this.channelCount]; + } + } + + return audioBuf; + } + + async encode(data: AudioBuffer) : Promise { + let buffer = new Float32Array(this.channelCount * data.length); + for (let offset = 0; offset < data.length; offset++) { + for (let channel = 0; channel < this.channelCount; channel++) + buffer[offset * this.channelCount + channel] = data.getChannelData(channel)[offset]; + } + + const result = await this.execute("encodeSamples", { data: buffer, length: buffer.length }); + if(result.timings.downstream > 5 || result.timings.upstream > 5) + log.warn(LogCategory.VOICE, tr("Worker message stock time: {downstream: %dms, handle: %dms, upstream: %dms}"), result.timings.downstream, result.timings.handle, result.timings.upstream); + if(!result.success) throw result.error || tr("unknown encode error"); + + let array = new Uint8Array(result.result.length); + for(let index = 0; index < array.length; index++) + array[index] = result.result.data[index]; + return array; + } + + reset() : boolean { + //TODO: Await result! + this.execute("reset", {}); + return true; + } + + private handle_worker_message(message: any) { if(!message["token"]) { log.error(LogCategory.VOICE, tr("Invalid worker token!")); return; } - if(message["token"] == this._workerCallbackToken) { - if(message["type"] == "loaded") { - log.info(LogCategory.VOICE, tr("[Codec] Got worker init response: Success: %o Message: %o"), message["success"], message["message"]); - if(message["success"]) { - if(this._workerCallbackResolve) - this._workerCallbackResolve(); - } else { - if(this._workerCallbackReject) - this._workerCallbackReject(message["message"]); - } - this._workerCallbackReject = undefined; - this._workerCallbackResolve = undefined; - return; - } else if(message["type"] == "chatmessage_server") { + if(message["token"] === "notify") { + /* currently not really used */ + if(message["type"] == "chatmessage_server") { //FIXME? return; } @@ -182,29 +141,69 @@ export class CodecWrapperWorker extends BasicCodec { return; } - /* lets warn on general packets. Control packets are allowed to "stuck" a bit longer */ - if(Date.now() - message["timestamp"] > 5) - log.warn(LogCategory.VOICE, tr("Worker message stock time: %d"), Date.now() - message["timestamp"]); + const request = this.pending_executes[message["token"]]; + if(typeof request !== "object") { + log.error(LogCategory.VOICE, tr("Received worker execute result for unknown token (%s)"), message["token"]); + return; + } + delete this.pending_executes[message["token"]]; - for(let entry of this._workerListener) { - if(entry.token == message["token"]) { - entry.resolve(message); - this._workerListener.remove(entry); - return; + const result: ExecuteResult = { + success: message["success"], + error: message["error"], + result: message["result"], + timings: { + downstream: message["timestamp_received"] - request.timestamp_send, + handle: message["timestamp_send"] - message["timestamp_received"], + upstream: Date.now() - message["timestamp_send"] } + }; + clearTimeout(request.timeout); + request.resolve(result); + } + + private handle_worker_error(error: any) { + log.error(LogCategory.VOICE, tr("Received error from codec worker. Closing worker.")); + for(const token of Object.keys(this.pending_executes)) { + this.pending_executes[token].reject(error); + delete this.pending_executes[token]; } - log.error(LogCategory.VOICE, tr("Could not find worker token entry! (%o)"), message["token"]); + this._worker = undefined; } - private spawnWorker() : Promise { - return new Promise((resolve, reject) => { - this._workerCallbackReject = reject; - this._workerCallbackResolve = resolve; + private execute(command: string, data: any, timeout?: number) : Promise { + return new Promise((resolve, reject) => { + if(!this._worker) { + reject(tr("worker does not exists")); + return; + } - this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerCodec.js"); - this._worker.onmessage = event => this.onWorkerMessage(event.data); - this._worker.onerror = (error: ErrorEvent) => reject("Failed to load worker (" + error.message + ")"); //TODO tr + const token = this._token_index++ + "_token"; + + const payload = { + token: token, + command: command, + data: data, + }; + + this.pending_executes[token] = { + timeout: typeof timeout === "number" ? setTimeout(() => reject(tr("timeout for command ") + command), timeout) : undefined, + resolve: resolve, + reject: reject, + timestamp_send: Date.now() + }; + + this._worker.postMessage(payload); }); } + + private async spawn_worker() : Promise { + this._worker = new Worker("tc-backend/web/workers/codec", { type: "module" }); + this._worker.onmessage = event => this.handle_worker_message(event.data); + this._worker.onerror = event => this.handle_worker_error(event.error); + + const result = await this.execute("global-initialize", {}, 15000); + if(!result.success) throw result.error; + } } \ No newline at end of file diff --git a/web/js/workers/codec/CodecWorker.ts b/web/js/workers/codec/CodecWorker.ts index 1563e1c5..2b88482f 100644 --- a/web/js/workers/codec/CodecWorker.ts +++ b/web/js/workers/codec/CodecWorker.ts @@ -1,7 +1,8 @@ -const prefix = "[CodecWorker] "; -const workerCallbackToken = "callback_token"; +import {CodecType} from "tc-backend/web/codec/Codec"; -interface CodecWorker { +const prefix = "[CodecWorker] "; + +export interface CodecWorker { name(); initialise?() : string; deinitialise(); @@ -11,78 +12,17 @@ interface CodecWorker { reset(); } -let codecInstance: CodecWorker; +let supported_types = {}; +export function register_codec(type: CodecType, allocator: (options?: any) => Promise) { + supported_types[type] = allocator; +} -onmessage = function(e: MessageEvent) { - let data = e.data; +let initialize_callback: () => Promise; +export function set_initialize_callback(callback: () => Promise) { + initialize_callback = callback; +} - let res: any = {}; - res.token = data.token; - res.success = false; - - //console.log(prefix + " Got from main: %o", data); - switch (data.command) { - case "initialise": - let error; - console.log(prefix + "Got initialize for type " + CodecType[data.type as CodecType]); - switch (data.type as CodecType) { - case CodecType.OPUS_MUSIC: - codecInstance = new OpusWorker(2, OpusType.AUDIO); - break; - case CodecType.OPUS_VOICE: - codecInstance = new OpusWorker(1, OpusType.VOIP); - break; - default: - error = "Could not find worker type!"; - console.error("Could not resolve opus type!"); - break; - } - - error = error || codecInstance.initialise(); - if(error) - res["message"] = error; - else - res["success"] = true; - break; - case "encodeSamples": - let encodeArray = new Float32Array(data.dataLength); - for(let index = 0; index < encodeArray.length; index++) - encodeArray[index] = data.data[index]; - - let encodeResult = codecInstance.encode(encodeArray); - - if(typeof encodeResult === "string") { - res.message = encodeResult; - } else { - res.success = true; - res.data = encodeResult; - res.dataLength = encodeResult.length; - } - break; - case "decodeSamples": - let decodeArray = new Uint8Array(data.dataLength); - for(let index = 0; index < decodeArray.length; index++) - decodeArray[index] = data.data[index]; - - let decodeResult = codecInstance.decode(decodeArray); - - if(typeof decodeResult === "string") { - res.message = decodeResult; - } else { - res.success = true; - res.data = decodeResult; - res.dataLength = decodeResult.length; - } - break; - case "reset": - codecInstance.reset(); - break; - default: - console.error(prefix + "Unknown type " + data.command); - } - - if(res.token && res.token.length > 0) sendMessage(res, e.origin); -}; +export let codecInstance: CodecWorker; function printMessageToServerTab(message: string) { /* @@ -95,7 +35,105 @@ function printMessageToServerTab(message: string) { } declare function postMessage(message: any): void; -function sendMessage(message: any, origin?: string){ +function sendMessage(message: any, origin?: string) { message["timestamp"] = Date.now(); postMessage(message); -} \ No newline at end of file +} + +let globally_initialized = false; +let global_initialize_result; + +/** + * @param command + * @param data + * @return string on error or object on success + */ +async function handle_message(command: string, data: any) : Promise { + switch (command) { + case "global-initialize": + const init_result = globally_initialized ? global_initialize_result : await initialize_callback(); + globally_initialized = true; + + if(typeof init_result === "string") + return init_result; + + return {}; + case "initialise": + console.log(prefix + "Got initialize for type " + CodecType[data.type as CodecType]); + if(!supported_types[data.type]) + return "type unsupported"; + + try { + codecInstance = await supported_types[data.type](data.options); + } catch(ex) { + console.error(prefix + "Failed to allocate codec: %o", ex); + return typeof ex === "string" ? ex : "failed to allocate codec"; + } + + const error = codecInstance.initialise(); + if(error) return error; + + return {}; + case "encodeSamples": + let encodeArray = new Float32Array(data.length); + for(let index = 0; index < encodeArray.length; index++) + encodeArray[index] = data.data[index]; + + let encodeResult = codecInstance.encode(encodeArray); + if(typeof encodeResult === "string") + return encodeResult; + else + return { data: encodeResult, length: encodeResult.length }; + case "decodeSamples": + let decodeArray = new Uint8Array(data.length); + for(let index = 0; index < decodeArray.length; index++) + decodeArray[index] = data.data[index]; + + let decodeResult = codecInstance.decode(decodeArray); + if(typeof decodeResult === "string") + return decodeResult; + else + return { data: decodeResult, length: decodeResult.length }; + case "reset": + codecInstance.reset(); + break; + default: + return "unknown command"; + } +} + + +const handle_message_event = (e: MessageEvent) => { + const token = e.data.token; + const received = Date.now(); + + const send_result = result => { + const data = {}; + if(typeof result === "object") { + data["result"] = result; + data["success"] = true; + } else if(typeof result === "string") { + data["error"] = result; + data["success"] = false; + } else { + data["error"] = "invalid result"; + data["success"] = false; + } + data["token"] = token; + data["timestamp_received"] = received; + data["timestamp_send"] = Date.now(); + + sendMessage(data, e.origin); + }; + handle_message(e.data.command, e.data.data).then(res => { + if(token) { + send_result(res); + } + }).catch(error => { + console.warn("An error has been thrown while handing command %s: %o", e.data.command, error); + if(token) { + send_result(typeof error === "string" ? error : "unexpected exception has been thrown"); + } + }); +}; +addEventListener("message", handle_message_event); \ No newline at end of file diff --git a/web/js/workers/codec/OpusCodec.ts b/web/js/workers/codec/OpusCodec.ts index 9bcf391a..b89fbc5c 100644 --- a/web/js/workers/codec/OpusCodec.ts +++ b/web/js/workers/codec/OpusCodec.ts @@ -1,78 +1,82 @@ -/// +import * as cworker from "./CodecWorker"; +import {CodecType} from "tc-backend/web/codec/Codec"; +import {CodecWorker} from "./CodecWorker"; + +const prefix = "OpusWorker"; + +declare global { + interface Window { + __init_em_module: ((Module: any) => void)[]; + } +} +self.__init_em_module = self.__init_em_module || []; const WASM_ERROR_MESSAGES = [ 'no native wasm support detected' ]; -this["Module"] = this["Module"] || ({} as any); /* its required to cast {} to any!*/ +let Module; +self.__init_em_module.push(m => Module = m); +const runtime_initialize_promise = new Promise((resolve, reject) => { + self.__init_em_module.push(Module => { + const cleanup = () => { + Module['onRuntimeInitialized'] = undefined; + Module['onAbort'] = undefined; + }; -let initialized = false; -Module['onRuntimeInitialized'] = function() { - initialized = true; - console.log(prefix + "Initialized!"); + Module['onRuntimeInitialized'] = () => { + cleanup(); + resolve(); + }; - sendMessage({ - token: workerCallbackToken, - type: "loaded", - success: true - }) -}; + Module['onAbort'] = error => { + cleanup(); + + let message; + if(error instanceof DOMException) + message = "DOMException (" + error.name + "): " + error.code + " => " + error.message; + else { + abort_message = error; + message = error; + if(error.indexOf("no binaryen method succeeded") != -1) { + for(const error of WASM_ERROR_MESSAGES) { + if(last_error_message.indexOf(error) != -1) { + message = "no native wasm support detected, but its required"; + break; + } + } + } + } + + reject(message); + } + }); +}); let abort_message: string = undefined; let last_error_message: string; +self.__init_em_module.push(Module => { + Module['print'] = function() { + if(arguments.length == 1 && arguments[0] == abort_message) + return; /* we don't need to reprint the abort message! */ -Module['print'] = function() { - if(arguments.length == 1 && arguments[0] == abort_message) - return; /* we don't need to reprint the abort message! */ - console.log(...arguments); -}; + console.log("Print: ", ...arguments); + }; -Module['printErr'] = function() { - if(arguments.length == 1 && arguments[0] == abort_message) - return; /* we don't need to reprint the abort message! */ + Module['printErr'] = function() { + if(arguments.length == 1 && arguments[0] == abort_message) + return; /* we don't need to reprint the abort message! */ - last_error_message = arguments[0]; - for(const suppress of WASM_ERROR_MESSAGES) - if((arguments[0] as string).indexOf(suppress) != -1) - return; + last_error_message = arguments[0]; + for(const suppress of WASM_ERROR_MESSAGES) + if((arguments[0] as string).indexOf(suppress) != -1) + return; - console.error(...arguments); -}; + console.error("Error: ",...arguments); + }; -Module['onAbort'] = (message: string | DOMException) => { - /* no native wasm support detected */ - Module['onAbort'] = undefined; - - if(message instanceof DOMException) - message = "DOMException (" + message.name + "): " + message.code + " => " + message.message; - else { - abort_message = message; - if(message.indexOf("no binaryen method succeeded") != -1) - for(const error of WASM_ERROR_MESSAGES) - if(last_error_message.indexOf(error) != -1) { - message = "no native wasm support detected, but its required"; - break; - } - } - - sendMessage({ - token: workerCallbackToken, - type: "loaded", - success: false, - message: message - }); -}; - -try { - console.log("Node init!"); Module['locateFile'] = file => "../../wasm/" + file; - importScripts("../../wasm/TeaWeb-Worker-Codec-Opus.js"); -} catch (e) { - if(typeof(Module['onAbort']) === "function") { - console.log(e); - Module['onAbort']("Failed to load native scripts"); - } /* else the error had been already handled because its a WASM error */ -} +}); enum OpusType { VOIP = 2048, @@ -89,6 +93,7 @@ class OpusWorker implements CodecWorker { private fn_decode: any; private fn_encode: any; private fn_reset: any; + private fn_error_message: any; private bufferSize = 4096 * 2; private encodeBufferRaw: any; @@ -106,11 +111,12 @@ class OpusWorker implements CodecWorker { } initialise?() : string { - this.fn_newHandle = cwrap("codec_opus_createNativeHandle", "number", ["number", "number"]); - this.fn_decode = cwrap("codec_opus_decode", "number", ["number", "number", "number", "number"]); + this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "number", ["number", "number"]); + this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["number", "number", "number", "number"]); /* codec_opus_decode(handle, buffer, length, maxlength) */ - this.fn_encode = cwrap("codec_opus_encode", "number", ["number", "number", "number", "number"]); - this.fn_reset = cwrap("codec_opus_reset", "number", ["number"]); + this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["number", "number", "number", "number"]); + this.fn_reset = Module.cwrap("codec_opus_reset", "number", ["number"]); + this.fn_error_message = Module.cwrap("opus_error_message", "string", ["number"]); this.nativeHandle = this.fn_newHandle(this.channelCount, this.type); @@ -127,12 +133,8 @@ class OpusWorker implements CodecWorker { decode(data: Uint8Array): Float32Array | string { if (data.byteLength > this.decodeBuffer.byteLength) return "Data to long!"; this.decodeBuffer.set(data); - //console.log("decode(" + data.length + ")"); - //console.log(data); let result = this.fn_decode(this.nativeHandle, this.decodeBuffer.byteOffset, data.byteLength, this.decodeBuffer.byteLength); - if (result < 0) { - return "invalid result on decode (" + result + ")"; - } + if (result < 0) return this.fn_error_message(result); return Module.HEAPF32.slice(this.decodeBuffer.byteOffset / 4, (this.decodeBuffer.byteOffset / 4) + (result * this.channelCount)); } @@ -140,9 +142,7 @@ class OpusWorker implements CodecWorker { this.encodeBuffer.set(data); let result = this.fn_encode(this.nativeHandle, this.encodeBuffer.byteOffset, data.length, this.encodeBuffer.byteLength); - if (result < 0) { - return "invalid result on encode (" + result + ")"; - } + if (result < 0) return this.fn_error_message(result); let buf = Module.HEAP8.slice(this.encodeBuffer.byteOffset, this.encodeBuffer.byteOffset + result); return Uint8Array.from(buf); } @@ -151,4 +151,25 @@ class OpusWorker implements CodecWorker { console.log(prefix + " Reseting opus codec!"); this.fn_reset(this.nativeHandle); } -} \ No newline at end of file +} +cworker.register_codec(CodecType.OPUS_MUSIC, async () => new OpusWorker(2, OpusType.AUDIO)); +cworker.register_codec(CodecType.OPUS_VOICE, async () => new OpusWorker(1, OpusType.VOIP)); + +cworker.set_initialize_callback(async () => { + try { + require("tc-generated/codec/opus"); + } catch (e) { + if(Module) { + if(typeof(Module['onAbort']) === "function") { + Module['onAbort']("Failed to load native scripts"); + } /* else the error had been already handled because its a WASM error */ + } else { + throw e; + } + } + if(!Module) + throw "Missing module handle"; + + await runtime_initialize_promise; + return true; +}); \ No newline at end of file diff --git a/web/js/workers/codec/index.ts b/web/js/workers/codec/index.ts new file mode 100644 index 00000000..2cdf4199 --- /dev/null +++ b/web/js/workers/codec/index.ts @@ -0,0 +1 @@ +require("./OpusCodec"); \ No newline at end of file diff --git a/web/js/workers/tsconfig_worker_codec.json b/web/js/workers/tsconfig_worker_codec.json deleted file mode 100644 index f2b9fa21..00000000 --- a/web/js/workers/tsconfig_worker_codec.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "module": "none", - "target": "es6", - "sourceMap": true, - "outFile": "WorkerCodec.js" - }, - "include": [ - "../../types/" - ], - "files": [ - "codec/CodecWorker.ts", - "codec/OpusCodec.ts", - "../codec/Codec.ts" - ] -} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 34b9cd30..55222cbb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,10 @@ const path = require('path'); +const webpack = require("webpack"); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CircularDependencyPlugin = require('circular-dependency-plugin') +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const ManifestGenerator = require("./webpack/ManifestPlugin"); +const WorkerPlugin = require('worker-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const isDevelopment = process.env.NODE_ENV === 'development'; module.exports = { @@ -11,10 +15,16 @@ module.exports = { devtool: 'inline-source-map', mode: "development", plugins: [ + new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: isDevelopment ? '[name].css' : '[name].[hash].css', chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' }), + new ManifestGenerator({ + file: path.join(__dirname, "dist/manifest.json") + }), + new WorkerPlugin(), + //new BundleAnalyzerPlugin() /* new CircularDependencyPlugin({ //exclude: /a\.js|node_modules/, @@ -23,6 +33,12 @@ module.exports = { cwd: process.cwd(), }) */ + /* + new webpack.optimize.AggressiveSplittingPlugin({ + minSize: 1024 * 128, + maxSize: 1024 * 1024 + }) + */ ], module: { rules: [ @@ -58,49 +74,28 @@ module.exports = { } } ] - }, + } ], }, resolve: { extensions: ['.tsx', '.ts', '.js', ".scss"], alias: { "tc-shared": path.resolve(__dirname, "shared/js"), - "tc-backend": path.resolve(__dirname, "web/js") + "tc-backend/web": path.resolve(__dirname, "web/js"), + "tc-backend": path.resolve(__dirname, "web/js"), + "tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"), //"tc-backend": path.resolve(__dirname, "shared/backend.d"), }, }, externals: { - "tc-loader": "umd loader" + "tc-loader": "window loader" }, output: { - filename: 'shared-app.js', + filename: '[contenthash].js', path: path.resolve(__dirname, 'dist'), - libraryTarget: "umd", - library: "shared" + publicPath: "js/" }, optimization: { - /* - splitChunks: { - chunks: 'async', - minSize: 1, - maxSize: 500000, - minChunks: 1, - maxAsyncRequests: 6, - maxInitialRequests: 4, - automaticNameDelimiter: '~', - automaticNameMaxLength: 30, - cacheGroups: { - defaultVendors: { - test: /[\\/]node_modules[\\/]/, - priority: -10 - }, - default: { - minChunks: 2, - priority: -20, - reuseExistingChunk: true - } - } - } - */ + splitChunks: { } } }; \ No newline at end of file diff --git a/webpack/ManifestPlugin.ts b/webpack/ManifestPlugin.ts new file mode 100644 index 00000000..c7381fa4 --- /dev/null +++ b/webpack/ManifestPlugin.ts @@ -0,0 +1,49 @@ +import * as webpack from "webpack"; +import * as fs from "fs"; + +interface Options { + file?: string; +} + +class ManifestGenerator { + private manifest_content; + + readonly options: Options; + constructor(options: Options) { + this.options = options || {}; + } + + apply(compiler: webpack.Compiler) { + compiler.hooks.afterCompile.tap(this.constructor.name, compilation => { + const chunks_data = {}; + for(const chunk_group of compilation.chunkGroups) { + console.log(chunk_group.options.name); + const js_files = []; + for(const chunk of chunk_group.chunks) { + if(chunk.files.length !== 1) throw "expected only one file per chunk"; + + const file = chunk.files[0]; + console.log("Chunk: %s - %s - %s", chunk.id, chunk.hash, file); + //console.log(chunk); + //console.log(" - %s - %o", chunk.id, chunk); + js_files.push({ + hash: chunk.hash, + file: file + }) + } + chunks_data[chunk_group.options.name] = js_files; + } + + this.manifest_content = { + version: 1, + chunks: chunks_data + }; + }); + + compiler.hooks.done.tap(this.constructor.name, () => { + fs.writeFileSync(this.options.file || "manifest.json", JSON.stringify(this.manifest_content)); + }); + } +} + +export = ManifestGenerator; \ No newline at end of file From bc9375afa136076a787e27acbf779fdf47ad6060 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 02:11:55 +0200 Subject: [PATCH 10/23] Cleaned up the opus worker a bit --- asm/CMakeLists.txt | 30 ++-- asm/src/opus.cpp | 224 ++++++++++++---------------- web/js/workers/codec/CodecWorker.ts | 33 +--- web/js/workers/codec/OpusCodec.ts | 64 ++++---- webpack.config.js | 2 - 5 files changed, 155 insertions(+), 198 deletions(-) diff --git a/asm/CMakeLists.txt b/asm/CMakeLists.txt index f82daa65..ead1f8c6 100644 --- a/asm/CMakeLists.txt +++ b/asm/CMakeLists.txt @@ -1,21 +1,25 @@ cmake_minimum_required(VERSION 3.9) project(TeaWeb-Native) -set (CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_FLAGS_DEBUG "") #Override some config values from the parent project -set(CMAKE_CXX_COMPILER "emcc") -set(CMAKE_C_COMPILER "emcc") -set(CMAKE_C_LINK_EXECUTABLE "emcc") +set (CMAKE_CXX_STANDARD 17) + + +function(import_opus) + # Native SIMD isn't supported yet by most browsers (only experimental) + # But since opus already detects if emscripten is able to handle SIMD we have no need to disable this explicitly + + # Disable the math.h warning spam: + # #warning "Don't have the functions lrint() and lrintf ()." + # #warning "Replacing these functions with a standard C cast." + set(CMAKE_C_FLAGS "-Wno-#warnings") + set(OPUS_STACK_PROTECTOR OFF CACHE BOOL "" FORCE) + add_subdirectory(libraries/opus/) +endfunction() +import_opus() + set(CMAKE_CXX_FLAGS "-O3 --llvm-lto 1 --memory-init-file 0 -s WASM=1 -s ASSERTIONS=1") # -s ALLOW_MEMORY_GROWTH=1 -O3 -set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_EXE_LINKER_FLAGS "-s EXPORTED_FUNCTIONS='[\"_malloc\", \"_free\"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]' -s ENVIRONMENT='worker' --pre-js ${CMAKE_SOURCE_DIR}/init.js") # -#add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/generated/") -include_directories(libraries/tommath/) -include_directories(libraries/tomcrypt/src/headers) -include_directories(libraries/opus/include/) -add_definitions(-DLTM_DESC) - add_executable(TeaWeb-Worker-Codec-Opus src/opus.cpp) -target_link_libraries(TeaWeb-Worker-Codec-Opus ${CMAKE_CURRENT_SOURCE_DIR}/libraries/opus/out/lib/libopus.a) +target_link_libraries(TeaWeb-Worker-Codec-Opus opus) diff --git a/asm/src/opus.cpp b/asm/src/opus.cpp index 854ad3e7..bdc1030a 100644 --- a/asm/src/opus.cpp +++ b/asm/src/opus.cpp @@ -1,138 +1,106 @@ #include +#include +#include #include #include -using namespace std; +typedef std::unique_ptr opus_encoder_t; +typedef std::unique_ptr opus_decoder_t; +struct OpusHandle { + opus_encoder_t encoder{nullptr, opus_encoder_destroy}; + opus_decoder_t decoder{nullptr, opus_decoder_destroy}; + + size_t channelCount{1}; + size_t sampleRate{48000}; + int opusType{OPUS_APPLICATION_AUDIO}; +}; + +constexpr std::array opus_errors = { + "One or more invalid/out of range arguments", //-1 (OPUS_BAD_ARG) + "Not enough bytes allocated in the buffer", //-2 (OPUS_BUFFER_TOO_SMALL) + "An internal error was detected", //-3 (OPUS_INTERNAL_ERROR) + "The compressed data passed is corrupted", //-4 (OPUS_INVALID_PACKET) + "Invalid/unsupported request number", //-5 (OPUS_UNIMPLEMENTED) + "An encoder or decoder structure is invalid or already freed", //-6 (OPUS_INVALID_STATE) + "Memory allocation has failed" //-7 (OPUS_ALLOC_FAIL) +}; + +inline std::string_view opus_error_message(int error) { + error = abs(error); + if (error > 0 && error <= 7) return opus_errors[error - 1]; + return "undefined error"; +} + +inline bool reinitialize_decoder(OpusHandle *handle) { + int error; + handle->decoder.reset(opus_decoder_create(handle->sampleRate, handle->channelCount, &error)); + if(error != OPUS_OK) { + printf("Failed to create decoder (%s)\n", opus_error_message(error).data()); + return false; + } + return true; +} + +inline bool reinitialize_encoder(OpusHandle *handle) { + int error; + handle->encoder.reset(opus_encoder_create(handle->sampleRate, handle->channelCount, handle->opusType, &error)); + if (error != OPUS_OK) { + printf("Failed to create encoder (%s)\n", opus_error_message(error).data()); + return false; + } + + if(error = opus_encoder_ctl(&*handle->encoder, OPUS_SET_COMPLEXITY(1)); error != OPUS_OK) { + printf("Failed to setup encoder (%s)\n", opus_error_message(error).data()); + return false; + } + //TODO: May set OPUS_SET_BITRATE(4740)? + //TODO: Is the encoder event needed anymore? Or is it just overhead + return true; +} + +#ifdef __cplusplus extern "C" { - struct OpusHandle { - OpusEncoder* encoder = nullptr; - OpusDecoder* decoder = nullptr; +#endif +EMSCRIPTEN_KEEPALIVE +OpusHandle *codec_opus_createNativeHandle(size_t channelCount, int type) { + auto codec = new OpusHandle{}; + codec->opusType = type; + codec->channelCount = channelCount; + codec->sampleRate = 48000; + if (!reinitialize_decoder(codec)) return nullptr; + if (!reinitialize_encoder(codec)) return nullptr; + return codec; +} - size_t channelCount = 1; - size_t sampleRate = 48000; - int opusType = OPUS_APPLICATION_AUDIO; - }; +EMSCRIPTEN_KEEPALIVE +void codec_opus_deleteNativeHandle(OpusHandle *codec) { + if (!codec) return; - const char* opus_errors[7] = { - "One or more invalid/out of range arguments", //-1 (OPUS_BAD_ARG) - "Not enough bytes allocated in the buffer", //-2 (OPUS_BUFFER_TOO_SMALL) - "An internal error was detected", //-3 (OPUS_INTERNAL_ERROR) - "The compressed data passed is corrupted", //-4 (OPUS_INVALID_PACKET) - "Invalid/unsupported request number", //-5 (OPUS_UNIMPLEMENTED) - "An encoder or decoder structure is invalid or already freed", //-6 (OPUS_INVALID_STATE) - "Memory allocation has failed" //-7 (OPUS_ALLOC_FAIL) - }; + codec->decoder.reset(); + codec->encoder.reset(); + delete codec; +} - EMSCRIPTEN_KEEPALIVE - inline const char* opus_error_message(int error) { - error = abs(error); - if(error > 0 && error <= 7) return opus_errors[error - 1]; - return "undefined error"; - } +EMSCRIPTEN_KEEPALIVE +int codec_opus_encode(OpusHandle *handle, uint8_t *buffer, size_t length, size_t maxLength) { + auto result = opus_encode_float(&*handle->encoder, (float *) buffer, length / handle->channelCount, buffer, maxLength); + if (result < 0) return result; + return result; +} - inline int currentMillies() { - return EM_ASM_INT({ return Date.now(); }); - } +EMSCRIPTEN_KEEPALIVE +int codec_opus_decode(OpusHandle *handle, uint8_t *buffer, size_t length, size_t maxLength) { + auto result = opus_decode_float(&*handle->decoder, buffer, length, (float *) buffer, maxLength / sizeof(float) / handle->channelCount, false); + if (result < 0) return result; + return result; +} - #define _S(x) #x - #define INVOKE_OPUS(result, method, ...) \ - result = method( __VA_ARGS__ ); \ - if(error != 0){ \ - printf("Got opus error while invoking %s. Code: %d Message: %s\n", _S(method), error, opus_error_message(error)); \ - return false; \ - } - - inline bool reinitialize_decoder(OpusHandle *handle) { - if (handle->decoder) - opus_decoder_destroy(handle->decoder); - - int error = 0; - INVOKE_OPUS(handle->decoder, opus_decoder_create, 48000, handle->channelCount, &error); - return true; - } - - inline bool reinitialize_encoder(OpusHandle *handle) { - if (handle->encoder) - opus_encoder_destroy(handle->encoder); - - int error = 0; - INVOKE_OPUS(handle->encoder, opus_encoder_create, 48000, handle->channelCount, handle->opusType, &error); - INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_COMPLEXITY(1)); - //INVOKE_OPUS(error, opus_encoder_ctl, handle->encoder, OPUS_SET_BITRATE(4740)); - - /* //This method is obsolete! - EM_ASM( - printMessageToServerTab('Encoder initialized!'); - printMessageToServerTab(' Comprexity: 1'); - printMessageToServerTab(' Bitrate: 4740'); - ); - */ - - return true; - } - - EMSCRIPTEN_KEEPALIVE - OpusHandle* codec_opus_createNativeHandle(size_t channelCount, int type) { - printf("Initialize opus. (Channel count: %d Sample rate: %d Type: %d)!\n", channelCount, 48000, type); - auto codec = new OpusHandle{}; - codec->opusType = type; - codec->channelCount = channelCount; - if(!reinitialize_decoder(codec)) return nullptr; - if(!reinitialize_encoder(codec)) return nullptr; - return codec; - } - - EMSCRIPTEN_KEEPALIVE - void codec_opus_deleteNativeHandle(OpusHandle* codec) { - if(!codec) return; - - if(codec->decoder) opus_decoder_destroy(codec->decoder); - codec->decoder = nullptr; - - if(codec->encoder) opus_encoder_destroy(codec->encoder); - codec->encoder = nullptr; - - delete codec; - } - - EMSCRIPTEN_KEEPALIVE - int codec_opus_encode(OpusHandle* handle, uint8_t* buffer, size_t length, size_t maxLength) { - auto begin = currentMillies(); - auto result = opus_encode_float(handle->encoder, (float*) buffer, length / handle->channelCount, buffer, maxLength); - if(result < 0) return result; - auto end = currentMillies(); - /* //This message is obsolete - EM_ASM({ - printMessageToServerTab("codec_opus_encode(...) tooks " + $0 + "ms to execute!"); - }, end - begin); - */ - return result; - } - - EMSCRIPTEN_KEEPALIVE - int codec_opus_decode(OpusHandle* handle, uint8_t* buffer, size_t length, size_t maxLength) { - auto result = opus_decode_float(handle->decoder, buffer, length, (float*) buffer, maxLength / sizeof(float) / handle->channelCount, false); - if(result < 0) return result; //Failed - return result; - } - - EMSCRIPTEN_KEEPALIVE - int codec_opus_changeApplication(OpusHandle* handle, int type) { - handle->opusType = type; - if(type != OPUS_APPLICATION_VOIP && type != OPUS_APPLICATION_AUDIO && type != OPUS_APPLICATION_RESTRICTED_LOWDELAY) - return 1; - return opus_encoder_ctl(handle->encoder, OPUS_SET_APPLICATION(type)); - } - - EMSCRIPTEN_KEEPALIVE - int codec_opus_reset(OpusHandle* handle) { - if(!reinitialize_decoder(handle)) return 0; - if(!reinitialize_encoder(handle)) return 0; - return 1; - } -/* -opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate)); -opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); -opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type)); - */ -} \ No newline at end of file +EMSCRIPTEN_KEEPALIVE +int codec_opus_reset(OpusHandle *handle) { + if (!reinitialize_decoder(handle)) return 0; + if (!reinitialize_encoder(handle)) return 0; + return 1; +} +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/web/js/workers/codec/CodecWorker.ts b/web/js/workers/codec/CodecWorker.ts index 2b88482f..2184d58c 100644 --- a/web/js/workers/codec/CodecWorker.ts +++ b/web/js/workers/codec/CodecWorker.ts @@ -22,24 +22,7 @@ export function set_initialize_callback(callback: () => Promise) initialize_callback = callback; } -export let codecInstance: CodecWorker; - -function printMessageToServerTab(message: string) { - /* - sendMessage({ - token: workerCallbackToken, - type: "chatmessage_server", - message: message - }); - */ -} - -declare function postMessage(message: any): void; -function sendMessage(message: any, origin?: string) { - message["timestamp"] = Date.now(); - postMessage(message); -} - +export let codec_instance: CodecWorker; let globally_initialized = false; let global_initialize_result; @@ -59,18 +42,18 @@ async function handle_message(command: string, data: any) : Promise { data["timestamp_received"] = received; data["timestamp_send"] = Date.now(); - sendMessage(data, e.origin); + postMessage(data, undefined); }; handle_message(e.data.command, e.data.data).then(res => { if(token) { diff --git a/web/js/workers/codec/OpusCodec.ts b/web/js/workers/codec/OpusCodec.ts index b89fbc5c..91eca0e1 100644 --- a/web/js/workers/codec/OpusCodec.ts +++ b/web/js/workers/codec/OpusCodec.ts @@ -2,8 +2,6 @@ import * as cworker from "./CodecWorker"; import {CodecType} from "tc-backend/web/codec/Codec"; import {CodecWorker} from "./CodecWorker"; -const prefix = "OpusWorker"; - declare global { interface Window { __init_em_module: ((Module: any) => void)[]; @@ -60,7 +58,7 @@ self.__init_em_module.push(Module => { if(arguments.length == 1 && arguments[0] == abort_message) return; /* we don't need to reprint the abort message! */ - console.log("Print: ", ...arguments); + console.log(...arguments); }; Module['printErr'] = function() { @@ -72,7 +70,7 @@ self.__init_em_module.push(Module => { if((arguments[0] as string).indexOf(suppress) != -1) return; - console.error("Error: ",...arguments); + console.error(...arguments); }; Module['locateFile'] = file => "../../wasm/" + file; @@ -84,22 +82,31 @@ enum OpusType { RESTRICTED_LOWDELAY = 2051 } +const OPUS_ERROR_CODES = [ + "One or more invalid/out of range arguments", //-1 (OPUS_BAD_ARG) + "Not enough bytes allocated in the buffer", //-2 (OPUS_BUFFER_TOO_SMALL) + "An internal error was detected", //-3 (OPUS_INTERNAL_ERROR) + "The compressed data passed is corrupted", //-4 (OPUS_INVALID_PACKET) + "Invalid/unsupported request number", //-5 (OPUS_UNIMPLEMENTED) + "An encoder or decoder structure is invalid or already freed", //-6 (OPUS_INVALID_STATE) + "Memory allocation has failed" //-7 (OPUS_ALLOC_FAIL) +]; + class OpusWorker implements CodecWorker { - private channelCount: number; + private readonly channelCount: number; + private readonly type: OpusType; private nativeHandle: any; - private type: OpusType; private fn_newHandle: any; private fn_decode: any; private fn_encode: any; private fn_reset: any; - private fn_error_message: any; - private bufferSize = 4096 * 2; - private encodeBufferRaw: any; - private encodeBuffer: Float32Array; - private decodeBufferRaw: any; - private decodeBuffer: Uint8Array; + private buffer_size = 4096 * 2; + private buffer: any; + + private encode_buffer: Float32Array; + private decode_buffer: Uint8Array; constructor(channelCount: number, type: OpusType) { this.channelCount = channelCount; @@ -113,42 +120,39 @@ class OpusWorker implements CodecWorker { initialise?() : string { this.fn_newHandle = Module.cwrap("codec_opus_createNativeHandle", "number", ["number", "number"]); this.fn_decode = Module.cwrap("codec_opus_decode", "number", ["number", "number", "number", "number"]); - /* codec_opus_decode(handle, buffer, length, maxlength) */ this.fn_encode = Module.cwrap("codec_opus_encode", "number", ["number", "number", "number", "number"]); this.fn_reset = Module.cwrap("codec_opus_reset", "number", ["number"]); - this.fn_error_message = Module.cwrap("opus_error_message", "string", ["number"]); this.nativeHandle = this.fn_newHandle(this.channelCount, this.type); - this.encodeBufferRaw = Module._malloc(this.bufferSize); - this.encodeBuffer = new Float32Array(Module.HEAPF32.buffer, this.encodeBufferRaw, this.bufferSize / 4); - - this.decodeBufferRaw = Module._malloc(this.bufferSize); - this.decodeBuffer = new Uint8Array(Module.HEAPU8.buffer, this.decodeBufferRaw, this.bufferSize); + this.buffer = Module._malloc(this.buffer_size); + this.encode_buffer = new Float32Array(Module.HEAPF32.buffer, this.buffer, Math.floor(this.buffer_size / 4)); + this.decode_buffer = new Uint8Array(Module.HEAPU8.buffer, this.buffer, this.buffer_size); return undefined; } deinitialise() { } //TODO decode(data: Uint8Array): Float32Array | string { - if (data.byteLength > this.decodeBuffer.byteLength) return "Data to long!"; - this.decodeBuffer.set(data); - let result = this.fn_decode(this.nativeHandle, this.decodeBuffer.byteOffset, data.byteLength, this.decodeBuffer.byteLength); - if (result < 0) return this.fn_error_message(result); - return Module.HEAPF32.slice(this.decodeBuffer.byteOffset / 4, (this.decodeBuffer.byteOffset / 4) + (result * this.channelCount)); + if (data.byteLength > this.decode_buffer.byteLength) return "supplied data exceeds internal buffer"; + this.decode_buffer.set(data); + + let result = this.fn_decode(this.nativeHandle, this.decode_buffer.byteOffset, data.byteLength, this.decode_buffer.byteLength); + if (result < 0) return OPUS_ERROR_CODES[-result] || "unknown decode error " + result; + + return Module.HEAPF32.slice(this.decode_buffer.byteOffset / 4, (this.decode_buffer.byteOffset / 4) + (result * this.channelCount)); } encode(data: Float32Array): Uint8Array | string { - this.encodeBuffer.set(data); + this.encode_buffer.set(data); - let result = this.fn_encode(this.nativeHandle, this.encodeBuffer.byteOffset, data.length, this.encodeBuffer.byteLength); - if (result < 0) return this.fn_error_message(result); - let buf = Module.HEAP8.slice(this.encodeBuffer.byteOffset, this.encodeBuffer.byteOffset + result); - return Uint8Array.from(buf); + let result = this.fn_encode(this.nativeHandle, this.encode_buffer.byteOffset, data.length, this.encode_buffer.byteLength); + if (result < 0) return OPUS_ERROR_CODES[-result] || "unknown encode error " + result; + + return Module.HEAP8.slice(this.encode_buffer.byteOffset, this.encode_buffer.byteOffset + result); } reset() { - console.log(prefix + " Reseting opus codec!"); this.fn_reset(this.nativeHandle); } } diff --git a/webpack.config.js b/webpack.config.js index 55222cbb..e2811529 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -33,12 +33,10 @@ module.exports = { cwd: process.cwd(), }) */ - /* new webpack.optimize.AggressiveSplittingPlugin({ minSize: 1024 * 128, maxSize: 1024 * 1024 }) - */ ], module: { rules: [ From 824e7c677b7f67fe8bad3a86e27e93433bcaa292 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 02:12:18 +0200 Subject: [PATCH 11/23] Removed not needed script --- asm/make_opus.sh | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100755 asm/make_opus.sh diff --git a/asm/make_opus.sh b/asm/make_opus.sh deleted file mode 100755 index d2b64667..00000000 --- a/asm/make_opus.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname $0)/libraries/opus/" || { echo "Failed to enter the opus directory."; exit 1; } - -[[ -d build_ ]] && { - rm -r build_ || { echo "failed to remove old build directory"; exit 1; } -} -mkdir build_ || exit 1 -cd build_ || exit 1 - -# Native SIMD isn't supported yet by most browsers (only experimental) -# So there is no need to build with that, it will make stuff even worse -simd_flags="-DOPUS_X86_MAY_HAVE_AVX=OFF -DOPUS_X86_MAY_HAVE_SSE4_1=OFF -DOPUS_X86_MAY_HAVE_SSE2=OFF -DOPUS_X86_MAY_HAVE_SSE=OFF" -emcmake cmake .. -DCMAKE_INSTALL_PREFIX="$(pwd)/../out/" -DOPUS_STACK_PROTECTOR=OFF ${simd_flags} || { - echo "failed to execute cmake" - exit 1 -} - -emmake make || { - echo "failed to build opus" - exit 1 -} -emmake make install || { - echo "failed to \"install\" opus" - exit 1 -} - -# Old: -#git checkout v1.1.2 -#./autogen.sh -#emconfigure ./configure --disable-extra-programs --disable-doc --disable-rtcd -#emmake make \ No newline at end of file From a789e5674f80782b85c0e26050f2064703645b9d Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 02:14:48 +0200 Subject: [PATCH 12/23] Improved opus build scripts --- asm/build.sh | 2 +- asm/download_compiled_files.sh | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/asm/build.sh b/asm/build.sh index b6365d56..d78e2bad 100644 --- a/asm/build.sh +++ b/asm/build.sh @@ -14,7 +14,7 @@ emcmake cmake .. || { exit 1 } -emmake make || { +emmake make -j"$(nproc --all)" || { echo "Failed to build file" exit 1 } \ No newline at end of file diff --git a/asm/download_compiled_files.sh b/asm/download_compiled_files.sh index 75fa65b3..7d227baa 100755 --- a/asm/download_compiled_files.sh +++ b/asm/download_compiled_files.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cd $(dirname $0) +cd "$(dirname "$0")" || exit 1 if [[ -d generated/ ]]; then rm -r generated @@ -15,19 +15,19 @@ mkdir generated exit 1 } -$(curl --version &> /dev/null) -[[ $? -ne 0 ]] && { +curl --version &> /dev/null; _exit_code=$? +[[ $_exit_code -ne 0 ]] && { echo "Missing CURL. Please install it" exit 1 } -curl https://web.teaspeak.de/wasm/TeaWeb-Worker-Codec-Opus.js --output generated/TeaWeb-Worker-Codec-Opus.js -[[ $? -ne 0 ]] && { +curl https://web.teaspeak.de/wasm/TeaWeb-Worker-Codec-Opus.js --output generated/TeaWeb-Worker-Codec-Opus.js; _exit_code=$? +[[ $_exit_code -ne 0 ]] && { echo "Failed to download opus worker library" exit 1 } -curl https://web.teaspeak.de/wasm/TeaWeb-Worker-Codec-Opus.wasm --output generated/TeaWeb-Worker-Codec-Opus.wasm -[[ $? -ne 0 ]] && { +curl https://web.teaspeak.de/wasm/TeaWeb-Worker-Codec-Opus.wasm --output generated/TeaWeb-Worker-Codec-Opus.wasm; _exit_code=$? +[[ $_exit_code -ne 0 ]] && { echo "Failed to download opus worker library natives" exit 1 } From 228c9d631ed630836b964834d9840875d27b7038 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 13:44:16 +0200 Subject: [PATCH 13/23] Fixed the identity worker and compile it with webpack --- dist/bundle.js | 6186 ----------------- package-lock.json | 466 +- package.json | 6 +- .../profiles/identities/TeamSpeakIdentity.ts | 274 +- .../js/workers/pow/{POWWorker.ts => index.ts} | 18 +- shared/{wat => js/workers}/pow/sha1.wat | 41 +- shared/wat/pow/sha1.wasm | Bin 1288 -> 0 bytes webpack.config.js | 6 + webpack/WatLoader.ts | 18 + 9 files changed, 634 insertions(+), 6381 deletions(-) delete mode 100644 dist/bundle.js rename shared/js/workers/pow/{POWWorker.ts => index.ts} (80%) rename shared/{wat => js/workers}/pow/sha1.wat (93%) delete mode 100644 shared/wat/pow/sha1.wasm create mode 100644 webpack/WatLoader.ts diff --git a/dist/bundle.js b/dist/bundle.js deleted file mode 100644 index 7a101cb5..00000000 --- a/dist/bundle.js +++ /dev/null @@ -1,6186 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./shared/js/main.ts"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/process/browser.js": -/*!*****************************************!*\ - !*** ./node_modules/process/browser.js ***! - \*****************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - - -/***/ }), - -/***/ "./node_modules/webpack/buildin/amd-options.js": -/*!****************************************!*\ - !*** (webpack)/buildin/amd-options.js ***! - \****************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -/* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {/* globals __webpack_amd_options__ */ -module.exports = __webpack_amd_options__; - -/* WEBPACK VAR INJECTION */}.call(this, {})) - -/***/ }), - -/***/ "./node_modules/webpack/buildin/global.js": -/*!***********************************!*\ - !*** (webpack)/buildin/global.js ***! - \***********************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -var g; - -// This works in non-strict mode -g = (function() { - return this; -})(); - -try { - // This works if eval is allowed (see CSP) - g = g || new Function("return this")(); -} catch (e) { - // This works if the window reference is available - if (typeof window === "object") g = window; -} - -// g can still be undefined, but nothing to do about it... -// We return undefined, instead of nothing here, so it's -// easier to handle this case. if(!global) { ...} - -module.exports = g; - - -/***/ }), - -/***/ "./shared/js/BrowserIPC.ts": -/*!*********************************!*\ - !*** ./shared/js/BrowserIPC.ts ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var bipc; -(function (bipc) { - function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - class BasicIPCHandler { - constructor() { - this._channels = []; - this._query_results = {}; - this._cert_accept_callbacks = {}; - this._cert_accept_succeeded = {}; - } - setup() { - this.unique_id = uuidv4(); - } - get_local_address() { return this.unique_id; } - handle_message(message) { - if (message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID) { - if (message.type == "process-query") { - log.debug(LogCategory.IPC, tr("Received a device query from %s."), message.sender); - this.send_message("process-query-response", { - request_query_id: message.data.query_id, - request_timestamp: message.data.timestamp, - device_id: this.unique_id, - protocol: BasicIPCHandler.PROTOCOL_VERSION - }, message.sender); - return; - } - } - else if (message.receiver === this.unique_id) { - if (message.type == "process-query-response") { - const response = message.data; - if (this._query_results[response.request_query_id]) - this._query_results[response.request_query_id].push(response); - else { - log.warn(LogCategory.IPC, tr("Received a query response for an unknown request.")); - } - return; - } - else if (message.type == "certificate-accept-callback") { - const data = message.data; - if (!this._cert_accept_callbacks[data.request_id]) { - log.warn(LogCategory.IPC, tr("Received certificate accept callback for an unknown request ID.")); - return; - } - this._cert_accept_callbacks[data.request_id](); - delete this._cert_accept_callbacks[data.request_id]; - this.send_message("certificate-accept-succeeded", {}, message.sender); - return; - } - else if (message.type == "certificate-accept-succeeded") { - if (!this._cert_accept_succeeded[message.sender]) { - log.warn(LogCategory.IPC, tr("Received certificate accept succeeded, but haven't a callback.")); - return; - } - this._cert_accept_succeeded[message.sender](); - return; - } - } - if (message.type === "channel") { - const data = message.data; - let channel_invoked = false; - for (const channel of this._channels) - if (channel.channel_id === data.channel_id && (typeof (channel.target_id) === "undefined" || channel.target_id === message.sender)) { - if (channel.message_handler) - channel.message_handler(message.sender, message.receiver === BasicIPCHandler.BROADCAST_UNIQUE_ID, data); - channel_invoked = true; - } - if (!channel_invoked) { - console.warn(tr("Received channel message for unknown channel (%s)"), data.channel_id); - } - } - } - create_channel(target_id, channel_id) { - let channel = { - target_id: target_id, - channel_id: channel_id || uuidv4(), - message_handler: undefined, - send_message: (type, data, target) => { - if (typeof target !== "undefined") { - if (typeof channel.target_id === "string" && target != channel.target_id) - throw "target id does not match channel target"; - } - this.send_message("channel", { - type: type, - data: data, - channel_id: channel.channel_id - }, target || channel.target_id || BasicIPCHandler.BROADCAST_UNIQUE_ID); - } - }; - this._channels.push(channel); - return channel; - } - channels() { return this._channels; } - delete_channel(channel) { - this._channels = this._channels.filter(e => e !== channel); - } - query_processes(timeout) { - return __awaiter(this, void 0, void 0, function* () { - const query_id = uuidv4(); - this._query_results[query_id] = []; - this.send_message("process-query", { - query_id: query_id, - timestamp: Date.now() - }); - yield new Promise(resolve => setTimeout(resolve, timeout || 250)); - const result = this._query_results[query_id]; - delete this._query_results[query_id]; - return result; - }); - } - register_certificate_accept_callback(callback) { - const id = uuidv4(); - this._cert_accept_callbacks[id] = callback; - return this.unique_id + ":" + id; - } - post_certificate_accpected(id, timeout) { - return new Promise((resolve, reject) => { - const data = id.split(":"); - const timeout_id = setTimeout(() => { - delete this._cert_accept_succeeded[data[0]]; - clearTimeout(timeout_id); - reject("timeout"); - }, timeout || 250); - this._cert_accept_succeeded[data[0]] = () => { - delete this._cert_accept_succeeded[data[0]]; - clearTimeout(timeout_id); - resolve(); - }; - this.send_message("certificate-accept-callback", { - request_id: data[1] - }, data[0]); - }); - } - } - BasicIPCHandler.BROADCAST_UNIQUE_ID = "00000000-0000-4000-0000-000000000000"; - BasicIPCHandler.PROTOCOL_VERSION = 1; - bipc.BasicIPCHandler = BasicIPCHandler; - class BroadcastChannelIPC extends BasicIPCHandler { - constructor() { - super(); - } - setup() { - super.setup(); - this.channel = new BroadcastChannel(BroadcastChannelIPC.CHANNEL_NAME); - this.channel.onmessage = this.on_message.bind(this); - this.channel.onmessageerror = this.on_error.bind(this); - } - on_message(event) { - if (typeof (event.data) !== "string") { - log.warn(LogCategory.IPC, tr("Received message with an invalid type (%s): %o"), typeof (event.data), event.data); - return; - } - let message; - try { - message = JSON.parse(event.data); - } - catch (error) { - log.error(LogCategory.IPC, tr("Received an invalid encoded message: %o"), event.data); - return; - } - super.handle_message(message); - } - on_error(event) { - log.warn(LogCategory.IPC, tr("Received error: %o"), event); - } - send_message(type, data, target) { - const message = {}; - message.sender = this.unique_id; - message.receiver = target ? target : BasicIPCHandler.BROADCAST_UNIQUE_ID; - message.timestamp = Date.now(); - message.type = type; - message.data = data; - this.channel.postMessage(JSON.stringify(message)); - } - } - BroadcastChannelIPC.CHANNEL_NAME = "TeaSpeak-Web"; - let connect; - (function (connect) { - class ConnectHandler { - constructor(ipc_handler) { - this.callback_available = () => false; - this.callback_execute = () => false; - this._pending_connect_offers = []; - this._pending_connects_requests = []; - this.ipc_handler = ipc_handler; - } - setup() { - this.ipc_channel = this.ipc_handler.create_channel(undefined, ConnectHandler.CHANNEL_NAME); - this.ipc_channel.message_handler = this.on_message.bind(this); - } - on_message(sender, broadcast, message) { - if (broadcast) { - if (message.type == "offer") { - const data = message.data; - const response = { - accepted: this.callback_available(data.data), - request_id: data.request_id - }; - if (response.accepted) { - log.debug(LogCategory.IPC, tr("Received new connect offer from %s: %s"), sender, data.request_id); - const ld = { - remote_handler: sender, - data: data.data, - id: data.request_id, - timeout: 0 - }; - this._pending_connect_offers.push(ld); - ld.timeout = setTimeout(() => { - log.debug(LogCategory.IPC, tr("Dropping connect request %s, because we never received an execute."), ld.id); - this._pending_connect_offers.remove(ld); - }, 120 * 1000); - } - this.ipc_channel.send_message("offer-answer", response, sender); - } - } - else { - if (message.type == "offer-answer") { - const data = message.data; - const request = this._pending_connects_requests.find(e => e.id === data.request_id); - if (!request) { - log.warn(LogCategory.IPC, tr("Received connect offer answer with unknown request id (%s)."), data.request_id); - return; - } - if (!data.accepted) { - log.debug(LogCategory.IPC, tr("Client %s rejected the connect offer (%s)."), sender, request.id); - return; - } - if (request.remote_handler) { - log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s), but offer has already been accepted."), sender, request.id); - return; - } - log.debug(LogCategory.IPC, tr("Client %s accepted the connect offer (%s). Request local acceptance."), sender, request.id); - request.remote_handler = sender; - clearTimeout(request.timeout); - request.callback_avail().then(flag => { - if (!flag) { - request.callback_failed("local avail rejected"); - return; - } - log.debug(LogCategory.IPC, tr("Executing connect with client %s"), request.remote_handler); - this.ipc_channel.send_message("execute", { - request_id: request.id - }, request.remote_handler); - request.timeout = setTimeout(() => { - request.callback_failed("connect execute timeout"); - }, 1000); - }).catch(error => { - log.error(LogCategory.IPC, tr("Local avail callback caused an error: %o"), error); - request.callback_failed(tr("local avail callback caused an error")); - }); - } - else if (message.type == "executed") { - const data = message.data; - const request = this._pending_connects_requests.find(e => e.id === data.request_id); - if (!request) { - log.warn(LogCategory.IPC, tr("Received connect executed with unknown request id (%s)."), data.request_id); - return; - } - if (request.remote_handler != sender) { - log.warn(LogCategory.IPC, tr("Received connect executed for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); - return; - } - log.debug(LogCategory.IPC, tr("Received connect executed response from client %s for request %s. Succeeded: %o (%s)"), sender, data.request_id, data.succeeded, data.message); - clearTimeout(request.timeout); - if (data.succeeded) - request.callback_success(); - else - request.callback_failed(data.message); - } - else if (message.type == "execute") { - const data = message.data; - const request = this._pending_connect_offers.find(e => e.id === data.request_id); - if (!request) { - log.warn(LogCategory.IPC, tr("Received connect execute with unknown request id (%s)."), data.request_id); - return; - } - if (request.remote_handler != sender) { - log.warn(LogCategory.IPC, tr("Received connect execute for request %s, but from wrong client: %s (expected %s)"), data.request_id, sender, request.remote_handler); - return; - } - clearTimeout(request.timeout); - this._pending_connect_offers.remove(request); - log.debug(LogCategory.IPC, tr("Executing connect for %s"), data.request_id); - const cr = this.callback_execute(request.data); - const response = { - request_id: data.request_id, - succeeded: typeof (cr) !== "string" && cr, - message: typeof (cr) === "string" ? cr : "", - }; - this.ipc_channel.send_message("executed", response, request.remote_handler); - } - } - } - post_connect_request(data, callback_avail) { - return new Promise((resolve, reject) => { - const pd = { - data: data, - id: uuidv4(), - timeout: 0, - callback_success: () => { - this._pending_connects_requests.remove(pd); - clearTimeout(pd.timeout); - resolve(); - }, - callback_failed: error => { - this._pending_connects_requests.remove(pd); - clearTimeout(pd.timeout); - reject(error); - }, - callback_avail: callback_avail, - }; - this._pending_connects_requests.push(pd); - this.ipc_channel.send_message("offer", { - request_id: pd.id, - data: pd.data - }); - pd.timeout = setTimeout(() => { - pd.callback_failed("received no response to offer"); - }, 50); - }); - } - } - ConnectHandler.CHANNEL_NAME = "connect"; - connect.ConnectHandler = ConnectHandler; - })(connect = bipc.connect || (bipc.connect = {})); - let mproxy; - (function (mproxy) { - class MethodProxy { - constructor(ipc_handler, connect_params) { - this._proxied_methods = {}; - this._proxied_callbacks = {}; - this.ipc_handler = ipc_handler; - this._ipc_parameters = connect_params; - this._connected = false; - this._slave = typeof (connect_params) !== "undefined"; - this._local = typeof (connect_params) !== "undefined" && connect_params.channel_id === "local" && connect_params.client_id === "local"; - } - setup() { - if (this._local) { - this._connected = true; - this.on_connected(); - } - else { - if (this._slave) - this._ipc_channel = this.ipc_handler.create_channel(this._ipc_parameters.client_id, this._ipc_parameters.channel_id); - else - this._ipc_channel = this.ipc_handler.create_channel(); - this._ipc_channel.message_handler = this._handle_message.bind(this); - if (this._slave) - this._ipc_channel.send_message("initialize", {}); - } - } - finalize() { - if (!this._local) { - if (this._connected) - this._ipc_channel.send_message("finalize", {}); - this.ipc_handler.delete_channel(this._ipc_channel); - this._ipc_channel = undefined; - } - for (const promise of Object.values(this._proxied_callbacks)) - promise.reject("disconnected"); - this._proxied_callbacks = {}; - this._connected = false; - this.on_disconnected(); - } - register_method(method) { - let method_name; - if (typeof method === "function") { - log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method.name); - method_name = method.name; - } - else { - log.debug(LogCategory.IPC, tr("Registering method proxy for %s"), method); - method_name = method; - } - if (!this[method_name]) - throw "method is missing in current object"; - this._proxied_methods[method_name] = this[method_name]; - if (!this._local) { - this[method_name] = (...args) => { - if (!this._connected) - return Promise.reject("not connected"); - const proxy_callback = { - promise_id: uuidv4() - }; - this._proxied_callbacks[proxy_callback.promise_id] = proxy_callback; - proxy_callback.promise = new Promise((resolve, reject) => { - proxy_callback.resolve = resolve; - proxy_callback.reject = reject; - }); - this._ipc_channel.send_message("invoke", { - promise_id: proxy_callback.promise_id, - arguments: [...args], - method_name: method_name - }); - return proxy_callback.promise; - }; - } - } - _handle_message(remote_id, boradcast, message) { - if (message.type === "finalize") { - this._handle_finalize(); - } - else if (message.type === "initialize") { - this._handle_remote_callback(remote_id); - } - else if (message.type === "invoke") { - this._handle_invoke(message.data); - } - else if (message.type === "result") { - this._handle_result(message.data); - } - } - _handle_finalize() { - this.on_disconnected(); - this.finalize(); - this._connected = false; - } - _handle_remote_callback(remote_id) { - if (!this._ipc_channel.target_id) { - if (this._slave) - throw "initialize wrong state!"; - this._ipc_channel.target_id = remote_id; - this.on_connected(); - this._ipc_channel.send_message("initialize", true); - } - else { - if (!this._slave) - throw "initialize wrong state!"; - this.on_connected(); - } - this._connected = true; - } - _send_result(promise_id, success, message) { - this._ipc_channel.send_message("result", { - promise_id: promise_id, - result: message, - success: success - }); - } - _handle_invoke(data) { - if (this._proxied_methods[data.method_name]) - throw "we could not invoke a local proxied method!"; - if (!this[data.method_name]) { - this._send_result(data.promise_id, false, "missing method"); - return; - } - try { - log.info(LogCategory.IPC, tr("Invoking method %s with arguments: %o"), data.method_name, data.arguments); - const promise = this[data.method_name](...data.arguments); - promise.then(result => { - log.info(LogCategory.IPC, tr("Result: %o"), result); - this._send_result(data.promise_id, true, result); - }).catch(error => { - this._send_result(data.promise_id, false, error); - }); - } - catch (error) { - this._send_result(data.promise_id, false, error); - return; - } - } - _handle_result(data) { - if (!this._proxied_callbacks[data.promise_id]) { - console.warn(tr("Received proxy method result for unknown promise")); - return; - } - const callback = this._proxied_callbacks[data.promise_id]; - delete this._proxied_callbacks[data.promise_id]; - if (data.success) - callback.resolve(data.result); - else - callback.reject(data.result); - } - generate_connect_parameters() { - if (this._slave) - throw "only masters can generate connect parameters!"; - if (!this._ipc_channel) - throw "please call setup() before"; - return { - channel_id: this._ipc_channel.channel_id, - client_id: this.ipc_handler.get_local_address() - }; - } - is_slave() { return this._local || this._slave; } - is_master() { return this._local || !this._slave; } - } - mproxy.MethodProxy = MethodProxy; - })(mproxy = bipc.mproxy || (bipc.mproxy = {})); - let handler; - let connect_handler; - function setup() { - if (!supported()) - return; - handler = new BroadcastChannelIPC(); - handler.setup(); - connect_handler = new connect.ConnectHandler(handler); - connect_handler.setup(); - } - bipc.setup = setup; - function get_handler() { - return handler; - } - bipc.get_handler = get_handler; - function get_connect_handler() { - return connect_handler; - } - bipc.get_connect_handler = get_connect_handler; - function supported() { - return typeof (window.BroadcastChannel) !== "undefined"; - } - bipc.supported = supported; -})(bipc = exports.bipc || (exports.bipc = {})); - - -/***/ }), - -/***/ "./shared/js/PPTListener.ts": -/*!**********************************!*\ - !*** ./shared/js/PPTListener.ts ***! - \**********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var KeyCode; -(function (KeyCode) { - KeyCode[KeyCode["KEY_CANCEL"] = 3] = "KEY_CANCEL"; - KeyCode[KeyCode["KEY_HELP"] = 6] = "KEY_HELP"; - KeyCode[KeyCode["KEY_BACK_SPACE"] = 8] = "KEY_BACK_SPACE"; - KeyCode[KeyCode["KEY_TAB"] = 9] = "KEY_TAB"; - KeyCode[KeyCode["KEY_CLEAR"] = 12] = "KEY_CLEAR"; - KeyCode[KeyCode["KEY_RETURN"] = 13] = "KEY_RETURN"; - KeyCode[KeyCode["KEY_ENTER"] = 14] = "KEY_ENTER"; - KeyCode[KeyCode["KEY_SHIFT"] = 16] = "KEY_SHIFT"; - KeyCode[KeyCode["KEY_CONTROL"] = 17] = "KEY_CONTROL"; - KeyCode[KeyCode["KEY_ALT"] = 18] = "KEY_ALT"; - KeyCode[KeyCode["KEY_PAUSE"] = 19] = "KEY_PAUSE"; - KeyCode[KeyCode["KEY_CAPS_LOCK"] = 20] = "KEY_CAPS_LOCK"; - KeyCode[KeyCode["KEY_ESCAPE"] = 27] = "KEY_ESCAPE"; - KeyCode[KeyCode["KEY_SPACE"] = 32] = "KEY_SPACE"; - KeyCode[KeyCode["KEY_PAGE_UP"] = 33] = "KEY_PAGE_UP"; - KeyCode[KeyCode["KEY_PAGE_DOWN"] = 34] = "KEY_PAGE_DOWN"; - KeyCode[KeyCode["KEY_END"] = 35] = "KEY_END"; - KeyCode[KeyCode["KEY_HOME"] = 36] = "KEY_HOME"; - KeyCode[KeyCode["KEY_LEFT"] = 37] = "KEY_LEFT"; - KeyCode[KeyCode["KEY_UP"] = 38] = "KEY_UP"; - KeyCode[KeyCode["KEY_RIGHT"] = 39] = "KEY_RIGHT"; - KeyCode[KeyCode["KEY_DOWN"] = 40] = "KEY_DOWN"; - KeyCode[KeyCode["KEY_PRINTSCREEN"] = 44] = "KEY_PRINTSCREEN"; - KeyCode[KeyCode["KEY_INSERT"] = 45] = "KEY_INSERT"; - KeyCode[KeyCode["KEY_DELETE"] = 46] = "KEY_DELETE"; - KeyCode[KeyCode["KEY_0"] = 48] = "KEY_0"; - KeyCode[KeyCode["KEY_1"] = 49] = "KEY_1"; - KeyCode[KeyCode["KEY_2"] = 50] = "KEY_2"; - KeyCode[KeyCode["KEY_3"] = 51] = "KEY_3"; - KeyCode[KeyCode["KEY_4"] = 52] = "KEY_4"; - KeyCode[KeyCode["KEY_5"] = 53] = "KEY_5"; - KeyCode[KeyCode["KEY_6"] = 54] = "KEY_6"; - KeyCode[KeyCode["KEY_7"] = 55] = "KEY_7"; - KeyCode[KeyCode["KEY_8"] = 56] = "KEY_8"; - KeyCode[KeyCode["KEY_9"] = 57] = "KEY_9"; - KeyCode[KeyCode["KEY_SEMICOLON"] = 59] = "KEY_SEMICOLON"; - KeyCode[KeyCode["KEY_EQUALS"] = 61] = "KEY_EQUALS"; - KeyCode[KeyCode["KEY_A"] = 65] = "KEY_A"; - KeyCode[KeyCode["KEY_B"] = 66] = "KEY_B"; - KeyCode[KeyCode["KEY_C"] = 67] = "KEY_C"; - KeyCode[KeyCode["KEY_D"] = 68] = "KEY_D"; - KeyCode[KeyCode["KEY_E"] = 69] = "KEY_E"; - KeyCode[KeyCode["KEY_F"] = 70] = "KEY_F"; - KeyCode[KeyCode["KEY_G"] = 71] = "KEY_G"; - KeyCode[KeyCode["KEY_H"] = 72] = "KEY_H"; - KeyCode[KeyCode["KEY_I"] = 73] = "KEY_I"; - KeyCode[KeyCode["KEY_J"] = 74] = "KEY_J"; - KeyCode[KeyCode["KEY_K"] = 75] = "KEY_K"; - KeyCode[KeyCode["KEY_L"] = 76] = "KEY_L"; - KeyCode[KeyCode["KEY_M"] = 77] = "KEY_M"; - KeyCode[KeyCode["KEY_N"] = 78] = "KEY_N"; - KeyCode[KeyCode["KEY_O"] = 79] = "KEY_O"; - KeyCode[KeyCode["KEY_P"] = 80] = "KEY_P"; - KeyCode[KeyCode["KEY_Q"] = 81] = "KEY_Q"; - KeyCode[KeyCode["KEY_R"] = 82] = "KEY_R"; - KeyCode[KeyCode["KEY_S"] = 83] = "KEY_S"; - KeyCode[KeyCode["KEY_T"] = 84] = "KEY_T"; - KeyCode[KeyCode["KEY_U"] = 85] = "KEY_U"; - KeyCode[KeyCode["KEY_V"] = 86] = "KEY_V"; - KeyCode[KeyCode["KEY_W"] = 87] = "KEY_W"; - KeyCode[KeyCode["KEY_X"] = 88] = "KEY_X"; - KeyCode[KeyCode["KEY_Y"] = 89] = "KEY_Y"; - KeyCode[KeyCode["KEY_Z"] = 90] = "KEY_Z"; - KeyCode[KeyCode["KEY_LEFT_CMD"] = 91] = "KEY_LEFT_CMD"; - KeyCode[KeyCode["KEY_RIGHT_CMD"] = 93] = "KEY_RIGHT_CMD"; - KeyCode[KeyCode["KEY_CONTEXT_MENU"] = 93] = "KEY_CONTEXT_MENU"; - KeyCode[KeyCode["KEY_NUMPAD0"] = 96] = "KEY_NUMPAD0"; - KeyCode[KeyCode["KEY_NUMPAD1"] = 97] = "KEY_NUMPAD1"; - KeyCode[KeyCode["KEY_NUMPAD2"] = 98] = "KEY_NUMPAD2"; - KeyCode[KeyCode["KEY_NUMPAD3"] = 99] = "KEY_NUMPAD3"; - KeyCode[KeyCode["KEY_NUMPAD4"] = 100] = "KEY_NUMPAD4"; - KeyCode[KeyCode["KEY_NUMPAD5"] = 101] = "KEY_NUMPAD5"; - KeyCode[KeyCode["KEY_NUMPAD6"] = 102] = "KEY_NUMPAD6"; - KeyCode[KeyCode["KEY_NUMPAD7"] = 103] = "KEY_NUMPAD7"; - KeyCode[KeyCode["KEY_NUMPAD8"] = 104] = "KEY_NUMPAD8"; - KeyCode[KeyCode["KEY_NUMPAD9"] = 105] = "KEY_NUMPAD9"; - KeyCode[KeyCode["KEY_MULTIPLY"] = 106] = "KEY_MULTIPLY"; - KeyCode[KeyCode["KEY_ADD"] = 107] = "KEY_ADD"; - KeyCode[KeyCode["KEY_SEPARATOR"] = 108] = "KEY_SEPARATOR"; - KeyCode[KeyCode["KEY_SUBTRACT"] = 109] = "KEY_SUBTRACT"; - KeyCode[KeyCode["KEY_DECIMAL"] = 110] = "KEY_DECIMAL"; - KeyCode[KeyCode["KEY_DIVIDE"] = 111] = "KEY_DIVIDE"; - KeyCode[KeyCode["KEY_F1"] = 112] = "KEY_F1"; - KeyCode[KeyCode["KEY_F2"] = 113] = "KEY_F2"; - KeyCode[KeyCode["KEY_F3"] = 114] = "KEY_F3"; - KeyCode[KeyCode["KEY_F4"] = 115] = "KEY_F4"; - KeyCode[KeyCode["KEY_F5"] = 116] = "KEY_F5"; - KeyCode[KeyCode["KEY_F6"] = 117] = "KEY_F6"; - KeyCode[KeyCode["KEY_F7"] = 118] = "KEY_F7"; - KeyCode[KeyCode["KEY_F8"] = 119] = "KEY_F8"; - KeyCode[KeyCode["KEY_F9"] = 120] = "KEY_F9"; - KeyCode[KeyCode["KEY_F10"] = 121] = "KEY_F10"; - KeyCode[KeyCode["KEY_F11"] = 122] = "KEY_F11"; - KeyCode[KeyCode["KEY_F12"] = 123] = "KEY_F12"; - KeyCode[KeyCode["KEY_F13"] = 124] = "KEY_F13"; - KeyCode[KeyCode["KEY_F14"] = 125] = "KEY_F14"; - KeyCode[KeyCode["KEY_F15"] = 126] = "KEY_F15"; - KeyCode[KeyCode["KEY_F16"] = 127] = "KEY_F16"; - KeyCode[KeyCode["KEY_F17"] = 128] = "KEY_F17"; - KeyCode[KeyCode["KEY_F18"] = 129] = "KEY_F18"; - KeyCode[KeyCode["KEY_F19"] = 130] = "KEY_F19"; - KeyCode[KeyCode["KEY_F20"] = 131] = "KEY_F20"; - KeyCode[KeyCode["KEY_F21"] = 132] = "KEY_F21"; - KeyCode[KeyCode["KEY_F22"] = 133] = "KEY_F22"; - KeyCode[KeyCode["KEY_F23"] = 134] = "KEY_F23"; - KeyCode[KeyCode["KEY_F24"] = 135] = "KEY_F24"; - KeyCode[KeyCode["KEY_NUM_LOCK"] = 144] = "KEY_NUM_LOCK"; - KeyCode[KeyCode["KEY_SCROLL_LOCK"] = 145] = "KEY_SCROLL_LOCK"; - KeyCode[KeyCode["KEY_COMMA"] = 188] = "KEY_COMMA"; - KeyCode[KeyCode["KEY_PERIOD"] = 190] = "KEY_PERIOD"; - KeyCode[KeyCode["KEY_SLASH"] = 191] = "KEY_SLASH"; - KeyCode[KeyCode["KEY_BACK_QUOTE"] = 192] = "KEY_BACK_QUOTE"; - KeyCode[KeyCode["KEY_OPEN_BRACKET"] = 219] = "KEY_OPEN_BRACKET"; - KeyCode[KeyCode["KEY_BACK_SLASH"] = 220] = "KEY_BACK_SLASH"; - KeyCode[KeyCode["KEY_CLOSE_BRACKET"] = 221] = "KEY_CLOSE_BRACKET"; - KeyCode[KeyCode["KEY_QUOTE"] = 222] = "KEY_QUOTE"; - KeyCode[KeyCode["KEY_META"] = 224] = "KEY_META"; -})(KeyCode = exports.KeyCode || (exports.KeyCode = {})); -var ppt; -(function (ppt) { - let EventType; - (function (EventType) { - EventType[EventType["KEY_PRESS"] = 0] = "KEY_PRESS"; - EventType[EventType["KEY_RELEASE"] = 1] = "KEY_RELEASE"; - EventType[EventType["KEY_TYPED"] = 2] = "KEY_TYPED"; - })(EventType = ppt.EventType || (ppt.EventType = {})); - let SpecialKey; - (function (SpecialKey) { - SpecialKey[SpecialKey["CTRL"] = 0] = "CTRL"; - SpecialKey[SpecialKey["WINDOWS"] = 1] = "WINDOWS"; - SpecialKey[SpecialKey["SHIFT"] = 2] = "SHIFT"; - SpecialKey[SpecialKey["ALT"] = 3] = "ALT"; - })(SpecialKey = ppt.SpecialKey || (ppt.SpecialKey = {})); - function key_description(key) { - let result = ""; - if (key.key_shift) - result += " + " + tr("Shift"); - if (key.key_alt) - result += " + " + tr("Alt"); - if (key.key_ctrl) - result += " + " + tr("CTRL"); - if (key.key_windows) - result += " + " + tr("Win"); - if (!result && !key.key_code) - return tr("unset"); - if (key.key_code) - result += " + " + key.key_code; - return result.substr(3); - } - ppt.key_description = key_description; -})(ppt = exports.ppt || (exports.ppt = {})); - - -/***/ }), - -/***/ "./shared/js/connection/CommandHelper.ts": -/*!***********************************************!*\ - !*** ./shared/js/connection/CommandHelper.ts ***! - \***********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const ServerConnectionDeclaration_1 = __webpack_require__(/*! ./ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); -const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); -const ConnectionBase_1 = __webpack_require__(/*! ./ConnectionBase */ "./shared/js/connection/ConnectionBase.ts"); -const log_1 = __webpack_require__(/*! ../log */ "./shared/js/log.ts"); -class CommandHelper extends ConnectionBase_1.AbstractCommandHandler { - constructor(connection) { - super(connection); - this._awaiters_unique_ids = {}; - this._awaiters_unique_dbid = {}; - this.volatile_handler_boss = false; - this.ignore_consumed = true; - } - initialize() { - this.connection.command_handler_boss().register_handler(this); - } - destroy() { - if (this.connection) { - const hboss = this.connection.command_handler_boss(); - hboss && hboss.unregister_handler(this); - } - this._awaiters_unique_ids = undefined; - } - handle_command(command) { - if (command.command == "notifyclientnamefromuid") - this.handle_notifyclientnamefromuid(command.arguments); - if (command.command == "notifyclientgetnamefromdbid") - this.handle_notifyclientgetnamefromdbid(command.arguments); - else - return false; - return true; - } - joinChannel(channel, password) { - return this.connection.send_command("clientmove", { - "clid": this.connection.client.getClientId(), - "cid": channel.getChannelId(), - "cpw": password || "" - }); - } - sendMessage(message, type, target) { - if (type == chat_1.ChatType.SERVER) - return this.connection.send_command("sendtextmessage", { "targetmode": 3, "target": 0, "msg": message }); - else if (type == chat_1.ChatType.CHANNEL) - return this.connection.send_command("sendtextmessage", { "targetmode": 2, "target": target.getChannelId(), "msg": message }); - else if (type == chat_1.ChatType.CLIENT) - return this.connection.send_command("sendtextmessage", { "targetmode": 1, "target": target.clientId(), "msg": message }); - } - updateClient(key, value) { - let data = {}; - data[key] = value; - return this.connection.send_command("clientupdate", data); - } - info_from_uid(..._unique_ids) { - return __awaiter(this, void 0, void 0, function* () { - const response = []; - const request = []; - const unique_ids = new Set(_unique_ids); - if (!unique_ids.size) - return []; - const unique_id_resolvers = {}; - for (const unique_id of unique_ids) { - request.push({ 'cluid': unique_id }); - (this._awaiters_unique_ids[unique_id] || (this._awaiters_unique_ids[unique_id] = [])) - .push(unique_id_resolvers[unique_id] = info => response.push(info)); - } - try { - yield this.connection.send_command("clientgetnamefromuid", request); - } - catch (error) { - if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - } - else { - throw error; - } - } - finally { - for (const unique_id of Object.keys(unique_id_resolvers)) - (this._awaiters_unique_ids[unique_id] || []).remove(unique_id_resolvers[unique_id]); - } - return response; - }); - } - handle_notifyclientgetnamefromdbid(json) { - for (const entry of json) { - const info = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - const functions = this._awaiters_unique_dbid[info.client_database_id] || []; - delete this._awaiters_unique_dbid[info.client_database_id]; - for (const fn of functions) - fn(info); - } - } - info_from_cldbid(..._cldbid) { - return __awaiter(this, void 0, void 0, function* () { - const response = []; - const request = []; - const unique_cldbid = new Set(_cldbid); - if (!unique_cldbid.size) - return []; - const unique_cldbid_resolvers = {}; - for (const cldbid of unique_cldbid) { - request.push({ 'cldbid': cldbid }); - (this._awaiters_unique_dbid[cldbid] || (this._awaiters_unique_dbid[cldbid] = [])) - .push(unique_cldbid_resolvers[cldbid] = info => response.push(info)); - } - try { - yield this.connection.send_command("clientgetnamefromdbid", request); - } - catch (error) { - if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - } - else { - throw error; - } - } - finally { - for (const cldbid of Object.keys(unique_cldbid_resolvers)) - (this._awaiters_unique_dbid[cldbid] || []).remove(unique_cldbid_resolvers[cldbid]); - } - return response; - }); - } - handle_notifyclientnamefromuid(json) { - for (const entry of json) { - const info = { - client_unique_id: entry["cluid"], - client_nickname: entry["clname"], - client_database_id: parseInt(entry["cldbid"]) - }; - const functions = this._awaiters_unique_ids[entry["cluid"]] || []; - delete this._awaiters_unique_ids[entry["cluid"]]; - for (const fn of functions) - fn(info); - } - } - request_query_list(server_id = undefined) { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyquerylist", - function: command => { - const json = command.arguments; - const result = {}; - result.flag_all = json[0]["flag_all"]; - result.flag_own = json[0]["flag_own"]; - result.queries = []; - for (const entry of json) { - const rentry = {}; - rentry.bounded_server = parseInt(entry["client_bound_server"]); - rentry.username = entry["client_login_name"]; - rentry.unique_id = entry["client_unique_identifier"]; - result.queries.push(rentry); - } - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - let data = {}; - if (server_id !== undefined) - data["server_id"] = server_id; - this.connection.send_command("querylist", data).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) { - if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - resolve(undefined); - return; - } - } - reject(error); - }); - }); - } - request_playlist_list() { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyplaylistlist", - function: command => { - const json = command.arguments; - const result = []; - for (const entry of json) { - try { - result.push({ - playlist_id: parseInt(entry["playlist_id"]), - playlist_bot_id: parseInt(entry["playlist_bot_id"]), - playlist_title: entry["playlist_title"], - playlist_type: parseInt(entry["playlist_type"]), - playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), - playlist_owner_name: entry["playlist_owner_name"], - needed_power_modify: parseInt(entry["needed_power_modify"]), - needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), - needed_power_delete: parseInt(entry["needed_power_delete"]), - needed_power_song_add: parseInt(entry["needed_power_song_add"]), - needed_power_song_move: parseInt(entry["needed_power_song_move"]), - needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) - }); - } - catch (error) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); - } - } - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistlist").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) { - if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }); - }); - } - request_playlist_songs(playlist_id) { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyplaylistsonglist", - function: command => { - const json = command.arguments; - if (json[0]["playlist_id"] != playlist_id) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); - return false; - } - const result = []; - for (const entry of json) { - try { - result.push({ - song_id: parseInt(entry["song_id"]), - song_invoker: entry["song_invoker"], - song_previous_song_id: parseInt(entry["song_previous_song_id"]), - song_url: entry["song_url"], - song_url_loader: entry["song_url_loader"], - song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", - song_metadata: entry["song_metadata"] - }); - } - catch (error) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); - } - } - resolve(result); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistsonglist", { playlist_id: playlist_id }).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) { - if (error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - } - reject(error); - }); - }); - } - request_playlist_client_list(playlist_id) { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyplaylistclientlist", - function: command => { - const json = command.arguments; - if (json[0]["playlist_id"] != playlist_id) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist clients")); - return false; - } - const result = []; - for (const entry of json) - result.push(parseInt(entry["cldbid"])); - resolve(result.filter(e => !isNaN(e))); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistclientlist", { playlist_id: playlist_id }).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - if (error instanceof ServerConnectionDeclaration_1.CommandResult && error.id == ServerConnectionDeclaration_1.ErrorID.EMPTY_RESULT) { - resolve([]); - return; - } - reject(error); - }); - }); - } - request_clients_by_server_group(group_id) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyservergroupclientlist", - function: command => { - if (command.arguments[0]["sgid"] != group_id) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for server group client list")); - return false; - } - try { - const result = []; - for (const entry of command.arguments) - result.push({ - client_database_id: parseInt(entry["cldbid"]), - client_nickname: entry["client_nickname"], - client_unique_identifier: entry["client_unique_identifier"] - }); - resolve(result); - } - catch (error) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse server group client list: %o"), error); - reject("failed to parse info"); - } - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("servergroupclientlist", { sgid: group_id }).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }); - }); - }); - } - request_playlist_info(playlist_id) { - return new Promise((resolve, reject) => { - const single_handler = { - command: "notifyplaylistinfo", - function: command => { - const json = command.arguments[0]; - if (json["playlist_id"] != playlist_id) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); - return; - } - try { - resolve({ - playlist_id: parseInt(json["playlist_id"]), - playlist_title: json["playlist_title"], - playlist_description: json["playlist_description"], - playlist_type: parseInt(json["playlist_type"]), - playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), - playlist_owner_name: json["playlist_owner_name"], - playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", - playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", - playlist_replay_mode: parseInt(json["playlist_replay_mode"]), - playlist_current_song_id: parseInt(json["playlist_current_song_id"]), - playlist_max_songs: parseInt(json["playlist_max_songs"]) - }); - } - catch (error) { - log_1.log.error(log_1.LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); - reject("failed to parse info"); - } - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("playlistinfo", { playlist_id: playlist_id }).catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }); - }); - } - current_virtual_server_id() { - if (this._who_am_i) - return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); - return new Promise((resolve, reject) => { - const single_handler = { - function: command => { - if (command.command != "" && command.command.indexOf("=") == -1) - return false; - this._who_am_i = command.arguments[0]; - resolve(parseInt(this._who_am_i["virtualserver_id"])); - return true; - } - }; - this.handler_boss.register_single_handler(single_handler); - this.connection.send_command("whoami").catch(error => { - this.handler_boss.remove_single_handler(single_handler); - reject(error); - }); - }); - } -} -exports.CommandHelper = CommandHelper; - - -/***/ }), - -/***/ "./shared/js/connection/ConnectionBase.ts": -/*!************************************************!*\ - !*** ./shared/js/connection/ConnectionBase.ts ***! - \************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const CommandHelper_1 = __webpack_require__(/*! ./CommandHelper */ "./shared/js/connection/CommandHelper.ts"); -exports.CommandOptionDefaults = { - flagset: [], - process_result: true, - timeout: 1000 -}; -class AbstractServerConnection { - constructor(client) { - this.client = client; - this.command_helper = new CommandHelper_1.CommandHelper(this); - } -} -exports.AbstractServerConnection = AbstractServerConnection; -var voice; -(function (voice) { - let PlayerState; - (function (PlayerState) { - PlayerState[PlayerState["PREBUFFERING"] = 0] = "PREBUFFERING"; - PlayerState[PlayerState["PLAYING"] = 1] = "PLAYING"; - PlayerState[PlayerState["BUFFERING"] = 2] = "BUFFERING"; - PlayerState[PlayerState["STOPPING"] = 3] = "STOPPING"; - PlayerState[PlayerState["STOPPED"] = 4] = "STOPPED"; - })(PlayerState = voice.PlayerState || (voice.PlayerState = {})); - class AbstractVoiceConnection { - constructor(connection) { - this.connection = connection; - } - } - voice.AbstractVoiceConnection = AbstractVoiceConnection; -})(voice = exports.voice || (exports.voice = {})); -class ServerCommand { -} -exports.ServerCommand = ServerCommand; -class AbstractCommandHandler { - constructor(connection) { - this.volatile_handler_boss = false; - this.ignore_consumed = false; - this.connection = connection; - } -} -exports.AbstractCommandHandler = AbstractCommandHandler; -class AbstractCommandHandlerBoss { - constructor(connection) { - this.command_handlers = []; - this.single_command_handler = []; - this.connection = connection; - } - destroy() { - this.command_handlers = undefined; - this.single_command_handler = undefined; - } - register_handler(handler) { - if (!handler.volatile_handler_boss && handler.handler_boss) - throw "handler already registered"; - this.command_handlers.remove(handler); - this.command_handlers.push(handler); - handler.handler_boss = this; - } - unregister_handler(handler) { - if (!handler.volatile_handler_boss && handler.handler_boss !== this) { - console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); - return; - } - this.command_handlers.remove(handler); - handler.handler_boss = undefined; - } - register_single_handler(handler) { - this.single_command_handler.push(handler); - } - remove_single_handler(handler) { - this.single_command_handler.remove(handler); - } - handlers() { - return this.command_handlers; - } - invoke_handle(command) { - let flag_consumed = false; - for (const handler of this.command_handlers) { - try { - if (!flag_consumed || handler.ignore_consumed) - flag_consumed = flag_consumed || handler.handle_command(command); - } - catch (error) { - console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); - } - } - for (const handler of [...this.single_command_handler]) { - if (handler.command && handler.command != command.command) - continue; - try { - if (handler.function(command)) - this.single_command_handler.remove(handler); - } - catch (error) { - console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); - } - } - return flag_consumed; - } -} -exports.AbstractCommandHandlerBoss = AbstractCommandHandlerBoss; - - -/***/ }), - -/***/ "./shared/js/connection/ServerConnectionDeclaration.ts": -/*!*************************************************************!*\ - !*** ./shared/js/connection/ServerConnectionDeclaration.ts ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var ErrorID; -(function (ErrorID) { - ErrorID[ErrorID["NOT_IMPLEMENTED"] = 2] = "NOT_IMPLEMENTED"; - ErrorID[ErrorID["COMMAND_NOT_FOUND"] = 256] = "COMMAND_NOT_FOUND"; - ErrorID[ErrorID["PERMISSION_ERROR"] = 2568] = "PERMISSION_ERROR"; - ErrorID[ErrorID["EMPTY_RESULT"] = 1281] = "EMPTY_RESULT"; - ErrorID[ErrorID["PLAYLIST_IS_IN_USE"] = 8451] = "PLAYLIST_IS_IN_USE"; - ErrorID[ErrorID["FILE_ALREADY_EXISTS"] = 2050] = "FILE_ALREADY_EXISTS"; - ErrorID[ErrorID["CLIENT_INVALID_ID"] = 512] = "CLIENT_INVALID_ID"; - ErrorID[ErrorID["CONVERSATION_INVALID_ID"] = 8704] = "CONVERSATION_INVALID_ID"; - ErrorID[ErrorID["CONVERSATION_MORE_DATA"] = 8705] = "CONVERSATION_MORE_DATA"; - ErrorID[ErrorID["CONVERSATION_IS_PRIVATE"] = 8706] = "CONVERSATION_IS_PRIVATE"; -})(ErrorID = exports.ErrorID || (exports.ErrorID = {})); -class CommandResult { - constructor(json) { - this.json = json; - this.id = parseInt(json["id"]); - this.message = json["msg"]; - this.extra_message = ""; - if (json["extra_msg"]) - this.extra_message = json["extra_msg"]; - this.success = this.id == 0; - } -} -exports.CommandResult = CommandResult; - - -/***/ }), - -/***/ "./shared/js/crypto/asn1.ts": -/*!**********************************!*\ - !*** ./shared/js/crypto/asn1.ts ***! - \**********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var asn1; -(function (asn1) { - const ellipsis = "\u2026"; - function string_cut(str, len) { - if (str.length > len) - str = str.substring(0, len) + ellipsis; - return str; - } - class Stream { - constructor(data, position) { - if (data instanceof Stream) - this.data = data.data; - else - this.data = data; - this.position = position; - } - length() { - if (this.data instanceof ArrayBuffer) - return this.data.byteLength; - return this.data.length; - } - get(position) { - if (position === undefined) - position = this.position++; - if (position >= this.length()) - throw 'Requesting byte offset ' + this.position + ' on a stream of length ' + this.length(); - return (typeof (this.data) === "string") ? this.data.charCodeAt(position) : this.data[position]; - } - hexByte(byte) { - return Stream.HEX_DIGITS.charAt((byte >> 4) & 0xF) + Stream.HEX_DIGITS.charAt(byte & 0xF); - } - parseStringISO(start, end) { - let s = ""; - for (let i = start; i < end; ++i) - s += String.fromCharCode(this.get(i)); - return s; - } - parseStringUTF(start, end) { - let s = ""; - for (let i = start; i < end;) { - let c = this.get(i++); - if (c < 128) - s += String.fromCharCode(c); - else if ((c > 191) && (c < 224)) - s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); - else - s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); - } - return s; - } - parseStringBMP(start, end) { - let str = "", hi, lo; - for (let i = start; i < end;) { - hi = this.get(i++); - lo = this.get(i++); - str += String.fromCharCode((hi << 8) | lo); - } - return str; - } - parseTime(start, end, shortYear) { - let s = this.parseStringISO(start, end), m = (shortYear ? Stream.reTimeS : Stream.reTimeL).exec(s); - if (!m) - return "Unrecognized time: " + s; - if (shortYear) { - throw "fixme!"; - } - s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; - if (m[5]) { - s += ":" + m[5]; - if (m[6]) { - s += ":" + m[6]; - if (m[7]) - s += "." + m[7]; - } - } - if (m[8]) { - s += " UTC"; - if (m[8] != 'Z') { - s += m[8]; - if (m[9]) - s += ":" + m[9]; - } - } - return s; - } - ; - parseInteger(start, end) { - let current = this.get(start); - let negative = (current > 127); - let padding = negative ? 255 : 0; - let length; - let descriptor; - while (current == padding && ++start < end) - current = this.get(start); - length = end - start; - if (length === 0) - return negative ? '-1' : '0'; - if (length > 4) { - descriptor = current; - length <<= 3; - while (((descriptor ^ padding) & 0x80) == 0) { - descriptor <<= 1; - --length; - } - descriptor = "(" + length + " bit)\n"; - } - if (negative) - current = current - 256; - let number = ""; - if (typeof (Int10) !== "undefined") { - let n = new Int10(current); - for (let i = start + 1; i < end; ++i) - n.mulAdd(256, this.get(i)); - number = n.toString(); - } - else { - let n = 0; - for (let i = start + 1; i < end; ++i) { - n <<= 8; - n += this.get(i); - } - number = n.toString(); - } - return descriptor + number; - } - ; - isASCII(start, end) { - for (let i = start; i < end; ++i) { - const c = this.get(i); - if (c < 32 || c > 176) - return false; - } - return true; - } - ; - parseBitString(start, end, maxLength) { - let unusedBit = this.get(start), lenBit = ((end - start - 1) << 3) - unusedBit, intro = "(" + lenBit + " bit)\n", s = ""; - for (let i = start + 1; i < end; ++i) { - let b = this.get(i), skip = (i == end - 1) ? unusedBit : 0; - for (let j = 7; j >= skip; --j) - s += (b >> j) & 1 ? "1" : "0"; - if (s.length > maxLength) - return intro + string_cut(s, maxLength); - } - return intro + s; - } - ; - parseOctetString(start, end, maxLength) { - if (this.isASCII(start, end)) - return string_cut(this.parseStringISO(start, end), maxLength); - let len = end - start, s = "(" + len + " byte)\n"; - maxLength /= 2; - if (len > maxLength) - end = start + maxLength; - for (let i = start; i < end; ++i) - s += this.hexByte(this.get(i)); - if (len > maxLength) - s += ellipsis; - return s; - } - ; - parseOID(start, end, maxLength) { - let s = '', n = new Int10(), bits = 0; - for (let i = start; i < end; ++i) { - let v = this.get(i); - n.mulAdd(128, v & 0x7F); - bits += 7; - if (!(v & 0x80)) { - if (s === '') { - n = n.simplify(); - if (n instanceof Int10) { - n.sub(80); - s = "2." + n.toString(); - } - else { - let m = n < 80 ? n < 40 ? 0 : 1 : 2; - s = m + "." + (n - m * 40); - } - } - else - s += "." + n.toString(); - if (s.length > maxLength) - return string_cut(s, maxLength); - n = new Int10(); - bits = 0; - } - } - if (bits > 0) - s += ".incomplete"; - return s; - } - ; - } - Stream.HEX_DIGITS = "0123456789ABCDEF"; - Stream.reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; - Stream.reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; - asn1.Stream = Stream; - let TagClass; - (function (TagClass) { - TagClass[TagClass["UNIVERSAL"] = 0] = "UNIVERSAL"; - TagClass[TagClass["APPLICATION"] = 1] = "APPLICATION"; - TagClass[TagClass["CONTEXT"] = 2] = "CONTEXT"; - TagClass[TagClass["PRIVATE"] = 3] = "PRIVATE"; - })(TagClass = asn1.TagClass || (asn1.TagClass = {})); - let TagType; - (function (TagType) { - TagType[TagType["EOC"] = 0] = "EOC"; - TagType[TagType["BOOLEAN"] = 1] = "BOOLEAN"; - TagType[TagType["INTEGER"] = 2] = "INTEGER"; - TagType[TagType["BIT_STRING"] = 3] = "BIT_STRING"; - TagType[TagType["OCTET_STRING"] = 4] = "OCTET_STRING"; - TagType[TagType["NULL"] = 5] = "NULL"; - TagType[TagType["OBJECT_IDENTIFIER"] = 6] = "OBJECT_IDENTIFIER"; - TagType[TagType["ObjectDescriptor"] = 7] = "ObjectDescriptor"; - TagType[TagType["EXTERNAL"] = 8] = "EXTERNAL"; - TagType[TagType["REAL"] = 9] = "REAL"; - TagType[TagType["ENUMERATED"] = 10] = "ENUMERATED"; - TagType[TagType["EMBEDDED_PDV"] = 11] = "EMBEDDED_PDV"; - TagType[TagType["UTF8String"] = 12] = "UTF8String"; - TagType[TagType["SEQUENCE"] = 16] = "SEQUENCE"; - TagType[TagType["SET"] = 17] = "SET"; - TagType[TagType["NumericString"] = 18] = "NumericString"; - TagType[TagType["PrintableString"] = 19] = "PrintableString"; - TagType[TagType["TeletextString"] = 20] = "TeletextString"; - TagType[TagType["VideotexString"] = 21] = "VideotexString"; - TagType[TagType["IA5String"] = 22] = "IA5String"; - TagType[TagType["UTCTime"] = 23] = "UTCTime"; - TagType[TagType["GeneralizedTime"] = 24] = "GeneralizedTime"; - TagType[TagType["GraphicString"] = 25] = "GraphicString"; - TagType[TagType["VisibleString"] = 26] = "VisibleString"; - TagType[TagType["GeneralString"] = 27] = "GeneralString"; - TagType[TagType["UniversalString"] = 28] = "UniversalString"; - TagType[TagType["BMPString"] = 30] = "BMPString"; - })(TagType = asn1.TagType || (asn1.TagType = {})); - class ASN1Tag { - constructor(stream) { - let buf = stream.get(); - this.tagClass = buf >> 6; - this.tagConstructed = ((buf & 0x20) !== 0); - this.tagNumber = buf & 0x1F; - if (this.tagNumber == 0x1F) { - let n = new Int10(); - do { - buf = stream.get(); - n.mulAdd(128, buf & 0x7F); - } while (buf & 0x80); - this.tagNumber = n.simplify(); - } - } - isUniversal() { - return this.tagClass === 0x00; - } - ; - isEOC() { - return this.tagClass === 0x00 && this.tagNumber === 0x00; - } - ; - } - class ASN1 { - constructor(stream, header, length, tag, children) { - this.stream = stream; - this.header = header; - this.length = length; - this.tag = tag; - this.children = children; - } - content(max_length, type) { - if (this.tag === undefined) - return null; - if (max_length === undefined) - max_length = Infinity; - let content = this.posContent(), len = Math.abs(this.length); - if (!this.tag.isUniversal()) { - if (this.children !== null) - return "(" + this.children.length + " elem)"; - return this.stream.parseOctetString(content, content + len, max_length); - } - switch (type || this.tag.tagNumber) { - case 0x01: - return (this.stream.get(content) === 0) ? "false" : "true"; - case 0x02: - return this.stream.parseInteger(content, content + len); - case 0x03: - return this.children ? "(" + this.children.length + " elem)" : - this.stream.parseBitString(content, content + len, max_length); - case 0x04: - return this.children ? "(" + this.children.length + " elem)" : - this.stream.parseOctetString(content, content + len, max_length); - case 0x06: - return this.stream.parseOID(content, content + len, max_length); - case 0x10: - case 0x11: - if (this.children !== null) - return "(" + this.children.length + " elem)"; - else - return "(no elem)"; - case 0x0C: - return string_cut(this.stream.parseStringUTF(content, content + len), max_length); - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x1A: - return string_cut(this.stream.parseStringISO(content, content + len), max_length); - case 0x1E: - return string_cut(this.stream.parseStringBMP(content, content + len), max_length); - case 0x17: - case 0x18: - return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); - } - return null; - } - ; - typeName() { - switch (this.tag.tagClass) { - case 0: - return TagType[this.tag.tagNumber] || ("Universal_" + this.tag.tagNumber.toString()); - case 1: - return "Application_" + this.tag.tagNumber.toString(); - case 2: - return "[" + this.tag.tagNumber.toString() + "]"; - case 3: - return "Private_" + this.tag.tagNumber.toString(); - } - } - ; - toString() { - return this.typeName() + "@" + this.stream.position + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.children === null) ? 'null' : this.children.length) + "]"; - } - toPrettyString(indent) { - if (indent === undefined) - indent = ''; - let s = indent + this.typeName() + " @" + this.stream.position; - if (this.length >= 0) - s += "+"; - s += this.length; - if (this.tag.tagConstructed) - s += " (constructed)"; - else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.children !== null)) - s += " (encapsulates)"; - let content = this.content(); - if (content) - s += ": " + content.replace(/\n/g, '|'); - s += "\n"; - if (this.children !== null) { - indent += ' '; - for (let i = 0, max = this.children.length; i < max; ++i) - s += this.children[i].toPrettyString(indent); - } - return s; - } - ; - posStart() { - return this.stream.position; - } - ; - posContent() { - return this.stream.position + this.header; - } - ; - posEnd() { - return this.stream.position + this.header + Math.abs(this.length); - } - ; - static decodeLength(stream) { - let buf = stream.get(); - const len = buf & 0x7F; - if (len == buf) - return len; - if (len > 6) - throw "Length over 48 bits not supported at position " + (stream.position - 1); - if (len === 0) - return null; - buf = 0; - for (let i = 0; i < len; ++i) - buf = (buf << 8) + stream.get(); - return buf; - } - ; - static encodeLength(buffer, offset, length) { - if (length < 0x7F) { - buffer[offset] = length; - } - else { - buffer[offset] = 0x80; - let index = 1; - while (length > 0) { - buffer[offset + index++] = length & 0xFF; - length >>= 8; - buffer[offset] += 1; - } - } - } - } - asn1.ASN1 = ASN1; - function decode0(stream) { - const streamStart = new Stream(stream, 0); - const tag = new ASN1Tag(stream); - let len = ASN1.decodeLength(stream); - const start = stream.position; - const length_header = start - streamStart.position; - let children = null; - const query_children = () => { - children = []; - if (len !== null) { - const end = start + len; - if (end > stream.length()) - throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; - while (stream.position < end) - children[children.length] = decode0(stream); - if (stream.position != end) - throw 'Content size is not correct for container at offset ' + start; - } - else { - try { - while (true) { - const s = decode0(stream); - if (s.tag.isEOC()) - break; - children[children.length] = s; - } - len = start - stream.position; - } - catch (e) { - throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e; - } - } - }; - if (tag.tagConstructed) { - query_children(); - } - else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { - try { - if (tag.tagNumber == 0x03) - if (stream.get() != 0) - throw "BIT STRINGs with unused bits cannot encapsulate."; - query_children(); - for (let i = 0; i < children.length; ++i) - if (children[i].tag.isEOC()) - throw 'EOC is not supposed to be actual content.'; - } - catch (e) { - children = null; - } - } - if (children === null) { - if (len === null) - throw "We can't skip over an invalid tag with undefined length at offset " + start; - stream.position = start + Math.abs(len); - } - return new ASN1(streamStart, length_header, len, tag, children); - } - function decode(stream) { - return decode0(new Stream(stream, 0)); - } - asn1.decode = decode; -})(asn1 = exports.asn1 || (exports.asn1 = {})); - - -/***/ }), - -/***/ "./shared/js/crypto/sha.ts": -/*!*********************************!*\ - !*** ./shared/js/crypto/sha.ts ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(process, global) {var __WEBPACK_AMD_DEFINE_RESULT__; -Object.defineProperty(exports, "__esModule", { value: true }); -var sha; -(function (sha) { - (function () { - 'use strict'; - let root = typeof window === 'object' ? window : {}; - let NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; - if (NODE_JS) { - root = global; - } - let COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports; - let AMD = true && __webpack_require__(/*! !webpack amd options */ "./node_modules/webpack/buildin/amd-options.js"); - let HEX_CHARS = '0123456789abcdef'.split(''); - let EXTRA = [-2147483648, 8388608, 32768, 128]; - let SHIFT = [24, 16, 8, 0]; - let OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; - let blocks = []; - let createOutputMethod = function (outputType) { - return function (message) { - return new Sha1(true).update(message)[outputType](); - }; - }; - let createMethod = function () { - let method = createOutputMethod('hex'); - if (NODE_JS) { - method = nodeWrap(method); - } - method.create = function () { - return new Sha1(); - }; - method.update = function (message) { - return method.create().update(message); - }; - for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createOutputMethod(type); - } - return method; - }; - var nodeWrap = function (method) { - var crypto = eval("require('crypto')"); - var Buffer = eval("require('buffer').Buffer"); - var nodeMethod = function (message) { - if (typeof message === 'string') { - return crypto.createHash('sha1').update(message, 'utf8').digest('hex'); - } - else if (message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } - else if (message.length === undefined) { - return method(message); - } - return crypto.createHash('sha1').update(new Buffer(message)).digest('hex'); - }; - return nodeMethod; - }; - function Sha1(sharedMemory) { - if (sharedMemory) { - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.blocks = blocks; - } - else { - this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - this.h0 = 0x67452301; - this.h1 = 0xEFCDAB89; - this.h2 = 0x98BADCFE; - this.h3 = 0x10325476; - this.h4 = 0xC3D2E1F0; - this.block = this.start = this.bytes = this.hBytes = 0; - this.finalized = this.hashed = false; - this.first = true; - } - Sha1.prototype.update = function (message) { - if (this.finalized) { - return; - } - var notString = typeof (message) !== 'string'; - if (notString && message.constructor === root.ArrayBuffer) { - message = new Uint8Array(message); - } - var code, index = 0, i, length = message.length || 0, blocks = this.blocks; - while (index < length) { - if (this.hashed) { - this.hashed = false; - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - if (notString) { - for (i = this.start; index < length && i < 64; ++index) { - blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; - } - } - else { - for (i = this.start; index < length && i < 64; ++index) { - code = message.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } - else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - else { - code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - this.lastByteIndex = i; - this.bytes += i - this.start; - if (i >= 64) { - this.block = blocks[16]; - this.start = i - 64; - this.hash(); - this.hashed = true; - } - else { - this.start = i; - } - } - if (this.bytes > 4294967295) { - this.hBytes += this.bytes / 4294967296 << 0; - this.bytes = this.bytes % 4294967296; - } - return this; - }; - Sha1.prototype.finalize = function () { - if (this.finalized) { - return; - } - this.finalized = true; - var blocks = this.blocks, i = this.lastByteIndex; - blocks[16] = this.block; - blocks[i >> 2] |= EXTRA[i & 3]; - this.block = blocks[16]; - if (i >= 56) { - if (!this.hashed) { - this.hash(); - } - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - blocks[14] = this.hBytes << 3 | this.bytes >>> 29; - blocks[15] = this.bytes << 3; - this.hash(); - }; - Sha1.prototype.hash = function () { - var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4; - var f, j, t, blocks = this.blocks; - for (j = 16; j < 80; ++j) { - t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; - blocks[j] = (t << 1) | (t >>> 31); - } - for (j = 0; j < 20; j += 5) { - f = (b & c) | ((~b) & d); - t = (a << 5) | (a >>> 27); - e = t + f + e + 1518500249 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - f = (a & b) | ((~a) & c); - t = (e << 5) | (e >>> 27); - d = t + f + d + 1518500249 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - f = (e & a) | ((~e) & b); - t = (d << 5) | (d >>> 27); - c = t + f + c + 1518500249 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - f = (d & e) | ((~d) & a); - t = (c << 5) | (c >>> 27); - b = t + f + b + 1518500249 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - f = (c & d) | ((~c) & e); - t = (b << 5) | (b >>> 27); - a = t + f + a + 1518500249 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - for (; j < 40; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = t + f + e + 1859775393 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = t + f + d + 1859775393 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = t + f + c + 1859775393 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = t + f + b + 1859775393 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = t + f + a + 1859775393 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - for (; j < 60; j += 5) { - f = (b & c) | (b & d) | (c & d); - t = (a << 5) | (a >>> 27); - e = t + f + e - 1894007588 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - f = (a & b) | (a & c) | (b & c); - t = (e << 5) | (e >>> 27); - d = t + f + d - 1894007588 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - f = (e & a) | (e & b) | (a & b); - t = (d << 5) | (d >>> 27); - c = t + f + c - 1894007588 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - f = (d & e) | (d & a) | (e & a); - t = (c << 5) | (c >>> 27); - b = t + f + b - 1894007588 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - f = (c & d) | (c & e) | (d & e); - t = (b << 5) | (b >>> 27); - a = t + f + a - 1894007588 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - for (; j < 80; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = t + f + e - 899497514 + blocks[j] << 0; - b = (b << 30) | (b >>> 2); - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = t + f + d - 899497514 + blocks[j + 1] << 0; - a = (a << 30) | (a >>> 2); - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = t + f + c - 899497514 + blocks[j + 2] << 0; - e = (e << 30) | (e >>> 2); - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = t + f + b - 899497514 + blocks[j + 3] << 0; - d = (d << 30) | (d >>> 2); - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = t + f + a - 899497514 + blocks[j + 4] << 0; - c = (c << 30) | (c >>> 2); - } - this.h0 = this.h0 + a << 0; - this.h1 = this.h1 + b << 0; - this.h2 = this.h2 + c << 0; - this.h3 = this.h3 + d << 0; - this.h4 = this.h4 + e << 0; - }; - Sha1.prototype.hex = function () { - this.finalize(); - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; - return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + - HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + - HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + - HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + - HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + - HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + - HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + - HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + - HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + - HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + - HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + - HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + - HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + - HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + - HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + - HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + - HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + - HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + - HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + - HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F]; - }; - Sha1.prototype.toString = Sha1.prototype.hex; - Sha1.prototype.digest = function () { - this.finalize(); - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4; - return [ - (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, - (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, - (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, - (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, - (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF - ]; - }; - Sha1.prototype.array = Sha1.prototype.digest; - Sha1.prototype.arrayBuffer = function () { - this.finalize(); - var buffer = new ArrayBuffer(20); - var dataView = new DataView(buffer); - dataView.setUint32(0, this.h0); - dataView.setUint32(4, this.h1); - dataView.setUint32(8, this.h2); - dataView.setUint32(12, this.h3); - dataView.setUint32(16, this.h4); - return buffer; - }; - var exports = createMethod(); - if (COMMON_JS) { - module.exports = exports; - } - else { - root._sha1 = exports; - if (AMD) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () { - return exports; - }).call(exports, __webpack_require__, exports, module), - __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } - } - })(); - function encode_text(buffer) { - if (window.TextEncoder) { - return new TextEncoder().encode(buffer).buffer; - } - let utf8 = unescape(encodeURIComponent(buffer)); - let result = new Uint8Array(utf8.length); - for (let i = 0; i < utf8.length; i++) { - result[i] = utf8.charCodeAt(i); - } - return result.buffer; - } - sha.encode_text = encode_text; - function sha1(message) { - if (!(typeof (message) === "string" || message instanceof ArrayBuffer)) - throw "Invalid type!"; - let buffer = message instanceof ArrayBuffer ? message : encode_text(message); - if (!crypto || !crypto.subtle || !crypto.subtle.digest || /Edge/.test(navigator.userAgent)) - return new Promise(resolve => { - resolve(_sha1.arrayBuffer(buffer)); - }); - else - return crypto.subtle.digest("SHA-1", buffer); - } - sha.sha1 = sha1; -})(sha = exports.sha || (exports.sha = {})); - -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/process/browser.js */ "./node_modules/process/browser.js"), __webpack_require__(/*! ./../../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) - -/***/ }), - -/***/ "./shared/js/crypto/uid.ts": -/*!*********************************!*\ - !*** ./shared/js/crypto/uid.ts ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -} -exports.guid = guid; - - -/***/ }), - -/***/ "./shared/js/i18n/localize.ts": -/*!************************************!*\ - !*** ./shared/js/i18n/localize.ts ***! - \************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const uid_1 = __webpack_require__(/*! ../crypto/uid */ "./shared/js/crypto/uid.ts"); -const log_1 = __webpack_require__(/*! ../log */ "./shared/js/log.ts"); -const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); -const settings_1 = __webpack_require__(/*! ../settings */ "./shared/js/settings.ts"); -const modal_1 = __webpack_require__(/*! ../ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); -var i18n; -(function (i18n) { - let translations = []; - let fast_translate = {}; - function tr(message, key) { - const sloppy = fast_translate[message]; - if (sloppy) - return sloppy; - log_1.log.info(log_1.LogCategory.I18N, "Translating \"%s\". Default: \"%s\"", key, message); - let translated = message; - for (const translation of translations) { - if (translation.key.message == message) { - translated = translation.translated; - break; - } - } - fast_translate[message] = translated; - return translated; - } - i18n.tr = tr; - function tra(message, ...args) { - message = tr(message); - return chat_1.MessageHelper.formatMessage(message, ...args); - } - i18n.tra = tra; - function load_translation_file(url, path) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - $.ajax({ - url: url, - async: true, - success: result => { - try { - const file = (typeof (result) === "string" ? JSON.parse(result) : result); - if (!file) { - reject("Invalid json"); - return; - } - file.full_url = url; - file.path = path; - resolve(file); - } - catch (error) { - log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to load translation file %s. Failed to parse or process json: %o"), url, error); - reject(tr("Failed to process or parse json!")); - } - }, - error: (xhr, error) => { - reject(tr("Failed to load file: ") + error); - } - }); - }); - }); - } - function load_file(url, path) { - return load_translation_file(url, path).then((result) => __awaiter(this, void 0, void 0, function* () { - try { - tr("Dummy translation test"); - } - catch (error) { - throw "dummy test failed"; - } - log_1.log.info(log_1.LogCategory.I18N, tr("Successfully initialized up translation file from %s"), url); - translations = result.translations; - })).catch(error => { - log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to load translation file from \"%s\". Error: %o"), url, error); - return Promise.reject(error); - }); - } - i18n.load_file = load_file; - function load_repository0(repo, reload) { - return __awaiter(this, void 0, void 0, function* () { - if (!repo.load_timestamp || repo.load_timestamp < 1000 || reload) { - const info_json = yield new Promise((resolve, reject) => { - $.ajax({ - url: repo.url + "/info.json", - async: true, - cache: !reload, - success: result => { - const file = (typeof (result) === "string" ? JSON.parse(result) : result); - if (!file) { - reject("Invalid json"); - return; - } - resolve(file); - }, - error: (xhr, error) => { - reject(tr("Failed to load file: ") + error); - } - }); - }); - Object.assign(repo, info_json); - } - if (!repo.unique_id) - repo.unique_id = uid_1.guid(); - repo.translations = repo.translations || []; - repo.load_timestamp = Date.now(); - }); - } - function load_repository(url) { - return __awaiter(this, void 0, void 0, function* () { - const result = {}; - result.url = url; - yield load_repository0(result, false); - return result; - }); - } - i18n.load_repository = load_repository; - let config; - (function (config_1) { - const repository_config_key = "i18n.repository"; - let _cached_repository_config; - function repository_config() { - if (_cached_repository_config) - return _cached_repository_config; - const config_string = localStorage.getItem(repository_config_key); - let config; - try { - config = config_string ? JSON.parse(config_string) : {}; - } - catch (error) { - log_1.log.error(log_1.LogCategory.I18N, tr("Failed to parse repository config: %o"), error); - } - config.repositories = config.repositories || []; - for (const repo of config.repositories) - (repo.repository || { load_timestamp: 0 }).load_timestamp = 0; - if (config.repositories.length == 0) { - load_repository(settings_1.StaticSettings.instance.static("i18n.default_repository", "https://web.teaspeak.de/i18n/")).then(repo => { - log_1.log.info(log_1.LogCategory.I18N, tr("Successfully added default repository from \"%s\"."), repo.url); - register_repository(repo); - }).catch(error => { - log_1.log.warn(log_1.LogCategory.I18N, tr("Failed to add default repository. Error: %o"), error); - }); - } - return _cached_repository_config = config; - } - config_1.repository_config = repository_config; - function save_repository_config() { - localStorage.setItem(repository_config_key, JSON.stringify(_cached_repository_config)); - } - config_1.save_repository_config = save_repository_config; - const translation_config_key = "i18n.translation"; - let _cached_translation_config; - function translation_config() { - if (_cached_translation_config) - return _cached_translation_config; - const config_string = localStorage.getItem(translation_config_key); - try { - _cached_translation_config = config_string ? JSON.parse(config_string) : {}; - } - catch (error) { - log_1.log.error(log_1.LogCategory.I18N, tr("Failed to initialize translation config. Using default one. Error: %o"), error); - _cached_translation_config = {}; - } - return _cached_translation_config; - } - config_1.translation_config = translation_config; - function save_translation_config() { - localStorage.setItem(translation_config_key, JSON.stringify(_cached_translation_config)); - } - config_1.save_translation_config = save_translation_config; - })(config = i18n.config || (i18n.config = {})); - function register_repository(repository) { - if (!repository) - return; - for (const repo of config.repository_config().repositories) - if (repo.url == repository.url) - return; - config.repository_config().repositories.push(repository); - config.save_repository_config(); - } - i18n.register_repository = register_repository; - function registered_repositories() { - return config.repository_config().repositories.map(e => e.repository || { url: e.url, load_timestamp: 0 }); - } - i18n.registered_repositories = registered_repositories; - function delete_repository(repository) { - if (!repository) - return; - for (const repo of [...config.repository_config().repositories]) - if (repo.url == repository.url) { - config.repository_config().repositories.remove(repo); - } - config.save_repository_config(); - } - i18n.delete_repository = delete_repository; - function iterate_repositories(callback_entry) { - return __awaiter(this, void 0, void 0, function* () { - const promises = []; - for (const repository of registered_repositories()) { - promises.push(load_repository0(repository, false).then(() => callback_entry(repository)).catch(error => { - log_1.log.warn(log_1.LogCategory.I18N, "Failed to fetch repository %s. error: %o", repository.url, error); - })); - } - yield Promise.all(promises); - }); - } - i18n.iterate_repositories = iterate_repositories; - function select_translation(repository, entry) { - const cfg = config.translation_config(); - if (entry && repository) { - cfg.current_language = entry.name; - cfg.current_repository_url = repository.url; - cfg.current_translation_url = repository.url + entry.path; - cfg.current_translation_path = entry.path; - } - else { - cfg.current_language = undefined; - cfg.current_repository_url = undefined; - cfg.current_translation_url = undefined; - cfg.current_translation_path = undefined; - } - config.save_translation_config(); - } - i18n.select_translation = select_translation; - function initialize() { - return __awaiter(this, void 0, void 0, function* () { - const rcfg = config.repository_config(); - const cfg = config.translation_config(); - if (cfg.current_translation_url) { - try { - yield load_file(cfg.current_translation_url, cfg.current_translation_path); - } - catch (error) { - console.error(tr("Failed to initialize selected translation: %o"), error); - const show_error = () => { - modal_1.createErrorModal(tr("Translation System"), tra("Failed to load current selected translation file.{:br:}File: {0}{:br:}Error: {1}{:br:}{:br:}Using default fallback translations.", cfg.current_translation_url, error)).open(); - }; - if (loader.running()) - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - priority: 10, - function: () => __awaiter(this, void 0, void 0, function* () { return show_error(); }), - name: "I18N error display" - }); - else - show_error(); - } - } - }); - } - i18n.initialize = initialize; -})(i18n = exports.i18n || (exports.i18n = {})); -const tr = i18n.tr; -const tra = i18n.tra; -window.tr = i18n.tr; -window.tra = i18n.tra; - - -/***/ }), - -/***/ "./shared/js/log.ts": -/*!**************************!*\ - !*** ./shared/js/log.ts ***! - \**************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -throw new Error("Module parse failed: Binding arguments in strict mode (151:43)\nFile was processed with these loaders:\n * ./node_modules/ts-loader/index.js\nYou may need an additional loader to handle the result of these loaders.\n| }\n| log_1.group = group;\n> function table(level, category, title, arguments) {\n| if (group_mode == GroupMode.NATIVE) {\n| console.groupCollapsed(title);"); - -/***/ }), - -/***/ "./shared/js/main.ts": -/*!***************************!*\ - !*** ./shared/js/main.ts ***! - \***************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var spawnYesNo = ModalConnect_1.Modals.spawnYesNo; -const BrowserIPC_1 = __webpack_require__(/*! ./BrowserIPC */ "./shared/js/BrowserIPC.ts"); -const log_1 = __webpack_require__(/*! ./log */ "./shared/js/log.ts"); -const ConnectionProfile_1 = __webpack_require__(/*! ./profiles/ConnectionProfile */ "./shared/js/profiles/ConnectionProfile.ts"); -const ModalConnect_1 = __webpack_require__(/*! ./ui/modal/ModalConnect */ "./shared/js/ui/modal/ModalConnect.ts"); -const settings_1 = __webpack_require__(/*! ./settings */ "./shared/js/settings.ts"); -const localize_1 = __webpack_require__(/*! ./i18n/localize */ "./shared/js/i18n/localize.ts"); -const modal_1 = __webpack_require__(/*! ./ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); -const chat_1 = __webpack_require__(/*! ./ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); -exports.js_render = window.jsrender || $; -exports.native_client = window.require !== undefined; -function getUserMediaFunctionPromise() { - if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) - return constraints => navigator.mediaDevices.getUserMedia(constraints); - const _callbacked_function = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - if (!_callbacked_function) - return undefined; - return constraints => new Promise((resolve, reject) => _callbacked_function(constraints, resolve, reject)); -} -exports.getUserMediaFunctionPromise = getUserMediaFunctionPromise; -function setup_close() { - window.onbeforeunload = event => { - if (ConnectionProfile_1.profiles.requires_save()) - ConnectionProfile_1.profiles.save(); - if (!settings_1.settings.static(settings_1.Settings.KEY_DISABLE_UNLOAD_DIALOG, false)) { - const active_connections = server_connections.server_connection_handlers().filter(e => e.connected); - if (active_connections.length == 0) - return; - if (!exports.native_client) { - event.returnValue = "Are you really sure?
You're still connected!"; - } - else { - const do_exit = () => { - const dp = server_connections.server_connection_handlers().map(e => { - if (e.serverConnection.connected()) - return e.serverConnection.disconnect(tr("client closed")); - return Promise.resolve(); - }).map(e => e.catch(error => { - console.warn(tr("Failed to disconnect from server on client close: %o"), e); - })); - const exit = () => { - const { remote } = exports.nodeRequire('electron'); - remote.getCurrentWindow().close(); - }; - Promise.all(dp).then(exit); - setTimeout(exit, 2500); - }; - if (window.open_connected_question) { - event.preventDefault(); - event.returnValue = "question"; - window.open_connected_question().then(result => { - if (result) { - window.onbeforeunload = e => e.preventDefault(); - setTimeout(() => window.onbeforeunload, 5000); - do_exit(); - } - }); - } - else { - do_exit(); - } - } - } - }; -} -exports.setup_close = setup_close; -function setup_jsrender() { - if (!exports.js_render) { - loader.critical_error("Missing jsrender extension!"); - return false; - } - if (!exports.js_render.views) { - loader.critical_error("Missing jsrender viewer extension!"); - return false; - } - exports.js_render.views.settings.allowCode(true); - exports.js_render.views.tags("rnd", (argument) => { - let min = parseInt(argument.substr(0, argument.indexOf('~'))); - let max = parseInt(argument.substr(argument.indexOf('~') + 1)); - return (Math.round(Math.random() * (min + max + 1) - min)).toString(); - }); - exports.js_render.views.tags("fmt_date", (...args) => { - return moment(args[0]).format(args[1]); - }); - exports.js_render.views.tags("tr", (...args) => { - return tr(args[0]); - }); - $(".jsrender-template").each((idx, _entry) => { - if (!exports.js_render.templates(_entry.id, _entry.innerHTML)) { - log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to setup cache for js renderer template %s!"), _entry.id); - } - else - log_1.log.info(log_1.LogCategory.GENERAL, tr("Successfully loaded jsrender template %s"), _entry.id); - }); - return true; -} -exports.setup_jsrender = setup_jsrender; -function initialize() { - return __awaiter(this, void 0, void 0, function* () { - settings_1.Settings.initialize(); - try { - yield localize_1.i18n.initialize(); - } - catch (error) { - console.error(tr("Failed to initialized the translation system!\nError: %o"), error); - loader.critical_error("Failed to setup the translation system"); - return; - } - BrowserIPC_1.bipc.setup(); - }); -} -exports.initialize = initialize; -function initialize_app() { - return __awaiter(this, void 0, void 0, function* () { - try { - const main = $("#tmpl_main").renderTag({ - multi_session: !settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_MULTI_SESSION), - app_version: app.ui_version() - }).dividerfy(); - $("body").append(main); - } - catch (error) { - log_1.log.error(log_1.LogCategory.GENERAL, error); - loader.critical_error(tr("Failed to setup main page!")); - return; - } - control_bar = new ControlBar($("#control_bar")); - if (!audio.player.initialize()) - console.warn(tr("Failed to initialize audio controller!")); - audio.player.on_ready(() => { - if (audio.player.set_master_volume) - audio.player.on_ready(() => audio.player.set_master_volume(settings_1.settings.global(settings_1.Settings.KEY_SOUND_MASTER) / 100)); - else - log_1.log.warn(log_1.LogCategory.GENERAL, tr("Client does not support audio.player.set_master_volume()... May client is too old?")); - if (audio.recorder.device_refresh_available()) - audio.recorder.refresh_devices(); - }); - default_recorder = new RecorderProfile("default"); - default_recorder.initialize().catch(error => { - log_1.log.error(log_1.LogCategory.AUDIO, tr("Failed to initialize default recorder: %o"), error); - }); - sound.initialize().then(() => { - log_1.log.info(log_1.LogCategory.AUDIO, tr("Sounds initialized")); - }); - sound.set_master_volume(settings_1.settings.global(settings_1.Settings.KEY_SOUND_MASTER_SOUNDS) / 100); - yield ConnectionProfile_1.profiles.load(); - try { - yield ppt.initialize(); - } - catch (error) { - log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to initialize ppt!\nError: %o"), error); - loader.critical_error(tr("Failed to initialize ppt!")); - return; - } - setup_close(); - }); -} -exports.initialize_app = initialize_app; -function str2ab8(str) { - const buf = new ArrayBuffer(str.length); - const bufView = new Uint8Array(buf); - for (let i = 0, strLen = str.length; i < strLen; i++) { - bufView[i] = str.charCodeAt(i); - } - return buf; -} -exports.str2ab8 = str2ab8; -function arrayBufferBase64(base64) { - base64 = atob(base64); - const buf = new ArrayBuffer(base64.length); - const bufView = new Uint8Array(buf); - for (let i = 0, strLen = base64.length; i < strLen; i++) { - bufView[i] = base64.charCodeAt(i); - } - return buf; -} -exports.arrayBufferBase64 = arrayBufferBase64; -function base64_encode_ab(source) { - const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - let base64 = ""; - const bytes = new Uint8Array(source); - const byte_length = bytes.byteLength; - const byte_reminder = byte_length % 3; - const main_length = byte_length - byte_reminder; - let a, b, c, d; - let chunk; - for (let i = 0; i < main_length; i = i + 3) { - chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; - a = (chunk & 16515072) >> 18; - b = (chunk & 258048) >> 12; - c = (chunk & 4032) >> 6; - d = (chunk & 63) >> 0; - base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; - } - if (byte_reminder == 1) { - chunk = bytes[main_length]; - a = (chunk & 252) >> 2; - b = (chunk & 3) << 4; - base64 += encodings[a] + encodings[b] + '=='; - } - else if (byte_reminder == 2) { - chunk = (bytes[main_length] << 8) | bytes[main_length + 1]; - a = (chunk & 64512) >> 10; - b = (chunk & 1008) >> 4; - c = (chunk & 15) << 2; - base64 += encodings[a] + encodings[b] + encodings[c] + '='; - } - return base64; -} -exports.base64_encode_ab = base64_encode_ab; -function handle_connect_request(properties, connection) { - const profile_uuid = properties.profile || (ConnectionProfile_1.profiles.default_profile() || { id: 'default' }).id; - const profile = ConnectionProfile_1.profiles.find_profile(profile_uuid) || ConnectionProfile_1.profiles.default_profile(); - const username = properties.username || profile.connect_username(); - const password = properties.password ? properties.password.value : ""; - const password_hashed = properties.password ? properties.password.hashed : false; - if (profile && profile.valid()) { - connection.startConnection(properties.address, profile, true, { - nickname: username, - password: password.length > 0 ? { - password: password, - hashed: password_hashed - } : undefined - }); - server_connections.set_active_connection_handler(connection); - } - else { - ModalConnect_1.Modals.spawnConnectModal({}, { - url: properties.address, - enforce: true - }, { - profile: profile, - enforce: true - }); - } -} -function main() { - { - const font = settings_1.settings.static_global(settings_1.Settings.KEY_FONT_SIZE, 14); - $(document.body).css("font-size", font + "px"); - } - $(document).on('contextmenu', event => { - if (event.isDefaultPrevented()) - return; - if (!settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU)) - event.preventDefault(); - }); - top_menu.initialize(); - server_connections = new ServerConnectionManager($("#connection-handlers")); - control_bar.initialise(); - const initial_handler = server_connections.spawn_server_connection_handler(); - initial_handler.acquire_recorder(default_recorder, false); - control_bar.set_connection_handler(initial_handler); - ConnectionProfile_1.profiles.identities.update_forum(); - let _resize_timeout; - $(window).on('resize', event => { - if (event.target !== window) - return; - if (_resize_timeout) - clearTimeout(_resize_timeout); - _resize_timeout = setTimeout(() => { - for (const connection of server_connections.server_connection_handlers()) - connection.invoke_resized_on_activate = true; - const active_connection = server_connections.active_connection_handler(); - if (active_connection) - active_connection.resize_elements(); - $(".window-resize-listener").trigger('resize'); - }, 1000); - }); - stats.initialize({ - verbose: true, - anonymize_ip_addresses: true, - volatile_collection_only: false - }); - stats.register_user_count_listener(status => { - log_1.log.info(log_1.LogCategory.STATISTICS, tr("Received user count update: %o"), status); - }); - server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]); - window.test_upload = (message) => { - message = message || "Hello World"; - const connection = server_connections.active_connection_handler(); - connection.fileManager.upload_file({ - size: message.length, - overwrite: true, - channel: connection.getClient().currentChannel(), - name: '/HelloWorld.txt', - path: '' - }).then(key => { - const upload = new RequestFileUpload(key); - const buffer = new Uint8Array(message.length); - { - for (let index = 0; index < message.length; index++) - buffer[index] = message.charCodeAt(index); - } - upload.put_data(buffer).catch(error => { - console.error(error); - }); - }); - }; - setTimeout(() => { - const connection = server_connections.active_connection_handler(); - }, 4000); - if (settings_1.settings.static_global(settings_1.Settings.KEY_USER_IS_NEW)) { - const modal = ModalConnect_1.Modals.openModalNewcomer(); - modal.close_listener.push(() => settings_1.settings.changeGlobal(settings_1.Settings.KEY_USER_IS_NEW, false)); - } -} -const task_teaweb_starter = { - name: "voice app starter", - function: () => __awaiter(void 0, void 0, void 0, function* () { - try { - yield initialize_app(); - main(); - if (!audio.player.initialized()) { - log_1.log.info(log_1.LogCategory.VOICE, tr("Initialize audio controller later!")); - if (!audio.player.initializeFromGesture) { - console.error(tr("Missing audio.player.initializeFromGesture")); - } - else - $(document).one('click', event => audio.player.initializeFromGesture()); - } - } - catch (ex) { - console.error(ex.stack); - if (ex instanceof ReferenceError || ex instanceof TypeError) - ex = ex.name + ": " + ex.message; - loader.critical_error("Failed to invoke main function:
" + ex); - } - }), - priority: 10 -}; -const task_connect_handler = { - name: "Connect handler", - function: () => __awaiter(void 0, void 0, void 0, function* () { - const address = settings_1.settings.static(settings_1.Settings.KEY_CONNECT_ADDRESS, ""); - const chandler = BrowserIPC_1.bipc.get_connect_handler(); - if (settings_1.settings.static(settings_1.Settings.KEY_FLAG_CONNECT_DEFAULT, false) && address) { - const connect_data = { - address: address, - profile: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_PROFILE, ""), - username: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_USERNAME, ""), - password: { - value: settings_1.settings.static(settings_1.Settings.KEY_CONNECT_PASSWORD, ""), - hashed: settings_1.settings.static(settings_1.Settings.KEY_FLAG_CONNECT_PASSWORD, false) - } - }; - if (chandler) { - try { - yield chandler.post_connect_request(connect_data, () => new Promise((resolve, reject) => { - spawnYesNo(tr("Another TeaWeb instance is already running"), tra("Another TeaWeb instance is already running.{:br:}Would you like to connect there?"), response => { - resolve(response); - }, { - closeable: false - }).open(); - })); - log_1.log.info(log_1.LogCategory.CLIENT, tr("Executed connect successfully in another browser window. Closing this window")); - const message = "You're connecting to {0} within the other TeaWeb instance.{:br:}" + - "You could now close this page."; - modal_1.createInfoModal(tr("Connecting successfully within other instance"), chat_1.MessageHelper.formatMessage(tr(message), connect_data.address), { - closeable: false, - footer: undefined - }).open(); - return; - } - catch (error) { - log_1.log.info(log_1.LogCategory.CLIENT, tr("Failed to execute connect within other TeaWeb instance. Using this one. Error: %o"), error); - } - } - loader.register_task(loader.Stage.LOADED, { - priority: 0, - function: () => __awaiter(void 0, void 0, void 0, function* () { return handle_connect_request(connect_data, server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler()); }), - name: tr("default url connect") - }); - } - if (chandler) { - chandler.callback_available = data => { - return !settings_1.settings.static_global(settings_1.Settings.KEY_DISABLE_MULTI_SESSION); - }; - chandler.callback_execute = data => { - handle_connect_request(data, server_connections.spawn_server_connection_handler()); - return true; - }; - } - loader.register_task(loader.Stage.LOADED, task_teaweb_starter); - }), - priority: 10 -}; -const task_certificate_callback = { - name: "certificate accept tester", - function: () => __awaiter(void 0, void 0, void 0, function* () { - const certificate_accept = settings_1.settings.static_global(settings_1.Settings.KEY_CERTIFICATE_CALLBACK, undefined); - if (certificate_accept) { - log_1.log.info(log_1.LogCategory.IPC, tr("Using this instance as certificate callback. ID: %s"), certificate_accept); - try { - try { - yield BrowserIPC_1.bipc.get_handler().post_certificate_accpected(certificate_accept); - } - catch (e) { } - log_1.log.info(log_1.LogCategory.IPC, tr("Other instance has acknowledged out work. Closing this window.")); - const seconds_tag = $.spawn("a"); - let seconds = 5; - let interval_id; - interval_id = setInterval(() => { - seconds--; - seconds_tag.text(seconds.toString()); - if (seconds <= 0) { - clearTimeout(interval_id); - log_1.log.info(log_1.LogCategory.GENERAL, tr("Closing window")); - window.close(); - return; - } - }, 1000); - const message = "You've successfully accepted the certificate.{:br:}" + - "This page will close in {0} seconds."; - modal_1.createInfoModal(tr("Certificate acccepted successfully"), chat_1.MessageHelper.formatMessage(tr(message), seconds_tag), { - closeable: false, - footer: undefined - }).open(); - return; - } - catch (error) { - log_1.log.warn(log_1.LogCategory.IPC, tr("Failed to successfully post certificate accept status: %o"), error); - } - } - else { - log_1.log.info(log_1.LogCategory.IPC, tr("We're not used to accept certificated. Booting app.")); - } - loader.register_task(loader.Stage.LOADED, task_connect_handler); - }), - priority: 10 -}; -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "jrendere initialize", - function: () => __awaiter(void 0, void 0, void 0, function* () { - try { - if (!setup_jsrender()) - throw "invalid load"; - } - catch (error) { - loader.critical_error(tr("Failed to setup jsrender")); - console.error(tr("Failed to load jsrender! %o"), error); - return; - } - }), - priority: 100 -}); -loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "app starter", - function: () => __awaiter(void 0, void 0, void 0, function* () { - try { - yield initialize(); - if (app.is_web()) { - loader.register_task(loader.Stage.LOADED, task_certificate_callback); - } - else { - loader.register_task(loader.Stage.LOADED, task_teaweb_starter); - } - } - catch (ex) { - if (ex instanceof Error || typeof (ex.stack) !== "undefined") - console.error((tr || (msg => msg))("Critical error stack trace: %o"), ex.stack); - if (ex instanceof ReferenceError || ex instanceof TypeError) - ex = ex.name + ": " + ex.message; - loader.critical_error("Failed to boot app function:
" + ex); - } - }), - priority: 1000 -}); - - -/***/ }), - -/***/ "./shared/js/profiles/ConnectionProfile.ts": -/*!*************************************************!*\ - !*** ./shared/js/profiles/ConnectionProfile.ts ***! - \*************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const chat_1 = __webpack_require__(/*! ../ui/frames/chat */ "./shared/js/ui/frames/chat.ts"); -const modal_1 = __webpack_require__(/*! ../ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); -const uid_1 = __webpack_require__(/*! ../crypto/uid */ "./shared/js/crypto/uid.ts"); -const Identity_1 = __webpack_require__(/*! ./Identity */ "./shared/js/profiles/Identity.ts"); -const TeaForumIdentity_1 = __webpack_require__(/*! ./identities/TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); -const TeamSpeakIdentity_1 = __webpack_require__(/*! ./identities/TeamSpeakIdentity */ "./shared/js/profiles/identities/TeamSpeakIdentity.ts"); -class ConnectionProfile { - constructor(id) { - this.selected_identity_type = "unset"; - this.identities = {}; - this.id = id; - } - connect_username() { - if (this.default_username && this.default_username !== "Another TeaSpeak user") - return this.default_username; - let selected = this.selected_identity(); - let name = selected ? selected.fallback_name() : undefined; - return name || "Another TeaSpeak user"; - } - selected_identity(current_type) { - if (!current_type) - current_type = this.selected_type(); - if (current_type === undefined) - return undefined; - if (current_type == Identity_1.IdentitifyType.TEAFORO) { - return TeaForumIdentity_1.static_forum_identity(); - } - else if (current_type == Identity_1.IdentitifyType.TEAMSPEAK || current_type == Identity_1.IdentitifyType.NICKNAME) { - return this.identities[Identity_1.IdentitifyType[current_type].toLowerCase()]; - } - return undefined; - } - selected_type() { - return this.selected_identity_type ? Identity_1.IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined; - } - set_identity(type, identity) { - this.identities[Identity_1.IdentitifyType[type].toLowerCase()] = identity; - } - spawn_identity_handshake_handler(connection) { - const identity = this.selected_identity(); - if (!identity) - return undefined; - return identity.spawn_identity_handshake_handler(connection); - } - encode() { - const identity_data = {}; - for (const key in this.identities) - if (this.identities[key]) - identity_data[key] = this.identities[key].encode(); - return JSON.stringify({ - version: 1, - username: this.default_username, - password: this.default_password, - profile_name: this.profile_name, - identity_type: this.selected_identity_type, - identity_data: identity_data, - id: this.id - }); - } - valid() { - const identity = this.selected_identity(); - if (!identity || !identity.valid()) - return false; - return true; - } -} -exports.ConnectionProfile = ConnectionProfile; -function decode_profile(data) { - return __awaiter(this, void 0, void 0, function* () { - data = JSON.parse(data); - if (data.version !== 1) - return "invalid version"; - const result = new ConnectionProfile(data.id); - result.default_username = data.username; - result.default_password = data.password; - result.profile_name = data.profile_name; - result.selected_identity_type = (data.identity_type || "").toLowerCase(); - if (data.identity_data) { - for (const key in data.identity_data) { - const type = Identity_1.IdentitifyType[key.toUpperCase()]; - const _data = data.identity_data[key]; - if (type == undefined) - continue; - const identity = yield Identity_1.decode_identity(type, _data); - if (identity == undefined) - continue; - result.identities[key.toLowerCase()] = identity; - } - } - return result; - }); -} -let available_profiles = []; -function load() { - return __awaiter(this, void 0, void 0, function* () { - available_profiles = []; - const profiles_json = localStorage.getItem("profiles"); - let profiles_data = (() => { - try { - return profiles_json ? JSON.parse(profiles_json) : { version: 0 }; - } - catch (error) { - debugger; - console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json); - modal_1.createErrorModal(tr("Profile data invalid"), chat_1.MessageHelper.formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open(); - return { version: 0 }; - } - })(); - if (profiles_data.version === 0) { - profiles_data = { - version: 1, - profiles: [] - }; - } - if (profiles_data.version == 1) { - for (const profile_data of profiles_data.profiles) { - const profile = yield decode_profile(profile_data); - if (typeof (profile) === 'string') { - console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data); - continue; - } - available_profiles.push(profile); - } - } - if (!find_profile("default")) { - { - const profile = create_new_profile("default", "default"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "Default Profile"; - try { - const identity = yield TeamSpeakIdentity_1.TeaSpeakIdentity.generate_new(); - let active = true; - setTimeout(() => { - active = false; - }, 1000); - yield identity.improve_level(8, 1, () => active); - profile.set_identity(Identity_1.IdentitifyType.TEAMSPEAK, identity); - profile.selected_identity_type = Identity_1.IdentitifyType[Identity_1.IdentitifyType.TEAMSPEAK]; - } - catch (error) { - modal_1.createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!
Please manually generate the identity within your settings => profiles")).open(); - } - } - { - const profile = create_new_profile("TeaSpeak Forum", "teaforo"); - profile.default_password = ""; - profile.default_username = ""; - profile.profile_name = "TeaSpeak Forum profile"; - profile.set_identity(Identity_1.IdentitifyType.TEAFORO, TeaForumIdentity_1.static_forum_identity()); - profile.selected_identity_type = Identity_1.IdentitifyType[Identity_1.IdentitifyType.TEAFORO]; - } - save(); - } - }); -} -exports.load = load; -function create_new_profile(name, id) { - const profile = new ConnectionProfile(id || uid_1.guid()); - profile.profile_name = name; - profile.default_username = ""; - available_profiles.push(profile); - return profile; -} -exports.create_new_profile = create_new_profile; -let _requires_save = false; -function save() { - const profiles = []; - for (const profile of available_profiles) - profiles.push(profile.encode()); - const data = JSON.stringify({ - version: 1, - profiles: profiles - }); - localStorage.setItem("profiles", data); -} -exports.save = save; -function mark_need_save() { - _requires_save = true; -} -exports.mark_need_save = mark_need_save; -function requires_save() { - return _requires_save; -} -exports.requires_save = requires_save; -function profiles() { - return available_profiles; -} -exports.profiles = profiles; -function find_profile(id) { - for (const profile of profiles()) - if (profile.id == id) - return profile; - return undefined; -} -exports.find_profile = find_profile; -function find_profile_by_name(name) { - name = name.toLowerCase(); - for (const profile of profiles()) - if ((profile.profile_name || "").toLowerCase() == name) - return profile; - return undefined; -} -exports.find_profile_by_name = find_profile_by_name; -function default_profile() { - return find_profile("default"); -} -exports.default_profile = default_profile; -function set_default_profile(profile) { - const old_default = default_profile(); - if (old_default && old_default != profile) { - old_default.id = uid_1.guid(); - } - profile.id = "default"; - return old_default; -} -exports.set_default_profile = set_default_profile; -function delete_profile(profile) { - available_profiles.remove(profile); -} -exports.delete_profile = delete_profile; - - -/***/ }), - -/***/ "./shared/js/profiles/Identity.ts": -/*!****************************************!*\ - !*** ./shared/js/profiles/Identity.ts ***! - \****************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const ConnectionBase_1 = __webpack_require__(/*! ../connection/ConnectionBase */ "./shared/js/connection/ConnectionBase.ts"); -const NameIdentity_1 = __webpack_require__(/*! ./identities/NameIdentity */ "./shared/js/profiles/identities/NameIdentity.ts"); -const TeaForumIdentity_1 = __webpack_require__(/*! ./identities/TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); -const TeamSpeakIdentity_1 = __webpack_require__(/*! ./identities/TeamSpeakIdentity */ "./shared/js/profiles/identities/TeamSpeakIdentity.ts"); -var IdentitifyType; -(function (IdentitifyType) { - IdentitifyType[IdentitifyType["TEAFORO"] = 0] = "TEAFORO"; - IdentitifyType[IdentitifyType["TEAMSPEAK"] = 1] = "TEAMSPEAK"; - IdentitifyType[IdentitifyType["NICKNAME"] = 2] = "NICKNAME"; -})(IdentitifyType = exports.IdentitifyType || (exports.IdentitifyType = {})); -function decode_identity(type, data) { - return __awaiter(this, void 0, void 0, function* () { - let identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity_1.NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity_1.TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeamSpeakIdentity_1.TeaSpeakIdentity(undefined, undefined); - break; - } - if (!identity) - return undefined; - try { - yield identity.decode(data); - } - catch (error) { - console.error(error); - return undefined; - } - return identity; - }); -} -exports.decode_identity = decode_identity; -function create_identity(type) { - let identity; - switch (type) { - case IdentitifyType.NICKNAME: - identity = new NameIdentity_1.NameIdentity(); - break; - case IdentitifyType.TEAFORO: - identity = new TeaForumIdentity_1.TeaForumIdentity(undefined); - break; - case IdentitifyType.TEAMSPEAK: - identity = new TeamSpeakIdentity_1.TeaSpeakIdentity(undefined, undefined); - break; - } - return identity; -} -exports.create_identity = create_identity; -class HandshakeCommandHandler extends ConnectionBase_1.AbstractCommandHandler { - constructor(connection, handle) { - super(connection); - this.handle = handle; - } - handle_command(command) { - if ($.isFunction(this[command.command])) - this[command.command](command.arguments); - else if (command.command == "error") { - return false; - } - else { - console.warn(tr("Received unknown command while handshaking (%o)"), command); - } - return true; - } -} -exports.HandshakeCommandHandler = HandshakeCommandHandler; -class AbstractHandshakeIdentityHandler { - constructor(connection) { - this.callbacks = []; - this.connection = connection; - } - register_callback(callback) { - this.callbacks.push(callback); - } - trigger_success() { - for (const callback of this.callbacks) - callback(true); - } - trigger_fail(message) { - for (const callback of this.callbacks) - callback(false, message); - } -} -exports.AbstractHandshakeIdentityHandler = AbstractHandshakeIdentityHandler; - - -/***/ }), - -/***/ "./shared/js/profiles/identities/NameIdentity.ts": -/*!*******************************************************!*\ - !*** ./shared/js/profiles/identities/NameIdentity.ts ***! - \*******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); -const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); -const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); -class NameHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { - constructor(connection, identity) { - super(connection); - this.identity = identity; - this.handler = new Identity_1.HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); - } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - client_nickname: this.identity.name() - }).catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize name based handshake. Error: %o"), error); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }).then(() => this.trigger_success()); - } - trigger_fail(message) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} -class NameIdentity { - constructor(name) { - this._name = name; - } - set_name(name) { this._name = name; } - name() { return this._name; } - fallback_name() { - return this._name; - } - uid() { - return btoa(this._name); - } - type() { - return Identity_1.IdentitifyType.NICKNAME; - } - valid() { - return this._name != undefined && this._name.length >= 5; - } - decode(data) { - data = JSON.parse(data); - if (data.version !== 1) - throw "invalid version"; - this._name = data["name"]; - return; - } - encode() { - return JSON.stringify({ - version: 1, - name: this._name - }); - } - spawn_identity_handshake_handler(connection) { - return new NameHandshakeHandler(connection, this); - } -} -exports.NameIdentity = NameIdentity; - - -/***/ }), - -/***/ "./shared/js/profiles/identities/TeaForumIdentity.ts": -/*!***********************************************************!*\ - !*** ./shared/js/profiles/identities/TeaForumIdentity.ts ***! - \***********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); -const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); -const teaspeak_forum_1 = __webpack_require__(/*! ./teaspeak-forum */ "./shared/js/profiles/identities/teaspeak-forum.ts"); -const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); -class TeaForumHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { - constructor(connection, identity) { - super(connection); - this.identity = identity; - this.handler = new Identity_1.HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); - } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - data: this.identity.data().data_json() - }).catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize TeaForum based handshake. Error: %o"), error); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - handle_proof(json) { - this.connection.send_command("handshakeindentityproof", { - proof: this.identity.data().data_sign() - }).catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - } - trigger_fail(message) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} -class TeaForumIdentity { - constructor(data) { - this.identity_data = data; - } - valid() { - return !!this.identity_data && !this.identity_data.is_expired(); - } - data() { - return this.identity_data; - } - decode(data) { - data = JSON.parse(data); - if (data.version !== 1) - throw "invalid version"; - return; - } - encode() { - return JSON.stringify({ - version: 1 - }); - } - spawn_identity_handshake_handler(connection) { - return new TeaForumHandshakeHandler(connection, this); - } - fallback_name() { - return this.identity_data ? this.identity_data.name() : undefined; - } - type() { - return Identity_1.IdentitifyType.TEAFORO; - } - uid() { - return "TeaForo#" + ((this.identity_data ? this.identity_data.name() : "Another TeaSpeak user")); - } -} -exports.TeaForumIdentity = TeaForumIdentity; -let static_identity; -function set_static_identity(identity) { - static_identity = identity; -} -exports.set_static_identity = set_static_identity; -function update_forum() { - if (teaspeak_forum_1.forum.logged_in() && (!static_identity || static_identity.data() !== teaspeak_forum_1.forum.data())) { - static_identity = new TeaForumIdentity(teaspeak_forum_1.forum.data()); - } - else { - static_identity = undefined; - } -} -exports.update_forum = update_forum; -function valid_static_forum_identity() { - return static_identity && static_identity.valid(); -} -exports.valid_static_forum_identity = valid_static_forum_identity; -function static_forum_identity() { - return static_identity; -} -exports.static_forum_identity = static_forum_identity; - - -/***/ }), - -/***/ "./shared/js/profiles/identities/TeamSpeakIdentity.ts": -/*!************************************************************!*\ - !*** ./shared/js/profiles/identities/TeamSpeakIdentity.ts ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const main_1 = __webpack_require__(/*! ../../main */ "./shared/js/main.ts"); -const sha_1 = __webpack_require__(/*! ../../crypto/sha */ "./shared/js/crypto/sha.ts"); -const asn1_1 = __webpack_require__(/*! ../../crypto/asn1 */ "./shared/js/crypto/asn1.ts"); -const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); -const ServerConnectionDeclaration_1 = __webpack_require__(/*! ../../connection/ServerConnectionDeclaration */ "./shared/js/connection/ServerConnectionDeclaration.ts"); -const settings_1 = __webpack_require__(/*! ../../settings */ "./shared/js/settings.ts"); -const Identity_1 = __webpack_require__(/*! ../Identity */ "./shared/js/profiles/Identity.ts"); -var CryptoHelper; -(function (CryptoHelper) { - function base64_url_encode(str) { - return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); - } - CryptoHelper.base64_url_encode = base64_url_encode; - function base64_url_decode(str, pad) { - if (typeof (pad) === 'undefined' || pad) - str = (str + '===').slice(0, str.length + (str.length % 4)); - return str.replace(/-/g, '+').replace(/_/g, '/'); - } - CryptoHelper.base64_url_decode = base64_url_decode; - function arraybuffer_to_string(buf) { - return String.fromCharCode.apply(null, new Uint16Array(buf)); - } - CryptoHelper.arraybuffer_to_string = arraybuffer_to_string; - function export_ecc_key(crypto_key, public_key) { - return __awaiter(this, void 0, void 0, function* () { - const key_data = yield crypto.subtle.exportKey("jwk", crypto_key); - let index = 0; - const length = public_key ? 79 : 114; - const buffer = new Uint8Array(length); - { - buffer[index++] = 0x30; - buffer[index++] = 0x00; - } - { - buffer[index++] = 0x03; - buffer[index++] = 0x02; - buffer[index++] = 0x07; - buffer[index++] = public_key ? 0x00 : 0x80; - } - { - buffer[index++] = 0x02; - buffer[index++] = 0x01; - buffer[index++] = 0x20; - } - try { - buffer[index++] = 0x02; - buffer[index++] = 0x20; - const raw = atob(base64_url_decode(key_data.x, false)); - if (raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - for (let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } - catch (error) { - if (error instanceof DOMException) - throw "failed to parse x coordinate (invalid base64)"; - throw error; - } - try { - buffer[index++] = 0x02; - buffer[index++] = 0x20; - const raw = atob(base64_url_decode(key_data.y, false)); - if (raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - for (let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } - catch (error) { - if (error instanceof DOMException) - throw "failed to parse y coordinate (invalid base64)"; - throw error; - } - if (!public_key) { - try { - buffer[index++] = 0x02; - buffer[index++] = 0x20; - const raw = atob(base64_url_decode(key_data.d, false)); - if (raw.charCodeAt(0) > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - for (let i = 0; i < 32; i++) - buffer[index++] = raw.charCodeAt(i); - } - catch (error) { - if (error instanceof DOMException) - throw "failed to parse y coordinate (invalid base64)"; - throw error; - } - } - buffer[1] = index - 2; - return main_1.base64_encode_ab(buffer.buffer.slice(0, index)); - }); - } - CryptoHelper.export_ecc_key = export_ecc_key; - const crypt_key = "b9dfaa7bee6ac57ac7b65f1094a1c155e747327bc2fe5d51c512023fe54a280201004e90ad1daaae1075d53b7d571c30e063b5a62a4a017bb394833aa0983e6e"; - function c_strlen(buffer, offset) { - let index = 0; - while (index + offset < buffer.length && buffer[index + offset] != 0) - index++; - return index; - } - function decrypt_ts_identity(buffer) { - return __awaiter(this, void 0, void 0, function* () { - const hash = new Uint8Array(yield sha_1.sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for (let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - const length = Math.min(buffer.length, 100); - for (let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - return arraybuffer_to_string(buffer); - }); - } - CryptoHelper.decrypt_ts_identity = decrypt_ts_identity; - function encrypt_ts_identity(buffer) { - return __awaiter(this, void 0, void 0, function* () { - const length = Math.min(buffer.length, 100); - for (let i = 0; i < length; i++) - buffer[i] ^= crypt_key.charCodeAt(i); - const hash = new Uint8Array(yield sha_1.sha.sha1(buffer.buffer.slice(20, 20 + c_strlen(buffer, 20)))); - for (let i = 0; i < 20; i++) - buffer[i] ^= hash[i]; - return main_1.base64_encode_ab(buffer); - }); - } - CryptoHelper.encrypt_ts_identity = encrypt_ts_identity; - function decode_tomcrypt_key(buffer) { - let decoded; - try { - decoded = asn1_1.asn1.decode(atob(buffer)); - } - catch (error) { - if (error instanceof DOMException) - throw "failed to parse key buffer (invalid base64)"; - throw error; - } - let { x, y, k } = { - x: decoded.children[2].content(Infinity, asn1_1.asn1.TagType.VisibleString), - y: decoded.children[3].content(Infinity, asn1_1.asn1.TagType.VisibleString), - k: decoded.children[4].content(Infinity, asn1_1.asn1.TagType.VisibleString) - }; - if (x.length > 32) { - if (x.charCodeAt(0) != 0) - throw "Invalid X coordinate! (Too long)"; - x = x.substr(1); - } - if (y.length > 32) { - if (y.charCodeAt(0) != 0) - throw "Invalid Y coordinate! (Too long)"; - y = y.substr(1); - } - if (k.length > 32) { - if (k.charCodeAt(0) != 0) - throw "Invalid private coordinate! (Too long)"; - k = k.substr(1); - } - return { - crv: "P-256", - d: base64_url_encode(btoa(k)), - x: base64_url_encode(btoa(x)), - y: base64_url_encode(btoa(y)), - ext: true, - key_ops: ["deriveKey", "sign"], - kty: "EC", - }; - } - CryptoHelper.decode_tomcrypt_key = decode_tomcrypt_key; -})(CryptoHelper = exports.CryptoHelper || (exports.CryptoHelper = {})); -class TeaSpeakHandshakeHandler extends Identity_1.AbstractHandshakeIdentityHandler { - constructor(connection, identity) { - super(connection); - this.identity = identity; - this.handler = new Identity_1.HandshakeCommandHandler(connection, this); - this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); - } - start_handshake() { - this.connection.command_handler_boss().register_handler(this.handler); - this.connection.send_command("handshakebegin", { - intention: 0, - authentication_method: this.identity.type(), - publicKey: this.identity.public_key - }).catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute begin (" + error + ")"); - }); - } - handle_proof(json) { - if (!json[0]["digest"]) { - this.trigger_fail("server too old"); - return; - } - this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { - this.connection.send_command("handshakeindentityproof", { proof: proof }).catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to proof the identity. Error: %o"), error); - if (error instanceof ServerConnectionDeclaration_1.CommandResult) - error = error.extra_message || error.message; - this.trigger_fail("failed to execute proof (" + error + ")"); - }).then(() => this.trigger_success()); - }).catch(error => { - this.trigger_fail("failed to sign message"); - }); - } - trigger_fail(message) { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_fail(message); - } - trigger_success() { - this.connection.command_handler_boss().unregister_handler(this.handler); - super.trigger_success(); - } -} -class IdentityPOWWorker { - initialize(key) { - return __awaiter(this, void 0, void 0, function* () { - this._worker = new Worker(settings_1.settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); - yield new Promise((resolve, reject) => { - const timeout_id = setTimeout(() => reject("timeout"), 1000); - this._worker.onmessage = event => { - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - if (!event.data.success) { - reject("initialize failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - this._worker.onerror = event => { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("POW Worker error %o"), event); - clearTimeout(timeout_id); - reject("Failed to load worker (" + event.message + ")"); - }; - }); - yield new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "set_data", - private_key: key, - code: "set_data" - }); - const timeout_id = setTimeout(() => reject("timeout (data)"), 1000); - this._worker.onmessage = event => { - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - if (!event.data.success) { - reject("initialize of data failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - this._worker.onmessage = event => this.handle_message(event.data); - resolve(); - }; - }); - }); - } - mine(hash, iterations, target, timeout) { - return __awaiter(this, void 0, void 0, function* () { - this._current_hash = hash; - if (target < this._best_level) - return true; - return yield new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "mine", - hash: this._current_hash, - iterations: iterations, - target: target, - code: "mine" - }); - const timeout_id = setTimeout(() => reject("timeout (mine)"), timeout || 5000); - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - if (!event.data.success) { - reject("mining failed (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - if (event.data.result) { - this._best_level = event.data.level; - this._current_hash = event.data.hash; - resolve(true); - } - else { - resolve(false); - } - }; - }); - }); - } - current_hash() { - return this._current_hash; - } - current_level() { - return this._best_level; - } - finalize(timeout) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "finalize", - code: "finalize" - }); - const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - clearTimeout(timeout_id); - if (!event.data) { - reject("invalid data"); - return; - } - if (!event.data.success) { - reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - resolve(); - }; - }); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); - } - this._worker.terminate(); - this._worker = undefined; - }); - } - handle_message(message) { - log_1.log.info(log_1.LogCategory.IDENTITIES, tr("Received message: %o"), message); - } -} -class TeaSpeakIdentity { - constructor(private_key, hash, name, initialize) { - this.private_key = private_key; - this.hash_number = hash || "0"; - this._name = name; - if (this.private_key && (typeof (initialize) === "undefined" || initialize)) { - this.initialize().catch(error => { - log_1.log.error(log_1.LogCategory.IDENTITIES, "Failed to initialize TeaSpeakIdentity (%s)", error); - this._initialized = false; - }); - } - } - static generate_new() { - return __awaiter(this, void 0, void 0, function* () { - let key; - try { - key = yield crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); - } - catch (e) { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Could not generate a new key: %o"), e); - throw "Failed to generate keypair"; - } - const private_key = yield CryptoHelper.export_ecc_key(key.privateKey, false); - const identity = new TeaSpeakIdentity(private_key, "0", undefined, false); - yield identity.initialize(); - return identity; - }); - } - static import_ts(ts_string, ini) { - return __awaiter(this, void 0, void 0, function* () { - const parse_string = string => { - const V_index = string.indexOf('V'); - if (V_index == -1) - throw "invalid input (missing V)"; - return { - hash: string.substr(0, V_index), - data: string.substr(V_index + 1), - name: "TeaSpeak user" - }; - }; - const { hash, data, name } = (!ini ? () => parse_string(ts_string) : () => { - let identity, name; - for (const line of ts_string.split("\n")) { - if (line.startsWith("identity=")) - identity = line.substr(9); - else if (line.startsWith("nickname=")) - name = line.substr(9); - } - if (!identity) - throw "missing identity keyword"; - identity = identity.match(/^"?([0-9]+V[0-9a-zA-Z+\/]+[=]+)"?$/)[1]; - if (!identity) - throw "invalid identity key value"; - const result = parse_string(identity); - result.name = name || result.name; - return result; - })(); - if (!ts_string.match(/[0-9]+/g)) - throw "invalid hash!"; - let buffer; - try { - buffer = new Uint8Array(main_1.arrayBufferBase64(data)); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, tr("Failed to decode given base64 data (%s)"), data); - throw "failed to base data (base64 decode failed)"; - } - const key64 = yield CryptoHelper.decrypt_ts_identity(new Uint8Array(main_1.arrayBufferBase64(data))); - const identity = new TeaSpeakIdentity(key64, hash, name, false); - yield identity.initialize(); - return identity; - }); - } - fallback_name() { - return this._name; - } - uid() { - return this._unique_id; - } - type() { - return Identity_1.IdentitifyType.TEAMSPEAK; - } - valid() { - return this._initialized && !!this._crypto_key && !!this._crypto_key_sign; - } - decode(data) { - return __awaiter(this, void 0, void 0, function* () { - const json = JSON.parse(data); - if (!json) - throw "invalid json"; - if (json.version == 2) { - this.private_key = json.key; - this.hash_number = json.hash; - this._name = json.name; - } - else if (json.version == 1) { - const key = json.key; - this._name = json.name; - const clone = yield TeaSpeakIdentity.import_ts(key, false); - this.private_key = clone.private_key; - this.hash_number = clone.hash_number; - } - else - throw "invalid version"; - yield this.initialize(); - }); - } - encode() { - return JSON.stringify({ - key: this.private_key, - hash: this.hash_number, - name: this._name, - version: 2 - }); - } - level() { - return __awaiter(this, void 0, void 0, function* () { - if (!this._initialized || !this.public_key) - throw "not initialized"; - const hash = new Uint8Array(yield sha_1.sha.sha1(this.public_key + this.hash_number)); - let level = 0; - while (level < hash.byteLength && hash[level] == 0) - level++; - if (level >= hash.byteLength) { - level = 256; - } - else { - let byte = hash[level]; - level <<= 3; - while ((byte & 0x1) == 0) { - level++; - byte >>= 1; - } - } - return level; - }); - } - string_add(a, b) { - const char_result = []; - const char_a = [...a].reverse().map(e => e.charCodeAt(0)); - const char_b = [...b].reverse().map(e => e.charCodeAt(0)); - let carry = false; - while (char_b.length > 0) { - let result = char_b.pop_front() + char_a.pop_front() + (carry ? 1 : 0) - 48; - if ((carry = result > 57)) - result -= 10; - char_result.push(result); - } - while (char_a.length > 0) { - let result = char_a.pop_front() + (carry ? 1 : 0); - if ((carry = result > 57)) - result -= 10; - char_result.push(result); - } - if (carry) - char_result.push(49); - return String.fromCharCode.apply(null, char_result.slice().reverse()); - } - improve_level_for(time, threads) { - return __awaiter(this, void 0, void 0, function* () { - let active = true; - setTimeout(() => active = false, time); - return yield this.improve_level(-1, threads, () => active); - }); - } - improve_level(target, threads, active_callback, callback_level, callback_status) { - return __awaiter(this, void 0, void 0, function* () { - if (!this._initialized || !this.public_key) - throw "not initialized"; - if (target == -1) - target = 0; - else if (target <= (yield this.level())) - return true; - const workers = []; - const iterations = 100000; - let current_hash; - const next_hash = () => { - if (!current_hash) - return (current_hash = this.hash_number); - if (current_hash.length < iterations.toString().length) { - current_hash = this.string_add(iterations.toString(), current_hash); - } - else { - current_hash = this.string_add(current_hash, iterations.toString()); - } - return current_hash; - }; - { - const initialize_promise = []; - for (let index = 0; index < threads; index++) { - const worker = new IdentityPOWWorker(); - workers.push(worker); - initialize_promise.push(worker.initialize(this.public_key)); - } - try { - yield Promise.all(initialize_promise); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, error); - throw "failed to initialize"; - } - } - let result = false; - let best_level = 0; - let target_level = target > 0 ? target : (yield this.level()) + 1; - const worker_promise = []; - const hash_timestamps = []; - let last_hashrate_update = 0; - const update_hashrate = () => { - if (!callback_status) - return; - const now = Date.now(); - hash_timestamps.push(now); - if (last_hashrate_update + 1000 < now) { - last_hashrate_update = now; - const timeout = now - 10 * 1000; - const rounds = hash_timestamps.filter(e => e > timeout); - callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))); - } - }; - try { - result = yield new Promise((resolve, reject) => { - let active = true; - const exit = () => { - const timeout = setTimeout(() => resolve(true), 1000); - Promise.all(worker_promise).then(result => { - clearTimeout(timeout); - resolve(true); - }).catch(error => resolve(true)); - active = false; - }; - for (const worker of workers) { - const worker_mine = () => { - if (!active) - return; - const promise = worker.mine(next_hash(), iterations, target_level); - const p = promise.then(result => { - update_hashrate(); - worker_promise.remove(p); - if (result.valueOf()) { - if (worker.current_level() > best_level) { - this.hash_number = worker.current_hash(); - log_1.log.info(log_1.LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); - best_level = worker.current_level(); - if (callback_level) - callback_level(best_level); - } - if (active) { - if (target > 0) - exit(); - else - target_level = best_level + 1; - } - } - if (active && (active = active_callback())) - setTimeout(() => worker_mine(), 0); - else { - exit(); - } - return Promise.resolve(); - }).catch(error => { - worker_promise.remove(p); - log_1.log.warn(log_1.LogCategory.IDENTITIES, "POW worker error %o", error); - reject(error); - return Promise.resolve(); - }); - worker_promise.push(p); - }; - worker_mine(); - } - }); - } - catch (error) { - } - { - const finalize_promise = []; - for (const worker of workers) - finalize_promise.push(worker.finalize(250)); - try { - yield Promise.all(finalize_promise); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, error); - throw "failed to finalize"; - } - } - return result; - }); - } - initialize() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.private_key) - throw "Invalid private key"; - let jwk; - try { - jwk = yield CryptoHelper.decode_tomcrypt_key(this.private_key); - if (!jwk) - throw "result undefined"; - } - catch (error) { - throw "failed to parse key (" + error + ")"; - } - try { - this._crypto_key_sign = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ["sign"]); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, error); - throw "failed to create crypto sign key"; - } - try { - this._crypto_key = yield crypto.subtle.importKey("jwk", jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ["deriveKey"]); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, error); - throw "failed to create crypto key"; - } - try { - this.public_key = yield CryptoHelper.export_ecc_key(this._crypto_key, true); - this._unique_id = main_1.base64_encode_ab(yield sha_1.sha.sha1(this.public_key)); - } - catch (error) { - log_1.log.error(log_1.LogCategory.IDENTITIES, error); - throw "failed to calculate unique id"; - } - this._initialized = true; - }); - } - export_ts(ini) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.private_key) - throw "Invalid private key"; - const identity = this.hash_number + "V" + (yield CryptoHelper.encrypt_ts_identity(new Uint8Array(main_1.str2ab8(this.private_key)))); - if (!ini) - return identity; - return "[Identity]\n" + - "id=TeaWeb-Exported\n" + - "identity=\"" + identity + "\"\n" + - "nickname=\"" + this.fallback_name() + "\"\n" + - "phonetic_nickname="; - }); - } - sign_message(message, hash = "SHA-256") { - return __awaiter(this, void 0, void 0, function* () { - const sign_buffer = yield crypto.subtle.sign({ - name: "ECDSA", - hash: hash - }, this._crypto_key_sign, main_1.str2ab8(message)); - const sign = new Uint8Array(sign_buffer); - const buffer = new Uint8Array(72); - let index = 0; - { - buffer[index++] = 0x30; - buffer[index++] = 0x00; - } - { - buffer[index++] = 0x02; - buffer[index++] = 0x20; - if (sign[0] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - for (let i = 0; i < 32; i++) - buffer[index++] = sign[i]; - } - { - buffer[index++] = 0x02; - buffer[index++] = 0x20; - if (sign[32] > 0x7F) { - buffer[index - 1] += 1; - buffer[index++] = 0; - } - for (let i = 0; i < 32; i++) - buffer[index++] = sign[32 + i]; - } - buffer[1] = index - 2; - return main_1.base64_encode_ab(buffer.subarray(0, index)); - }); - } - spawn_identity_handshake_handler(connection) { - return new TeaSpeakHandshakeHandler(connection, this); - } -} -exports.TeaSpeakIdentity = TeaSpeakIdentity; - - -/***/ }), - -/***/ "./shared/js/profiles/identities/teaspeak-forum.ts": -/*!*********************************************************!*\ - !*** ./shared/js/profiles/identities/teaspeak-forum.ts ***! - \*********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const settings_1 = __webpack_require__(/*! ../../settings */ "./shared/js/settings.ts"); -const TeaForumIdentity_1 = __webpack_require__(/*! ./TeaForumIdentity */ "./shared/js/profiles/identities/TeaForumIdentity.ts"); -var forum; -(function (forum) { - let gcaptcha; - (function (gcaptcha) { - function initialize() { - return __awaiter(this, void 0, void 0, function* () { - if (typeof (window.grecaptcha) === "undefined") { - let script = document.createElement("script"); - script.async = true; - let timeout; - const callback_name = "captcha_callback_" + Math.random().toString().replace(".", ""); - try { - yield new Promise((resolve, reject) => { - script.onerror = reject; - window[callback_name] = resolve; - script.src = "https://www.google.com/recaptcha/api.js?onload=" + encodeURIComponent(callback_name) + "&render=explicit"; - document.body.append(script); - timeout = setTimeout(() => reject("timeout"), 15000); - }); - } - catch (error) { - script.remove(); - script = undefined; - console.error(tr("Failed to fetch recaptcha javascript source: %o"), error); - throw tr("failed to download source"); - } - finally { - if (script) - script.onerror = undefined; - delete window[callback_name]; - clearTimeout(timeout); - } - } - if (typeof (window.grecaptcha) === "undefined") - throw tr("failed to load recaptcha"); - }); - } - gcaptcha.initialize = initialize; - function spawn(container, key, callback_data) { - return __awaiter(this, void 0, void 0, function* () { - try { - yield initialize(); - } - catch (error) { - console.error(tr("Failed to initialize G-Recaptcha. Error: %o"), error); - throw tr("initialisation failed"); - } - if (container.attr("captcha-uuid")) - window.grecaptcha.reset(container.attr("captcha-uuid")); - else { - container.attr("captcha-uuid", window.grecaptcha.render(container[0], { - "sitekey": key, - callback: callback_data - })); - } - }); - } - gcaptcha.spawn = spawn; - })(gcaptcha = forum.gcaptcha || (forum.gcaptcha = {})); - function api_url() { - return settings_1.settings.static_global(settings_1.Settings.KEY_TEAFORO_URL); - } - class Data { - constructor(auth, raw, sign) { - this.auth_key = auth; - this.raw = raw; - this.sign = sign; - this.parsed = JSON.parse(raw); - } - data_json() { return this.raw; } - data_sign() { return this.sign; } - name() { return this.parsed.user_name; } - user_id() { return this.parsed.user_id; } - user_group() { return this.parsed.user_group_id; } - is_stuff() { return this.parsed.is_staff; } - is_premium() { return this.parsed.user_groups.indexOf(5) != -1; } - data_age() { return new Date(this.parsed.data_age); } - is_expired() { return this.parsed.data_age + 48 * 60 * 60 * 1000 < Date.now(); } - should_renew() { return this.parsed.data_age + 24 * 60 * 60 * 1000 < Date.now(); } - } - forum.Data = Data; - let _data; - function logged_in() { - return !!_data && !_data.is_expired(); - } - forum.logged_in = logged_in; - function data() { return _data; } - forum.data = data; - function login(username, password, captcha) { - return __awaiter(this, void 0, void 0, function* () { - let response; - try { - response = yield new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/login", - type: "POST", - cache: false, - data: { - username: username, - password: password, - remember: true, - "g-recaptcha-response": captcha - }, - crossDomain: true, - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Login request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }); - }); - } - catch (error) { - return { - status: "error", - error_message: tr("failed to send login request") - }; - } - if (response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - return { - status: "error", - error_message: (response["errors"] || [])[0] || tr("Unknown error") - }; - } - if (!response["success"]) { - console.error(tr("Login failed. Response %o"), response); - let message = tr("failed to login"); - let captcha; - if (response["code"] == 1 || response["code"] == 3) - message = tr("Invalid username or password"); - if (response["code"] == 2 || response["code"] == 3) { - captcha = { - type: response["captcha"]["type"], - data: response["captcha"]["siteKey"] - }; - if (response["code"] == 2) - message = tr("captcha required"); - } - return { - status: typeof (captcha) !== "undefined" ? "captcha" : "error", - error_message: message, - captcha: captcha - }; - } - try { - _data = new Data(response["auth-key"], response["data"], response["sign"]); - localStorage.setItem("teaspeak-forum-data", response["data"]); - localStorage.setItem("teaspeak-forum-sign", response["sign"]); - localStorage.setItem("teaspeak-forum-auth", response["auth-key"]); - TeaForumIdentity_1.update_forum(); - } - catch (error) { - console.error(tr("Failed to parse forum given data: %o"), error); - return { - status: "error", - error_message: tr("Failed to parse response data") - }; - } - return { - status: "success" - }; - }); - } - forum.login = login; - function renew_data() { - return __awaiter(this, void 0, void 0, function* () { - let response; - try { - response = yield new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/renew-data", - type: "GET", - cache: false, - crossDomain: true, - data: { - "auth-key": _data.auth_key - }, - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Renew request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }); - }); - } - catch (error) { - throw tr("failed to send renew request"); - } - if (response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - throw (response["errors"] || [])[0] || tr("Unknown error"); - } - if (!response["success"]) { - if (response["code"] == 1) { - return "login-required"; - } - throw "invalid error code (" + response["code"] + ")"; - } - if (!response["data"] || !response["sign"]) - throw tr("response missing data"); - console.debug(tr("Renew succeeded. Parsing data.")); - try { - _data = new Data(_data.auth_key, response["data"], response["sign"]); - localStorage.setItem("teaspeak-forum-data", response["data"]); - localStorage.setItem("teaspeak-forum-sign", response["sign"]); - TeaForumIdentity_1.update_forum(); - } - catch (error) { - console.error(tr("Failed to parse forum given data: %o"), error); - throw tr("failed to parse data"); - } - return "success"; - }); - } - forum.renew_data = renew_data; - function logout() { - return __awaiter(this, void 0, void 0, function* () { - if (!logged_in()) - return; - let response; - try { - response = yield new Promise((resolve, reject) => { - $.ajax({ - url: api_url() + "?web-api/v1/logout", - type: "GET", - cache: false, - crossDomain: true, - data: { - "auth-key": _data.auth_key - }, - success: resolve, - error: (xhr, status, error) => { - console.log(tr("Logout request failed %o: %o"), status, error); - reject(tr("request failed")); - } - }); - }); - } - catch (error) { - throw tr("failed to send logout request"); - } - if (response["status"] !== "ok") { - console.error(tr("Response status not okey. Error happend: %o"), response); - throw (response["errors"] || [])[0] || tr("Unknown error"); - } - if (!response["success"]) { - if (response["code"] != 1) { - throw "invalid error code (" + response["code"] + ")"; - } - } - _data = undefined; - localStorage.removeItem("teaspeak-forum-data"); - localStorage.removeItem("teaspeak-forum-sign"); - localStorage.removeItem("teaspeak-forum-auth"); - TeaForumIdentity_1.update_forum(); - }); - } - forum.logout = logout; - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "TeaForo initialize", - priority: 10, - function: () => __awaiter(this, void 0, void 0, function* () { - const raw_data = localStorage.getItem("teaspeak-forum-data"); - const raw_sign = localStorage.getItem("teaspeak-forum-sign"); - const forum_auth = localStorage.getItem("teaspeak-forum-auth"); - if (!raw_data || !raw_sign || !forum_auth) { - console.log(tr("No TeaForo authentification found. TeaForo connection status: unconnected")); - return; - } - try { - _data = new Data(forum_auth, raw_data, raw_sign); - } - catch (error) { - console.error(tr("Failed to initialize TeaForo connection from local data. Error: %o"), error); - return; - } - if (_data.should_renew()) { - console.info(tr("TeaForo data should be renewed. Executing renew.")); - renew_data().then(status => { - if (status === "success") { - console.info(tr("TeaForo data has been successfully renewed.")); - } - else { - console.warn(tr("Failed to renew TeaForo data. New login required.")); - localStorage.removeItem("teaspeak-forum-data"); - localStorage.removeItem("teaspeak-forum-sign"); - localStorage.removeItem("teaspeak-forum-auth"); - } - }).catch(error => { - console.warn(tr("Failed to renew TeaForo data. An error occurred: %o"), error); - }); - return; - } - if (_data && _data.is_expired()) { - console.error(tr("TeaForo data is expired. TeaForo connection isn't available!")); - } - }) - }); -})(forum = exports.forum || (exports.forum = {})); - - -/***/ }), - -/***/ "./shared/js/settings.ts": -/*!*******************************!*\ - !*** ./shared/js/settings.ts ***! - \*******************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const log_1 = __webpack_require__(/*! ./log */ "./shared/js/log.ts"); -const modal_1 = __webpack_require__(/*! ./ui/elements/modal */ "./shared/js/ui/elements/modal.ts"); -if (typeof (customElements) !== "undefined") { - try { - class X_Properties extends HTMLElement { - } - class X_Property extends HTMLElement { - } - customElements.define('x-properties', X_Properties, { extends: 'div' }); - customElements.define('x-property', X_Property, { extends: 'div' }); - } - catch (error) { - console.warn("failed to define costum elements"); - } -} -class SettingsBase { - static transformStO(input, _default, default_type) { - default_type = default_type || typeof _default; - if (typeof input === "undefined") - return _default; - if (default_type === "string") - return input; - else if (default_type === "number") - return parseInt(input); - else if (default_type === "boolean") - return (input == "1" || input == "true"); - else if (default_type === "undefined") - return input; - return JSON.parse(input); - } - static transformOtS(input) { - if (typeof input === "string") - return input; - else if (typeof input === "number") - return input.toString(); - else if (typeof input === "boolean") - return input ? "1" : "0"; - else if (typeof input === "undefined") - return undefined; - return JSON.stringify(input); - } - static resolveKey(key, _default, resolver, default_type) { - let value = resolver(key.key); - if (!value) { - for (const fallback of key.fallback_keys || []) { - value = resolver(fallback); - if (typeof (value) === "string") { - const importer = (key.fallback_imports || {})[fallback]; - if (importer) - return importer(value); - break; - } - } - } - if (typeof (value) !== 'string') - return _default; - return SettingsBase.transformStO(value, _default, default_type); - } - static keyify(key) { - if (typeof (key) === "string") - return { key: key }; - if (typeof (key) === "object" && key.key) - return key; - throw "key is not a key"; - } -} -exports.SettingsBase = SettingsBase; -SettingsBase.UPDATE_DIRECT = true; -class StaticSettings extends SettingsBase { - constructor(_reserved = undefined) { - super(); - if (_reserved && !StaticSettings._instance) { - this._staticPropsTag = $("#properties"); - this.initializeStatic(); - } - else { - this._handle = StaticSettings.instance; - } - } - static get instance() { - if (!this._instance) - this._instance = new StaticSettings(true); - return this._instance; - } - initializeStatic() { - let search; - if (window.opener && window.opener !== window) { - search = new URL(window.location.href).search; - } - else { - search = location.search; - } - search.substr(1).split("&").forEach(part => { - let item = part.split("="); - $("") - .attr("key", item[0]) - .attr("value", item[1]) - .appendTo(this._staticPropsTag); - }); - } - static(key, _default, default_type) { - if (this._handle) - return this._handle.static(key, _default, default_type); - key = StaticSettings.keyify(key); - return StaticSettings.resolveKey(key, _default, key => { - let result = this._staticPropsTag.find("[key='" + key + "']"); - if (result.length > 0) - return decodeURIComponent(result.last().attr('value')); - return false; - }, default_type); - } - deleteStatic(key) { - if (this._handle) { - this._handle.deleteStatic(key); - return; - } - key = StaticSettings.keyify(key); - let result = this._staticPropsTag.find("[key='" + key.key + "']"); - if (result.length != 0) - result.detach(); - } -} -exports.StaticSettings = StaticSettings; -class Settings extends StaticSettings { - constructor() { - super(); - this.cacheGlobal = {}; - this.updated = false; - const json = localStorage.getItem("settings.global"); - try { - this.cacheGlobal = JSON.parse(json); - } - catch (error) { - log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to load global settings!\nJson: %s\nError: %o"), json, error); - const show_popup = () => { - modal_1.createErrorModal(tr("Failed to load global settings"), tr("Failed to load global client settings!\nLookup console for more information.")).open(); - }; - if (!loader.finished()) - loader.register_task(loader.Stage.LOADED, { - priority: 0, - name: "Settings error", - function: () => __awaiter(this, void 0, void 0, function* () { return show_popup(); }) - }); - else - show_popup(); - } - if (!this.cacheGlobal) - this.cacheGlobal = {}; - this.saveWorker = setInterval(() => { - if (this.updated) - this.save(); - }, 5 * 1000); - } - static initialize() { - exports.settings = new Settings(); - } - static_global(key, _default) { - const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; - const default_object = { seed: Math.random() }; - let _static = this.static(key, default_object, typeof _default); - if (_static !== default_object) - return StaticSettings.transformStO(_static, actual_default); - return this.global(key, actual_default); - } - global(key, _default) { - const actual_default = typeof (_default) === "undefined" && typeof (key) === "object" && 'default_value' in key ? key.default_value : _default; - return StaticSettings.resolveKey(Settings.keyify(key), actual_default, key => this.cacheGlobal[key]); - } - changeGlobal(key, value) { - key = Settings.keyify(key); - if (this.cacheGlobal[key.key] == value) - return; - this.updated = true; - this.cacheGlobal[key.key] = StaticSettings.transformOtS(value); - if (Settings.UPDATE_DIRECT) - this.save(); - } - save() { - this.updated = false; - let global = JSON.stringify(this.cacheGlobal); - localStorage.setItem("settings.global", global); - if (localStorage.save) - localStorage.save(); - } -} -exports.Settings = Settings; -Settings.KEY_USER_IS_NEW = { - key: 'user_is_new_user', - default_value: true -}; -Settings.KEY_DISABLE_COSMETIC_SLOWDOWN = { - key: 'disable_cosmetic_slowdown', - description: 'Disable the cosmetic slowdows in some processes, like icon upload.' -}; -Settings.KEY_DISABLE_CONTEXT_MENU = { - key: 'disableContextMenu', - description: 'Disable the context menu for the channel tree which allows to debug the DOM easier' -}; -Settings.KEY_DISABLE_GLOBAL_CONTEXT_MENU = { - key: 'disableGlobalContextMenu', - description: 'Disable the general context menu prevention', - default_value: false -}; -Settings.KEY_DISABLE_UNLOAD_DIALOG = { - key: 'disableUnloadDialog', - description: 'Disables the unload popup on side closing' -}; -Settings.KEY_DISABLE_VOICE = { - key: 'disableVoice', - description: 'Disables the voice bridge. If disabled, the audio and codec workers aren\'t required anymore' -}; -Settings.KEY_DISABLE_MULTI_SESSION = { - key: 'disableMultiSession', - default_value: false, - require_restart: true -}; -Settings.KEY_LOAD_DUMMY_ERROR = { - key: 'dummy_load_error', - description: 'Triggers a loading error at the end of the loading process.' -}; -Settings.KEY_CONTROL_MUTE_INPUT = { - key: 'mute_input' -}; -Settings.KEY_CONTROL_MUTE_OUTPUT = { - key: 'mute_output' -}; -Settings.KEY_CONTROL_SHOW_QUERIES = { - key: 'show_server_queries' -}; -Settings.KEY_CONTROL_CHANNEL_SUBSCRIBE_ALL = { - key: 'channel_subscribe_all' -}; -Settings.KEY_FLAG_CONNECT_DEFAULT = { - key: 'connect_default' -}; -Settings.KEY_CONNECT_ADDRESS = { - key: 'connect_address' -}; -Settings.KEY_CONNECT_PROFILE = { - key: 'connect_profile', - default_value: 'default' -}; -Settings.KEY_CONNECT_USERNAME = { - key: 'connect_username' -}; -Settings.KEY_CONNECT_PASSWORD = { - key: 'connect_password' -}; -Settings.KEY_FLAG_CONNECT_PASSWORD = { - key: 'connect_password_hashed' -}; -Settings.KEY_CONNECT_HISTORY = { - key: 'connect_history' -}; -Settings.KEY_CONNECT_NO_DNSPROXY = { - key: 'connect_no_dnsproxy', - default_value: false -}; -Settings.KEY_CERTIFICATE_CALLBACK = { - key: 'certificate_callback' -}; -Settings.KEY_SOUND_MASTER = { - key: 'audio_master_volume', - default_value: 100 -}; -Settings.KEY_SOUND_MASTER_SOUNDS = { - key: 'audio_master_volume_sounds', - default_value: 100 -}; -Settings.KEY_CHAT_FIXED_TIMESTAMPS = { - key: 'chat_fixed_timestamps', - default_value: false, - description: 'Enables fixed timestamps for chat messages and disabled the updating once (2 seconds ago... etc)' -}; -Settings.KEY_CHAT_COLLOQUIAL_TIMESTAMPS = { - key: 'chat_colloquial_timestamps', - default_value: true, - description: 'Enabled colloquial timestamp formatting like "Yesterday at ..." or "Today at ..."' -}; -Settings.KEY_CHAT_COLORED_EMOJIES = { - key: 'chat_colored_emojies', - default_value: true, - description: 'Enables colored emojies powered by Twemoji' -}; -Settings.KEY_CHAT_TAG_URLS = { - key: 'chat_tag_urls', - default_value: true, - description: 'Automatically link urls with [url]' -}; -Settings.KEY_CHAT_ENABLE_MARKDOWN = { - key: 'chat_enable_markdown', - default_value: true, - description: 'Enabled markdown chat support.' -}; -Settings.KEY_CHAT_ENABLE_BBCODE = { - key: 'chat_enable_bbcode', - default_value: false, - description: 'Enabled bbcode support in chat.' -}; -Settings.KEY_CHAT_IMAGE_WHITELIST_REGEX = { - key: 'chat_image_whitelist_regex', - default_value: JSON.stringify([]) -}; -Settings.KEY_SWITCH_INSTANT_CHAT = { - key: 'switch_instant_chat', - default_value: true, - description: 'Directly switch to channel chat on channel select' -}; -Settings.KEY_SWITCH_INSTANT_CLIENT = { - key: 'switch_instant_client', - default_value: true, - description: 'Directly switch to client info on client select' -}; -Settings.KEY_HOSTBANNER_BACKGROUND = { - key: 'hostbanner_background', - default_value: false, - description: 'Enables a default background begind the hostbanner' -}; -Settings.KEY_CHANNEL_EDIT_ADVANCED = { - key: 'channel_edit_advanced', - default_value: false, - description: 'Edit channels in advanced mode with a lot more settings' -}; -Settings.KEY_PERMISSIONS_SHOW_ALL = { - key: 'permissions_show_all', - default_value: false, - description: 'Show all permissions even thou they dont make sense for the server/channel group' -}; -Settings.KEY_TEAFORO_URL = { - key: "teaforo_url", - default_value: "https://forum.teaspeak.de/" -}; -Settings.KEY_FONT_SIZE = { - key: "font_size" -}; -Settings.KEY_ICON_SIZE = { - key: "icon_size", - default_value: 100 -}; -Settings.KEY_LAST_INVITE_LINK_TYPE = { - key: "last_invite_link_type", - default_value: "tea-web" -}; -Settings.FN_INVITE_LINK_SETTING = name => { - return { - key: 'invite_link_setting_' + name - }; -}; -Settings.FN_SERVER_CHANNEL_SUBSCRIBE_MODE = channel => { - return { - key: 'channel_subscribe_mode_' + channel - }; -}; -Settings.FN_PROFILE_RECORD = name => { - return { - key: 'profile_record' + name - }; -}; -Settings.KEYS = (() => { - const result = []; - for (const key in Settings) { - if (!key.toUpperCase().startsWith("KEY_")) - continue; - if (key.toUpperCase() == "KEYS") - continue; - result.push(key); - } - return result; -})(); -class ServerSettings extends SettingsBase { - constructor() { - super(); - this.cacheServer = {}; - this._server_settings_updated = false; - this._destroyed = false; - this._server_save_worker = setInterval(() => { - if (this._server_settings_updated) - this.save(); - }, 5 * 1000); - } - destroy() { - this._destroyed = true; - this._server_unique_id = undefined; - this.cacheServer = undefined; - clearInterval(this._server_save_worker); - this._server_save_worker = undefined; - } - server(key, _default) { - if (this._destroyed) - throw "destroyed"; - return StaticSettings.resolveKey(Settings.keyify(key), _default, key => this.cacheServer[key]); - } - changeServer(key, value) { - if (this._destroyed) - throw "destroyed"; - key = Settings.keyify(key); - if (this.cacheServer[key.key] == value) - return; - this._server_settings_updated = true; - this.cacheServer[key.key] = StaticSettings.transformOtS(value); - if (Settings.UPDATE_DIRECT) - this.save(); - } - setServer(server_unique_id) { - if (this._destroyed) - throw "destroyed"; - if (this._server_unique_id) { - this.save(); - this.cacheServer = {}; - this._server_unique_id = undefined; - } - this._server_unique_id = server_unique_id; - if (this._server_unique_id) { - const json = localStorage.getItem("settings.server_" + server_unique_id); - try { - this.cacheServer = JSON.parse(json); - } - catch (error) { - log_1.log.error(log_1.LogCategory.GENERAL, tr("Failed to load server settings for server %s!\nJson: %s\nError: %o"), server_unique_id, json, error); - } - if (!this.cacheServer) - this.cacheServer = {}; - } - } - save() { - if (this._destroyed) - throw "destroyed"; - this._server_settings_updated = false; - if (this._server_unique_id) { - let server = JSON.stringify(this.cacheServer); - localStorage.setItem("settings.server_" + this._server_unique_id, server); - if (localStorage.save) - localStorage.save(); - } - } -} -exports.ServerSettings = ServerSettings; - - -/***/ }), - -/***/ "./shared/js/ui/elements/modal.ts": -/*!****************************************!*\ - !*** ./shared/js/ui/elements/modal.ts ***! - \****************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const PPTListener_1 = __webpack_require__(/*! ../../PPTListener */ "./shared/js/PPTListener.ts"); -var ElementType; -(function (ElementType) { - ElementType[ElementType["HEADER"] = 0] = "HEADER"; - ElementType[ElementType["BODY"] = 1] = "BODY"; - ElementType[ElementType["FOOTER"] = 2] = "FOOTER"; -})(ElementType = exports.ElementType || (exports.ElementType = {})); -exports.ModalFunctions = { - divify: function (val) { - if (val.length > 1) - return $.spawn("div").append(val); - return val; - }, - jqueriefy: function (val, type) { - if (typeof (val) === "function") - val = val(); - if (val instanceof jQuery) - return val; - if (Array.isArray(val)) { - if (val.length == 0) - return undefined; - return val.map(e => this.jqueriefy(e)); - } - switch (typeof val) { - case "string": - if (type == ElementType.HEADER) - return $.spawn("div").addClass("modal-title").text(val); - return $("
" + val + "
"); - case "object": return val; - case "undefined": - return undefined; - default: - console.error(("Invalid type %o"), typeof val); - return $(); - } - }, - warpProperties(data) { - if (data instanceof ModalProperties) { - return data; - } - else { - const props = new ModalProperties(); - for (const key of Object.keys(data)) - props[key] = data[key]; - return props; - } - } -}; -class ModalProperties { - constructor() { - this.header = () => "HEADER"; - this.body = () => "BODY"; - this.footer = () => "FOOTER"; - this.closeListener = () => { }; - this.height = "auto"; - this.closeable = true; - this.template_properties = {}; - this.trigger_tab = true; - this.full_size = false; - } - registerCloseListener(listener) { - if (this.closeListener) { - if ($.isArray(this.closeListener)) - this.closeListener.push(listener); - else - this.closeListener = [this.closeListener, listener]; - } - else - this.closeListener = listener; - return this; - } - triggerClose() { - if ($.isArray(this.closeListener)) - for (let listener of this.closeListener) - listener(); - else - this.closeListener(); - } -} -exports.ModalProperties = ModalProperties; -var modal; -(function (modal) { - function initialize_modals() { - register_global_events(); - } - modal.initialize_modals = initialize_modals; - const scrollSize = 18; - function scroll_bar_clicked(event) { - const x = event.pageX, y = event.pageY, e = $(event.target); - if (e.hasScrollBar("height")) { - const top = e.offset().top; - const right = e.offset().left + e.width(); - const bottom = top + e.height(); - const left = right - scrollSize; - if ((y >= top && y <= bottom) && (x >= left && x <= right)) - return true; - } - if (e.hasScrollBar("width")) { - const bottom = e.offset().top + e.height(); - const top = bottom - scrollSize; - const left = e.offset().left; - const right = left + e.width(); - if ((y >= top && y <= bottom) && (x >= left && x <= right)) - return true; - } - return false; - } - function register_global_events() { - $(document).on('mousedown', (event) => { - if (_global_modal_count == 0 || typeof (event.pageX) === "undefined" || typeof (event.pageY) === "undefined") - return; - let element = event.target; - const original = element; - do { - if (element.classList.contains('modal-content')) - break; - if (!element.classList.contains('modal')) - continue; - if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) - break; - if (element === original && scroll_bar_clicked(event)) { - _global_modal_last_time = Date.now(); - break; - } - $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); - break; - } while ((element = element.parentElement)); - }); - $(document).on('keyup', (event) => { - if (_global_modal_count == 0 || typeof (event.target) === "undefined") - return; - if (event.key !== "Escape") - return; - let element = event.target; - if (element.nodeName == "HTMLInputElement" || element.nodeName == "HTMLSelectElement" || element.nodeName == "HTMLTextAreaElement") - return; - do { - if (element.classList.contains('modal-content')) - break; - if (!element.classList.contains('modal')) - continue; - if (element == _global_modal_last && _global_modal_last_time + 100 > Date.now()) - break; - $(element).find("> .modal-dialog > .modal-content > .modal-header .button-modal-close").trigger('click'); - break; - } while ((element = element.parentElement)); - }); - } -})(modal = exports.modal || (exports.modal = {})); -modal.initialize_modals(); -let _global_modal_count = 0; -let _global_modal_last; -let _global_modal_last_time; -class Modal { - constructor(props) { - this.open_listener = []; - this.close_listener = []; - this.properties = props; - this.shown = false; - } - get htmlTag() { - if (!this._htmlTag) - this._create(); - return this._htmlTag; - } - _create() { - const header = exports.ModalFunctions.jqueriefy(this.properties.header, ElementType.HEADER); - const body = exports.ModalFunctions.jqueriefy(this.properties.body, ElementType.BODY); - const footer = exports.ModalFunctions.jqueriefy(this.properties.footer, ElementType.FOOTER); - const template = $(this.properties.template || "#tmpl_modal"); - const properties = { - modal_header: header, - modal_body: body, - modal_footer: footer, - closeable: this.properties.closeable, - full_size: this.properties.full_size - }; - if (this.properties.template_properties) - Object.assign(properties, this.properties.template_properties); - const tag = template.renderTag(properties); - if (typeof (this.properties.width) !== "undefined" && typeof (this.properties.min_width) !== "undefined") - tag.find(".modal-content") - .css("min-width", this.properties.min_width) - .css("width", this.properties.width); - else if (typeof (this.properties.width) !== "undefined") - tag.find(".modal-content").css("min-width", this.properties.width); - else if (typeof (this.properties.min_width) !== "undefined") - tag.find(".modal-content").css("min-width", this.properties.min_width); - this.close_elements = tag.find(".button-modal-close"); - this.close_elements.toggle(this.properties.closeable).on('click', event => { - if (this.properties.closeable) - this.close(); - }); - this._htmlTag = tag; - this._htmlTag.find("input").on('change', event => { - $(event.target).parents(".form-group").toggleClass('is-filled', !!event.target.value); - }); - this._htmlTag.on('hide.bs.modal', event => !this.properties.closeable || this.close()); - this._htmlTag.on('hidden.bs.modal', event => this._htmlTag.remove()); - } - open() { - if (this.shown) - return; - _global_modal_last_time = Date.now(); - _global_modal_last = this.htmlTag[0]; - this.shown = true; - this.htmlTag.appendTo($("body")); - _global_modal_count++; - this.htmlTag.show(); - setTimeout(() => this.htmlTag.addClass('shown'), 0); - setTimeout(() => { - for (const listener of this.open_listener) - listener(); - this.htmlTag.find(".tab").trigger('tab.resize'); - }, 300); - } - close() { - if (!this.shown) - return; - _global_modal_count--; - if (_global_modal_last === this.htmlTag[0]) - _global_modal_last = undefined; - this.shown = false; - this.htmlTag.removeClass('shown'); - setTimeout(() => { - this.htmlTag.remove(); - this._htmlTag = undefined; - }, 300); - this.properties.triggerClose(); - for (const listener of this.close_listener) - listener(); - } - set_closeable(flag) { - if (flag === this.properties.closeable) - return; - this.properties.closeable = flag; - this.close_elements.toggle(flag); - } -} -exports.Modal = Modal; -function createModal(data) { - return new Modal(exports.ModalFunctions.warpProperties(data)); -} -exports.createModal = createModal; -class InputModalProperties extends ModalProperties { -} -exports.InputModalProperties = InputModalProperties; -function createInputModal(headMessage, question, validator, callback, props = {}) { - props = exports.ModalFunctions.warpProperties(props); - props.template_properties || (props.template_properties = {}); - props.template_properties.field_title = props.field_title; - props.template_properties.field_label = props.field_label; - props.template_properties.field_placeholder = props.field_placeholder; - props.template_properties.error_message = props.error_message; - props.template = "#tmpl_modal_input"; - props.header = headMessage; - props.template_properties.question = exports.ModalFunctions.jqueriefy(question); - const modal = createModal(props); - const input = modal.htmlTag.find(".container-value input"); - const button_cancel = modal.htmlTag.find(".button-cancel"); - const button_submit = modal.htmlTag.find(".button-submit"); - let submited = false; - input.on('keyup change', event => { - const str = input.val(); - const valid = str !== undefined && validator(str); - input.attr("pattern", valid ? null : "^[a]{1000}$").toggleClass("is-invalid", !valid); - button_submit.prop("disabled", !valid); - }); - input.on('keydown', event => { - if (event.keyCode !== PPTListener_1.KeyCode.KEY_RETURN || event.shiftKey) - return; - if (button_submit.prop("disabled")) - return; - button_submit.trigger('click'); - }); - button_submit.on('click', event => { - if (!submited) { - submited = true; - const str = input.val(); - if (str !== undefined && validator(str)) - callback(str); - else - callback(false); - } - modal.close(); - }).prop("disabled", !validator("")); - button_cancel.on('click', event => { - if (!submited) { - submited = true; - callback(false); - } - modal.close(); - }); - modal.open_listener.push(() => input.focus()); - modal.close_listener.push(() => button_cancel.trigger('click')); - return modal; -} -exports.createInputModal = createInputModal; -function createErrorModal(header, message, props = { footer: undefined }) { - props = exports.ModalFunctions.warpProperties(props); - (props.template_properties || (props.template_properties = {})).header_class = "modal-header-error"; - props.header = header; - props.body = message; - const modal = createModal(props); - modal.htmlTag.find(".modal-body").addClass("modal-error"); - return modal; -} -exports.createErrorModal = createErrorModal; -function createInfoModal(header, message, props = { footer: undefined }) { - props = exports.ModalFunctions.warpProperties(props); - (props.template_properties || (props.template_properties = {})).header_class = "modal-header-info"; - props.header = header; - props.body = message; - const modal = createModal(props); - modal.htmlTag.find(".modal-body").addClass("modal-info"); - return modal; -} -exports.createInfoModal = createInfoModal; - - -/***/ }), - -/***/ "./shared/js/ui/frames/chat.ts": -/*!*************************************!*\ - !*** ./shared/js/ui/frames/chat.ts ***! - \*************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const log_1 = __webpack_require__(/*! ../../log */ "./shared/js/log.ts"); -var ChatType; -(function (ChatType) { - ChatType[ChatType["GENERAL"] = 0] = "GENERAL"; - ChatType[ChatType["SERVER"] = 1] = "SERVER"; - ChatType[ChatType["CHANNEL"] = 2] = "CHANNEL"; - ChatType[ChatType["CLIENT"] = 3] = "CLIENT"; -})(ChatType = exports.ChatType || (exports.ChatType = {})); -var MessageHelper; -(function (MessageHelper) { - function htmlEscape(message) { - const div = document.createElement('div'); - div.innerText = message; - message = div.innerHTML; - return message.replace(/ /g, ' ').split(/
/); - } - MessageHelper.htmlEscape = htmlEscape; - function formatElement(object, escape_html = true) { - if ($.isArray(object)) { - let result = []; - for (let element of object) - result.push(...formatElement(element, escape_html)); - return result; - } - else if (typeof (object) == "string") { - if (object.length == 0) - return []; - return escape_html ? - htmlEscape(object).map((entry, idx, array) => $.spawn("a").css("display", (idx == 0 || idx + 1 == array.length ? "inline" : "") + "block").html(entry == "" && idx != 0 ? " " : entry)) : - [$.spawn("div").css("display", "inline-block").html(object)]; - } - else if (typeof (object) === "object") { - if (object instanceof $) - return [object]; - return formatElement(""); - } - else if (typeof (object) === "function") - return formatElement(object(), escape_html); - else if (typeof (object) === "undefined") - return formatElement(""); - else if (typeof (object) === "number") - return [$.spawn("a").text(object)]; - return formatElement(""); - } - MessageHelper.formatElement = formatElement; - function formatMessage(pattern, ...objects) { - let begin = 0, found = 0; - let result = []; - do { - found = pattern.indexOf('{', found); - if (found == -1 || pattern.length <= found + 1) { - result.push(...formatElement(pattern.substr(begin))); - break; - } - if (found > 0 && pattern[found - 1] == '\\') { - found++; - continue; - } - result.push(...formatElement(pattern.substr(begin, found - begin))); - let offset = 0; - if (pattern[found + 1] == ':') { - offset++; - while (pattern[found + 1 + offset] != ':' && found + 1 + offset < pattern.length) - offset++; - const tag = pattern.substr(found + 2, offset - 1); - offset++; - if (pattern[found + offset + 1] != '}' && found + 1 + offset < pattern.length) { - found++; - continue; - } - result.push($.spawn(tag)); - } - else { - let number; - while ("0123456789".includes(pattern[found + 1 + offset])) - offset++; - number = parseInt(offset > 0 ? pattern.substr(found + 1, offset) : "0"); - if (pattern[found + offset + 1] != '}') { - found++; - continue; - } - if (objects.length < number) - log_1.log.warn(log_1.LogCategory.GENERAL, tr("Message to format contains invalid index (%o)"), number); - result.push(...formatElement(objects[number])); - } - found = found + 1 + offset; - begin = found + 1; - } while (found++); - return result; - } - MessageHelper.formatMessage = formatMessage; - function bbcode_chat(message) { - return messages.formatter.bbcode.format(message, { - is_chat_message: true - }); - } - MessageHelper.bbcode_chat = bbcode_chat; - let network; - (function (network) { - network.KB = 1024; - network.MB = 1024 * network.KB; - network.GB = 1024 * network.MB; - network.TB = 1024 * network.GB; - function format_bytes(value, options) { - options = options || {}; - if (typeof options.exact !== "boolean") - options.exact = true; - if (typeof options.unit !== "string") - options.unit = "Bytes"; - let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); - let v, unit; - if (value > 2 * network.TB) { - unit = "TB"; - v = value / network.TB; - } - else if (value > network.GB) { - unit = "GB"; - v = value / network.GB; - } - else if (value > network.MB) { - unit = "MB"; - v = value / network.MB; - } - else if (value > network.KB) { - unit = "KB"; - v = value / network.KB; - } - else { - unit = ""; - v = value; - } - let result = ""; - if (options.exact || !unit) { - result += points; - if (options.unit) { - result += " " + options.unit; - if (options.time) - result += "/" + options.time; - } - } - if (unit) { - result += (result ? " / " : "") + v.toFixed(2) + " " + unit; - if (options.time) - result += "/" + options.time; - } - return result; - } - network.format_bytes = format_bytes; - })(network = MessageHelper.network || (MessageHelper.network = {})); - MessageHelper.K = 1000; - MessageHelper.M = 1000 * MessageHelper.K; - MessageHelper.G = 1000 * MessageHelper.M; - MessageHelper.T = 1000 * MessageHelper.G; - function format_number(value, options) { - options = Object.assign(options || {}, {}); - let points = value.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); - let v, unit; - if (value > 2 * MessageHelper.T) { - unit = "T"; - v = value / MessageHelper.T; - } - else if (value > MessageHelper.G) { - unit = "G"; - v = value / MessageHelper.G; - } - else if (value > MessageHelper.M) { - unit = "M"; - v = value / MessageHelper.M; - } - else if (value > MessageHelper.K) { - unit = "K"; - v = value / MessageHelper.K; - } - else { - unit = ""; - v = value; - } - if (unit && options.time) - unit = unit + "/" + options.time; - return points + " " + (options.unit || "") + (unit ? (" / " + v.toFixed(2) + " " + unit) : ""); - } - MessageHelper.format_number = format_number; - MessageHelper.TIME_SECOND = 1000; - MessageHelper.TIME_MINUTE = 60 * MessageHelper.TIME_SECOND; - MessageHelper.TIME_HOUR = 60 * MessageHelper.TIME_MINUTE; - MessageHelper.TIME_DAY = 24 * MessageHelper.TIME_HOUR; - MessageHelper.TIME_WEEK = 7 * MessageHelper.TIME_DAY; - function format_time(time, default_value) { - let result = ""; - if (time > MessageHelper.TIME_WEEK) { - const amount = Math.floor(time / MessageHelper.TIME_WEEK); - result += " " + amount + " " + (amount > 1 ? tr("Weeks") : tr("Week")); - time -= amount * MessageHelper.TIME_WEEK; - } - if (time > MessageHelper.TIME_DAY) { - const amount = Math.floor(time / MessageHelper.TIME_DAY); - result += " " + amount + " " + (amount > 1 ? tr("Days") : tr("Day")); - time -= amount * MessageHelper.TIME_DAY; - } - if (time > MessageHelper.TIME_HOUR) { - const amount = Math.floor(time / MessageHelper.TIME_HOUR); - result += " " + amount + " " + (amount > 1 ? tr("Hours") : tr("Hour")); - time -= amount * MessageHelper.TIME_HOUR; - } - if (time > MessageHelper.TIME_MINUTE) { - const amount = Math.floor(time / MessageHelper.TIME_MINUTE); - result += " " + amount + " " + (amount > 1 ? tr("Minutes") : tr("Minute")); - time -= amount * MessageHelper.TIME_MINUTE; - } - if (time > MessageHelper.TIME_SECOND) { - const amount = Math.floor(time / MessageHelper.TIME_SECOND); - result += " " + amount + " " + (amount > 1 ? tr("Seconds") : tr("Second")); - time -= amount * MessageHelper.TIME_SECOND; - } - return result.length > 0 ? result.substring(1) : default_value; - } - MessageHelper.format_time = format_time; - let _icon_size_style; - function set_icon_size(size) { - if (!_icon_size_style) - _icon_size_style = $.spawn("style").appendTo($("#style")); - _icon_size_style.text("\n" + - ".message > .emoji {\n" + - " height: " + size + "!important;\n" + - " width: " + size + "!important;\n" + - "}\n"); - } - MessageHelper.set_icon_size = set_icon_size; - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: "icon size init", - function: () => __awaiter(this, void 0, void 0, function* () { - MessageHelper.set_icon_size((settings.static_global(Settings.KEY_ICON_SIZE) / 100).toFixed(2) + "em"); - }), - priority: 10 - }); -})(MessageHelper = exports.MessageHelper || (exports.MessageHelper = {})); - - -/***/ }), - -/***/ "./shared/js/ui/modal/ModalConnect.ts": -/*!********************************************!*\ - !*** ./shared/js/ui/modal/ModalConnect.ts ***! - \********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var connection_log; -(function (connection_log) { - let _history = []; - function log_connect(address) { - let entry = _history.find(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port); - if (!entry) { - _history.push(entry = { - last_timestamp: Date.now(), - first_timestamp: Date.now(), - address: address, - clients_online: 0, - clients_total: 0, - country: 'unknown', - name: 'Unknown', - icon_id: 0, - total_connection: 0, - flag_password: false, - password_hash: undefined - }); - } - entry.last_timestamp = Date.now(); - entry.total_connection++; - _save(); - } - connection_log.log_connect = log_connect; - function update_address_info(address, data) { - _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { - for (const key of Object.keys(data)) { - if (typeof (data[key]) !== "undefined") { - e[key] = data[key]; - } - } - }); - _save(); - } - connection_log.update_address_info = update_address_info; - function update_address_password(address, password_hash) { - _history.filter(e => e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port).forEach(e => { - e.password_hash = password_hash; - }); - _save(); - } - connection_log.update_address_password = update_address_password; - function _save() { - settings.changeGlobal(Settings.KEY_CONNECT_HISTORY, JSON.stringify(_history)); - } - function history() { - return _history.sort((a, b) => b.last_timestamp - a.last_timestamp); - } - connection_log.history = history; - function delete_entry(address) { - _history = _history.filter(e => !(e.address.hostname.toLowerCase() == address.hostname.toLowerCase() && e.address.port == address.port)); - _save(); - } - connection_log.delete_entry = delete_entry; - loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { - name: 'connection history load', - priority: 1, - function: () => __awaiter(this, void 0, void 0, function* () { - _history = []; - try { - _history = JSON.parse(settings.global(Settings.KEY_CONNECT_HISTORY, "[]")); - } - catch (error) { - log.warn(LogCategory.CLIENT, tr("Failed to load connection history: {}"), error); - } - }) - }); -})(connection_log = exports.connection_log || (exports.connection_log = {})); -var Modals; -(function (Modals) { - function spawnConnectModal(options, defaultHost = { url: "ts.TeaSpeak.de", enforce: false }, connect_profile) { - let selected_profile; - const random_id = (() => { - const array = new Uint32Array(10); - window.crypto.getRandomValues(array); - return array.join(""); - })(); - const modal = createModal({ - header: tr("Connect to a server"), - body: $("#tmpl_connect").renderTag({ - client: native_client, - forum_path: settings.static("forum_path"), - password_id: random_id, - multi_tab: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), - default_connect_new_tab: typeof (options.default_connect_new_tab) === "boolean" && options.default_connect_new_tab - }), - footer: () => undefined, - min_width: "28em" - }); - modal.htmlTag.find(".modal-body").addClass("modal-connect"); - { - const container_last_servers = modal.htmlTag.find(".container-last-servers"); - const button = modal.htmlTag.find(".button-toggle-last-servers"); - const set_show = shown => { - container_last_servers.toggleClass('shown', shown); - button.find(".arrow").toggleClass('down', shown).toggleClass('up', !shown); - settings.changeGlobal("connect_show_last_servers", shown); - }; - button.on('click', event => { - set_show(!container_last_servers.hasClass("shown")); - }); - set_show(settings.static_global("connect_show_last_servers", false)); - } - const apply = (header, body, footer) => { - const container_last_server_body = modal.htmlTag.find(".container-last-servers .table .body"); - const container_empty = container_last_server_body.find(".body-empty"); - let current_connect_data; - const button_connect = footer.find(".button-connect"); - const button_connect_tab = footer.find(".button-connect-new-tab"); - const button_manage = body.find(".button-manage-profiles"); - const input_profile = body.find(".container-select-profile select"); - const input_address = body.find(".container-address input"); - const input_nickname = body.find(".container-nickname input"); - const input_password = body.find(".container-password input"); - let updateFields = (reset_current_data) => { - if (reset_current_data) { - current_connect_data = undefined; - container_last_server_body.find(".selected").removeClass("selected"); - } - let address = input_address.val().toString(); - settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address); - let flag_address = !!address.match(Modals.Regex.IP_V4) || !!address.match(Modals.Regex.IP_V6) || !!address.match(Modals.Regex.DOMAIN); - let nickname = input_nickname.val().toString(); - if (nickname) - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, nickname); - else - nickname = input_nickname.attr("placeholder") || ""; - let flag_nickname = nickname.length >= 3 && nickname.length <= 32; - input_address.attr('pattern', flag_address ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_address); - input_nickname.attr('pattern', flag_nickname ? null : '^[a]{1000}$').toggleClass('is-invalid', !flag_nickname); - const flag_disabled = !flag_nickname || !flag_address || !selected_profile || !selected_profile.valid(); - button_connect.prop("disabled", flag_disabled); - button_connect_tab.prop("disabled", flag_disabled); - }; - input_address.val(defaultHost.enforce ? defaultHost.url : settings.static_global(Settings.KEY_CONNECT_ADDRESS, defaultHost.url)); - input_address - .on("keyup", () => updateFields(true)) - .on('keydown', event => { - if (event.keyCode == KeyCode.KEY_ENTER && !event.shiftKey) - button_connect.trigger('click'); - }); - button_manage.on('click', event => { - const modal = Modals.spawnSettingsModal("identity-profiles"); - modal.close_listener.push(() => { - input_profile.trigger('change'); - }); - return true; - }); - { - for (const profile of profiles.profiles()) { - input_profile.append($.spawn("option").text(profile.profile_name).val(profile.id)); - } - input_profile.on('change', event => { - selected_profile = profiles.find_profile(input_profile.val()) || profiles.default_profile(); - { - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined); - input_nickname - .attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user") - .val(""); - } - settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, selected_profile.id); - input_profile.toggleClass("is-invalid", !selected_profile || !selected_profile.valid()); - updateFields(true); - }); - input_profile.val(connect_profile && connect_profile.profile ? - connect_profile.profile.id : - settings.static_global(Settings.KEY_CONNECT_PROFILE, "default")).trigger('change'); - } - const last_nickname = settings.static_global(Settings.KEY_CONNECT_USERNAME, undefined); - if (last_nickname) - settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, last_nickname); - input_nickname.val(last_nickname); - input_nickname.on("keyup", () => updateFields(true)); - setTimeout(() => updateFields(false), 100); - const server_address = () => { - let address = input_address.val().toString(); - if (address.match(Modals.Regex.IP_V6) && !address.startsWith("[")) - return "[" + address + "]"; - return address; - }; - button_connect.on('click', event => { - modal.close(); - const connection = server_connections.active_connection_handler(); - if (connection) { - connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { - nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), - password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } - }); - } - else { - button_connect_tab.trigger('click'); - } - }); - button_connect_tab.on('click', event => { - modal.close(); - const connection = server_connections.spawn_server_connection_handler(); - server_connections.set_active_connection_handler(connection); - connection.startConnection(current_connect_data ? current_connect_data.address.hostname + ":" + current_connect_data.address.port : server_address(), selected_profile, true, { - nickname: input_nickname.val().toString() || input_nickname.attr("placeholder"), - password: (current_connect_data && current_connect_data.password_hash) ? { password: current_connect_data.password_hash, hashed: true } : { password: input_password.val().toString(), hashed: false } - }); - }); - { - for (const entry of connection_log.history().slice(0, 10)) { - $.spawn("div").addClass("row").append($.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { - event.preventDefault(); - const row = $(event.target).parents('.row'); - row.hide(250, () => { - row.detach(); - }); - connection_log.delete_entry(entry.address); - container_empty.toggle(container_last_server_body.children().length > 1); - })).append($.spawn("div").addClass("column name").append([ - IconManager.generate_tag(IconManager.load_cached_icon(entry.icon_id)), - $.spawn("a").text(entry.name) - ])).append($.spawn("div").addClass("column address").text(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : ""))).append($.spawn("div").addClass("column password").text(entry.flag_password ? tr("Yes") : tr("No"))).append($.spawn("div").addClass("column country-name").append([ - $.spawn("div").addClass("country flag-" + entry.country.toLowerCase()), - $.spawn("a").text(i18n.country_name(entry.country, tr("Global"))) - ])).append($.spawn("div").addClass("column clients").text(entry.clients_online + "/" + entry.clients_total)).append($.spawn("div").addClass("column connections").text(entry.total_connection + "")).on('click', event => { - if (event.isDefaultPrevented()) - return; - event.preventDefault(); - current_connect_data = entry; - container_last_server_body.find(".selected").removeClass("selected"); - $(event.target).parent('.row').addClass('selected'); - input_address.val(entry.address.hostname + (entry.address.port != 9987 ? (":" + entry.address.port) : "")); - input_password.val(entry.flag_password && entry.password_hash ? "WolverinDEV Yeahr!" : "").trigger('change'); - }).on('dblclick', event => { - current_connect_data = entry; - button_connect.trigger('click'); - }).appendTo(container_last_server_body); - container_empty.toggle(false); - } - } - }; - apply(modal.htmlTag, modal.htmlTag, modal.htmlTag); - modal.open(); - return; - } - Modals.spawnConnectModal = spawnConnectModal; - Modals.Regex = { - DOMAIN: /^(localhost|((([a-zA-Z0-9_-]{0,63}\.){0,253})?[a-zA-Z0-9_-]{0,63}\.[a-zA-Z]{2,64}))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,46}))$/, - IP_V4: /(^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(|:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[0-5]?[0-9]{1,4}))$/, - IP_V6: /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, - IP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, - }; -})(Modals = exports.Modals || (exports.Modals = {})); - - -/***/ }) - -/******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3Byb2Nlc3MvYnJvd3Nlci5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vYW1kLW9wdGlvbnMuanMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvQnJvd3NlcklQQy50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvUFBUTGlzdGVuZXIudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL2Nvbm5lY3Rpb24vQ29tbWFuZEhlbHBlci50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZS50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvY29ubmVjdGlvbi9TZXJ2ZXJDb25uZWN0aW9uRGVjbGFyYXRpb24udHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL2NyeXB0by9hc24xLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9jcnlwdG8vc2hhLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9jcnlwdG8vdWlkLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9pMThuL2xvY2FsaXplLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9tYWluLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9wcm9maWxlcy9Db25uZWN0aW9uUHJvZmlsZS50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvcHJvZmlsZXMvSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvTmFtZUlkZW50aXR5LnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy9wcm9maWxlcy9pZGVudGl0aWVzL1RlYUZvcnVtSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvVGVhbVNwZWFrSWRlbnRpdHkudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3Byb2ZpbGVzL2lkZW50aXRpZXMvdGVhc3BlYWstZm9ydW0udHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3NoYXJlZC9qcy91aS9lbGVtZW50cy9tb2RhbC50cyIsIndlYnBhY2s6Ly8vLi9zaGFyZWQvanMvdWkvZnJhbWVzL2NoYXQudHMiLCJ3ZWJwYWNrOi8vLy4vc2hhcmVkL2pzL3VpL21vZGFsL01vZGFsQ29ubmVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO1FBQUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7OztRQUdBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSwwQ0FBMEMsZ0NBQWdDO1FBQzFFO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0Esd0RBQXdELGtCQUFrQjtRQUMxRTtRQUNBLGlEQUFpRCxjQUFjO1FBQy9EOztRQUVBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQSx5Q0FBeUMsaUNBQWlDO1FBQzFFLGdIQUFnSCxtQkFBbUIsRUFBRTtRQUNySTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDJCQUEyQiwwQkFBMEIsRUFBRTtRQUN2RCxpQ0FBaUMsZUFBZTtRQUNoRDtRQUNBO1FBQ0E7O1FBRUE7UUFDQSxzREFBc0QsK0RBQStEOztRQUVySDtRQUNBOzs7UUFHQTtRQUNBOzs7Ozs7Ozs7Ozs7QUNsRkE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FBSUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHVCQUF1QixzQkFBc0I7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQkFBcUI7QUFDckI7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLHFDQUFxQzs7QUFFckM7QUFDQTtBQUNBOztBQUVBLDJCQUEyQjtBQUMzQjtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsVUFBVTs7Ozs7Ozs7Ozs7O0FDdkx0QztBQUNBOzs7Ozs7Ozs7Ozs7O0FDREE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQzs7QUFF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LEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUNwRSxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBRWxDLEtBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFO2dCQUNyQixDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztnQkFDcEUsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2FBQ3RDO1lBRUQsS0FBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDdkIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ3pCLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2FBQzdCO1lBRUQsT0FBTSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2xCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsVUFBVSxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNoRCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBRTFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ2hELENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFFMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNkLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsVUFBVSxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNoRCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7YUFDN0I7WUFFRCxPQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDbEIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNoQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDNUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFVBQVUsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEQsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2FBQzdCO1lBRUQsT0FBTSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2xCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDM0MsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMvQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBRTFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDZCxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7Z0JBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQy9DLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztnQkFFMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNkLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFNBQVMsR0FBRyxNQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDL0MsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUUxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2QsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUMvQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7YUFDN0I7WUFFRCxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMvQixDQUFDLENBQUM7UUFFRixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsR0FBRztZQUNqQixJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFFaEIsSUFBSSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUV6RSxPQUFPLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUM5RCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzFELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQztnQkFDbEQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDMUQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDO2dCQUNsRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMxRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUM7Z0JBQ2xELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDM0QsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzFELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQztnQkFDbEQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzNELFNBQVMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDO2dCQUMzRCxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDMUQsU0FBUyxDQUFDLENB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LEdBQUcsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzdCLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUMsSUFBTSxFQUFFLENBQUM7UUFDN0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFTLENBQUMsQ0FBQztRQUM3QixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDLElBQVcsQ0FBQyxDQUFDO1FBRzdCLE1BQU0sSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDdkU7SUFHRCxJQUFJLGFBQWEsSUFBSSxDQUFDLEVBQUU7UUFDcEIsS0FBSyxHQUFHLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUUzQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBR3ZCLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsSUFBTSxDQUFDLENBQUM7UUFFdkIsTUFBTSxJQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0tBQ2hEO1NBQU0sSUFBSSxhQUFhLElBQUksQ0FBQyxFQUFFO1FBQzNCLEtBQUssR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRTNELENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDMUIsQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFNLENBQUMsQ0FBQztRQUcxQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDLElBQVEsQ0FBQyxDQUFDO1FBRTFC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QUUsQ0FBQyxDQUFDLENBQUM7d0JBQzNHLGNBQWMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLGFBQWEsSUFBSSxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUNqSCxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxFQUFFLEtBQUssQ0FBQyxFQUFFO3dCQUN0QixvQkFBb0IsR0FBRyxLQUFLLENBQUM7d0JBQzdCLGNBQWMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3BDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO29CQUN4QyxlQUFlLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO2lCQUNqQzthQUNKO1FBQ0wsQ0FBQyxDQUFDO1FBQ0YsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFbkQsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2IsT0FBTztJQUNYLENBQUM7SUFsT2Usd0JBQWlCLG9CQWtPaEM7SUFFWSxZQUFLLEdBQUc7UUFFakIsTUFBTSxFQUFFLG9LQUFvSztRQUU1SyxLQUFLLEVBQUUsc0tBQXNLO1FBQzdLLEtBQUssRUFBRSxxcEJBQXFwQjtRQUM1cEIsRUFBRSxFQUFFLG13Q0FBbXdDO0tBQzF3QyxDQUFDO0FBQ04sQ0FBQyxFQTdPZ0IsTUFBTSxHQUFOLGNBQU0sS0FBTixjQUFNLFFBNk90QiIsImZpbGUiOiJidW5kbGUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3NoYXJlZC9qcy9tYWluLnRzXCIpO1xuIiwiLy8gc2hpbSBmb3IgdXNpbmcgcHJvY2VzcyBpbiBicm93c2VyXG52YXIgcHJvY2VzcyA9IG1vZHVsZS5leHBvcnRzID0ge307XG5cbi8vIGNhY2hlZCBmcm9tIHdoYXRldmVyIGdsb2JhbCBpcyBwcmVzZW50IHNvIHRoYXQgdGVzdCBydW5uZXJzIHRoYXQgc3R1YiBpdFxuLy8gZG9uJ3QgYnJlYWsgdGhpbmdzLiAgQnV0IHdlIG5lZWQgdG8gd3JhcCBpdCBpbiBhIHRyeSBjYXRjaCBpbiBjYXNlIGl0IGlzXG4vLyB3cmFwcGVkIGluIHN0cmljdCBtb2RlIGNvZGUgd2hpY2ggZG9lc24ndCBkZWZpbmUgYW55IGdsb2JhbHMuICBJdCdzIGluc2lkZSBhXG4vLyBmdW5jdGlvbiBiZWNhdXNlIHRyeS9jYXRjaGVzIGRlb3B0aW1pemUgaW4gY2VydGFpbiBlbmdpbmVzLlxuXG52YXIgY2FjaGVkU2V0VGltZW91dDtcbnZhciBjYWNoZWRDbGVhclRpbWVvdXQ7XG5cbmZ1bmN0aW9uIGRlZmF1bHRTZXRUaW1vdXQoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdzZXRUaW1lb3V0IGhhcyBub3QgYmVlbiBkZWZpbmVkJyk7XG59XG5mdW5jdGlvbiBkZWZhdWx0Q2xlYXJUaW1lb3V0ICgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NsZWFyVGltZW91dCBoYXMgbm90IGJlZW4gZGVmaW5lZCcpO1xufVxuKGZ1bmN0aW9uICgpIHtcbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIHNldFRpbWVvdXQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBzZXRUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IGRlZmF1bHRTZXRUaW1vdXQ7XG4gICAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGNhY2hlZFNldFRpbWVvdXQgPSBkZWZhdWx0U2V0VGltb3V0O1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICBpZiAodHlwZW9mIGNsZWFyVGltZW91dCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gY2xlYXJUaW1lb3V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgY2FjaGVkQ2xlYXJUaW1lb3V0ID0gZGVmYXVsdENsZWFyVGltZW91dDtcbiAgICB9XG59ICgpKVxuZnVuY3Rpb24gcnVuVGltZW91dChmdW4pIHtcbiAgICBpZiAoY2FjaGVkU2V0VGltZW91dCA9PT0gc2V0VGltZW91dCkge1xuICAgICAgICAvL25vcm1hbCBlbnZpcm9tZW50cyBpbiBzYW5lIHNpdHVhdGlvbnNcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9XG4gICAgLy8gaWYgc2V0VGltZW91dCB3YXNuJ3QgYXZhaWxhYmxlIGJ1dCB3YXMgbGF0dGVyIGRlZmluZWRcbiAgICBpZiAoKGNhY2hlZFNldFRpbWVvdXQgPT09IGRlZmF1bHRTZXRUaW1vdXQgfHwgIWNhY2hlZFNldFRpbWVvdXQpICYmIHNldFRpbWVvdXQpIHtcbiAgICAgICAgY2FjaGVkU2V0VGltZW91dCA9IHNldFRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBzZXRUaW1lb3V0KGZ1biwgMCk7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIC8vIHdoZW4gd2hlbiBzb21lYm9keSBoYXMgc2NyZXdlZCB3aXRoIHNldFRpbWVvdXQgYnV0IG5vIEkuRS4gbWFkZG5lc3NcbiAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQoZnVuLCAwKTtcbiAgICB9IGNhdGNoKGUpe1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gV2hlbiB3ZSBhcmUgaW4gSS5FLiBidXQgdGhlIHNjcmlwdCBoYXMgYmVlbiBldmFsZWQgc28gSS5FLiBkb2Vzbid0IHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkU2V0VGltZW91dC5jYWxsKG51bGwsIGZ1biwgMCk7XG4gICAgICAgIH0gY2F0Y2goZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvclxuICAgICAgICAgICAgcmV0dXJuIGNhY2hlZFNldFRpbWVvdXQuY2FsbCh0aGlzLCBmdW4sIDApO1xuICAgICAgICB9XG4gICAgfVxuXG5cbn1cbmZ1bmN0aW9uIHJ1bkNsZWFyVGltZW91dChtYXJrZXIpIHtcbiAgICBpZiAoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBjbGVhclRpbWVvdXQpIHtcbiAgICAgICAgLy9ub3JtYWwgZW52aXJvbWVudHMgaW4gc2FuZSBzaXR1YXRpb25zXG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgLy8gaWYgY2xlYXJUaW1lb3V0IHdhc24ndCBhdmFpbGFibGUgYnV0IHdhcyBsYXR0ZXIgZGVmaW5lZFxuICAgIGlmICgoY2FjaGVkQ2xlYXJUaW1lb3V0ID09PSBkZWZhdWx0Q2xlYXJUaW1lb3V0IHx8ICFjYWNoZWRDbGVhclRpbWVvdXQpICYmIGNsZWFyVGltZW91dCkge1xuICAgICAgICBjYWNoZWRDbGVhclRpbWVvdXQgPSBjbGVhclRpbWVvdXQ7XG4gICAgICAgIHJldHVybiBjbGVhclRpbWVvdXQobWFya2VyKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgICAgLy8gd2hlbiB3aGVuIHNvbWVib2R5IGhhcyBzY3Jld2VkIHdpdGggc2V0VGltZW91dCBidXQgbm8gSS5FLiBtYWRkbmVzc1xuICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0KG1hcmtlcik7XG4gICAgfSBjYXRjaCAoZSl7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBXaGVuIHdlIGFyZSBpbiBJLkUuIGJ1dCB0aGUgc2NyaXB0IGhhcyBiZWVuIGV2YWxlZCBzbyBJLkUuIGRvZXNuJ3QgIHRydXN0IHRoZSBnbG9iYWwgb2JqZWN0IHdoZW4gY2FsbGVkIG5vcm1hbGx5XG4gICAgICAgICAgICByZXR1cm4gY2FjaGVkQ2xlYXJUaW1lb3V0LmNhbGwobnVsbCwgbWFya2VyKTtcbiAgICAgICAgfSBjYXRjaCAoZSl7XG4gICAgICAgICAgICAvLyBzYW1lIGFzIGFib3ZlIGJ1dCB3aGVuIGl0J3MgYSB2ZXJzaW9uIG9mIEkuRS4gdGhhdCBtdXN0IGhhdmUgdGhlIGdsb2JhbCBvYmplY3QgZm9yICd0aGlzJywgaG9wZnVsbHkgb3VyIGNvbnRleHQgY29ycmVjdCBvdGhlcndpc2UgaXQgd2lsbCB0aHJvdyBhIGdsb2JhbCBlcnJvci5cbiAgICAgICAgICAgIC8vIFNvbWUgdmVyc2lvbnMgb2YgSS5FLiBoYXZlIGRpZmZlcmVudCBydWxlcyBmb3IgY2xlYXJUaW1lb3V0IHZzIHNldFRpbWVvdXRcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRDbGVhclRpbWVvdXQuY2FsbCh0aGlzLCBtYXJrZXIpO1xuICAgICAgICB9XG4gICAgfVxuXG5cblxufVxudmFyIHF1ZXVlID0gW107XG52YXIgZHJhaW5pbmcgPSBmYWxzZTtcbnZhciBjdXJyZW50UXVldWU7XG52YXIgcXVldWVJbmRleCA9IC0xO1xuXG5mdW5jdGlvbiBjbGVhblVwTmV4dFRpY2soKSB7XG4gICAgaWYgKCFkcmFpbmluZyB8fCAhY3VycmVudFF1ZXVlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZHJhaW5pbmcgPSBmYWxzZTtcbiAgICBpZiAoY3VycmVudFF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBxdWV1ZSA9IGN1cnJlbnRRdWV1ZS5jb25jYXQocXVldWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICB9XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCkge1xuICAgICAgICBkcmFpblF1ZXVlKCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBkcmFpblF1ZXVlKCkge1xuICAgIGlmIChkcmFpbmluZykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIHZhciB0aW1lb3V0ID0gcnVuVGltZW91dChjbGVhblVwTmV4dFRpY2spO1xuICAgIGRyYWluaW5nID0gdHJ1ZTtcblxuICAgIHZhciBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgd2hpbGUobGVuKSB7XG4gICAgICAgIGN1cnJlbnRRdWV1ZSA9IHF1ZXVlO1xuICAgICAgICBxdWV1ZSA9IFtdO1xuICAgICAgICB3aGlsZSAoKytxdWV1ZUluZGV4IDwgbGVuKSB7XG4gICAgICAgICAgICBpZiAoY3VycmVudFF1ZXVlKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudFF1ZXVlW3F1ZXVlSW5kZXhdLnJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHF1ZXVlSW5kZXggPSAtMTtcbiAgICAgICAgbGVuID0gcXVldWUubGVuZ3RoO1xuICAgIH1cbiAgICBjdXJyZW50UXVldWUgPSBudWxsO1xuICAgIGRyYWluaW5nID0gZmFsc2U7XG4gICAgcnVuQ2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xufVxuXG5wcm9jZXNzLm5leHRUaWNrID0gZnVuY3Rpb24gKGZ1bikge1xuICAgIHZhciBhcmdzID0gbmV3IEFycmF5KGFyZ3VtZW50cy5sZW5ndGggLSAxKTtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGFyZ3NbaSAtIDFdID0gYXJndW1lbnRzW2ldO1xuICAgICAgICB9XG4gICAgfVxuICAgIHF1ZXVlLnB1c2gobmV3IEl0ZW0oZnVuLCBhcmdzKSk7XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCA9PT0gMSAmJiAhZHJhaW5pbmcpIHtcbiAgICAgICAgcnVuVGltZW91dChkcmFpblF1ZXVlKTtcbiAgICB9XG59O1xuXG4vLyB2OCBsaWtlcyBwcmVkaWN0aWJsZSBvYmplY3RzXG5mdW5jdGlvbiBJdGVtKGZ1biwgYXJyYXkpIHtcbiAgICB0aGlzLmZ1biA9IGZ1bjtcbiAgICB0aGlzLmFycmF5ID0gYXJyYXk7XG59XG5JdGVtLnByb3RvdHlwZS5ydW4gPSBmdW5jdGlvbiAoKSB7XG4gICAgdGhpcy5mdW4uYXBwbHkobnVsbCwgdGhpcy5hcnJheSk7XG59O1xucHJvY2Vzcy50aXRsZSA9ICdicm93c2VyJztcbnByb2Nlc3MuYnJvd3NlciA9IHRydWU7XG5wcm9jZXNzLmVudiA9IHt9O1xucHJvY2Vzcy5hcmd2ID0gW107XG5wcm9jZXNzLnZlcnNpb24gPSAnJzsgLy8gZW1wdHkgc3RyaW5nIHRvIGF2b2lkIHJlZ2V4cCBpc3N1ZXNcbnByb2Nlc3MudmVyc2lvbnMgPSB7fTtcblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbnByb2Nlc3Mub24gPSBub29wO1xucHJvY2Vzcy5hZGRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLm9uY2UgPSBub29wO1xucHJvY2Vzcy5vZmYgPSBub29wO1xucHJvY2Vzcy5yZW1vdmVMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUFsbExpc3RlbmVycyA9IG5vb3A7XG5wcm9jZXNzLmVtaXQgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kTGlzdGVuZXIgPSBub29wO1xucHJvY2Vzcy5wcmVwZW5kT25jZUxpc3RlbmVyID0gbm9vcDtcblxucHJvY2Vzcy5saXN0ZW5lcnMgPSBmdW5jdGlvbiAobmFtZSkgeyByZXR1cm4gW10gfVxuXG5wcm9jZXNzLmJpbmRpbmcgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5iaW5kaW5nIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5cbnByb2Nlc3MuY3dkID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gJy8nIH07XG5wcm9jZXNzLmNoZGlyID0gZnVuY3Rpb24gKGRpcikge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5jaGRpciBpcyBub3Qgc3VwcG9ydGVkJyk7XG59O1xucHJvY2Vzcy51bWFzayA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gMDsgfTtcbiIsIi8qIGdsb2JhbHMgX193ZWJwYWNrX2FtZF9vcHRpb25zX18gKi9cbm1vZHVsZS5leHBvcnRzID0gX193ZWJwYWNrX2FtZF9vcHRpb25zX187XG4iLCJ2YXIgZztcblxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcbmcgPSAoZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0aGlzO1xufSkoKTtcblxudHJ5IHtcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXG5cdGcgPSBnIHx8IG5ldyBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCk7XG59IGNhdGNoIChlKSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xufVxuXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGc7XG4iLCJleHBvcnQgaW50ZXJmYWNlIFdpbmRvdyB7XHJcbiAgICBCcm9hZGNhc3RDaGFubmVsOiBCcm9hZGNhc3RDaGFubmVsO1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIGJpcGMge1xyXG4gICAgZXhwb3J0IGludGVyZmFjZSBCcm9hZGNhc3RNZXNzYWdlIHtcclxuICAgICAgICB0aW1lc3RhbXA6IG51bWJlcjtcclxuICAgICAgICByZWNlaXZlcjogc3RyaW5nO1xyXG4gICAgICAgIHNlbmRlcjogc3RyaW5nO1xyXG5cclxuICAgICAgICB0eXBlOiBzdHJpbmc7XHJcbiAgICAgICAgZGF0YTogYW55O1xyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIHV1aWR2NCgpIHtcclxuICAgICAgICByZXR1cm4gJ3h4eHh4eHh4LXh4eHgtNHh4eC15eHh4LXh4eHh4eHh4eHh4eCcucmVwbGFjZSgvW3h5XS9nLCBmdW5jdGlvbihjKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHIgPSBNYXRoLnJhbmRvbSgpICogMTYgfCAwLCB2ID0gYyA9PSAneCcgPyByIDogKHIgJiAweDMgfCAweDgpO1xyXG4gICAgICAgICAgICByZXR1cm4gdi50b1N0cmluZygxNik7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgaW50ZXJmYWNlIFByb2Nlc3NRdWVyeSB7XHJcbiAgICAgICAgdGltZXN0YW1wOiBudW1iZXJcclxuICAgICAgICBxdWVyeV9pZDogc3RyaW5nO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ2hhbm5lbE1lc3NhZ2Uge1xyXG4gICAgICAgIGNoYW5uZWxfaWQ6IHN0cmluZztcclxuICAgICAgICB0eXBlOiBzdHJpbmc7XHJcbiAgICAgICAgZGF0YTogYW55O1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgUHJvY2Vzc1F1ZXJ5UmVzcG9uc2Uge1xyXG4gICAgICAgIHJlcXVlc3RfdGltZXN0YW1wOiBudW1iZXJcclxuICAgICAgICByZXF1ZXN0X3F1ZXJ5X2lkOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIGRldmljZV9pZDogc3RyaW5nO1xyXG4gICAgICAgIHByb3RvY29sOiBudW1iZXI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBDZXJ0aWZpY2F0ZUFjY2VwdENhbGxiYWNrIHtcclxuICAgICAgICByZXF1ZXN0X2lkOiBzdHJpbmc7XHJcbiAgICB9XHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIENlcnRpZmljYXRlQWNjZXB0U3VjY2VlZGVkIHsgfVxyXG5cclxuICAgIGV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNpY0lQQ0hhbmRsZXIge1xyXG4gICAgICAgIHByb3RlY3RlZCBzdGF0aWMgcmVhZG9ubHkgQlJPQURDQVNUX1VOSVFVRV9JRCA9IFwiMDAwMDAwMDAtMDAwMC00MDAwLTAwMDAtMDAwMDAwMDAwMDAwXCI7XHJcbiAgICAgICAgcHJvdGVjdGVkIHN0YXRpYyByZWFkb25seSBQUk9UT0NPTF9WRVJTSU9OID0gMTtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIF9jaGFubmVsczogQ2hhbm5lbFtdID0gW107XHJcbiAgICAgICAgcHJvdGVjdGVkIHVuaXF1ZV9pZDtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKCkgeyB9XHJcblxyXG4gICAgICAgIHNldHVwKCkge1xyXG4gICAgICAgICAgICB0aGlzLnVuaXF1ZV9pZCA9IHV1aWR2NCgpOyAvKiBsZXRzIGdldCBhbiB1bmlxdWUgaWRlbnRpZmllciAqL1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZ2V0X2xvY2FsX2FkZHJlc3MoKSB7IHJldHVybiB0aGlzLnVuaXF1ZV9pZDsgfVxyXG5cclxuICAgICAgICBhYnN0cmFjdCBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBkYXRhOiBhbnksIHRhcmdldD86IHN0cmluZyk7XHJcblxyXG4gICAgICAgIHByb3RlY3RlZCBoYW5kbGVfbWVzc2FnZShtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgIC8vbG9nLnRyYWNlKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBtZXNzYWdlICVvXCIpLCBtZXNzYWdlKTtcclxuXHJcbiAgICAgICAgICAgIGlmKG1lc3NhZ2UucmVjZWl2ZXIgPT09IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lEKSB7XHJcbiAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJwcm9jZXNzLXF1ZXJ5XCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGEgZGV2aWNlIHF1ZXJ5IGZyb20gJXMuXCIpLCBtZXNzYWdlLnNlbmRlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZW5kX21lc3NhZ2UoXCJwcm9jZXNzLXF1ZXJ5LXJlc3BvbnNlXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9xdWVyeV9pZDogKDxQcm9jZXNzUXVlcnk+bWVzc2FnZS5kYXRhKS5xdWVyeV9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF90aW1lc3RhbXA6ICg8UHJvY2Vzc1F1ZXJ5Pm1lc3NhZ2UuZGF0YSkudGltZXN0YW1wLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgZGV2aWNlX2lkOiB0aGlzLnVuaXF1ZV9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2w6IEJhc2ljSVBDSGFuZGxlci5QUk9UT0NPTF9WRVJTSU9OXHJcbiAgICAgICAgICAgICAgICAgICAgfSBhcyBQcm9jZXNzUXVlcnlSZXNwb25zZSwgbWVzc2FnZS5zZW5kZXIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSBlbHNlIGlmKG1lc3NhZ2UucmVjZWl2ZXIgPT09IHRoaXMudW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJwcm9jZXNzLXF1ZXJ5LXJlc3BvbnNlXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZTogUHJvY2Vzc1F1ZXJ5UmVzcG9uc2UgPSBtZXNzYWdlLmRhdGE7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5fcXVlcnlfcmVzdWx0c1tyZXNwb25zZS5yZXF1ZXN0X3F1ZXJ5X2lkXSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcXVlcnlfcmVzdWx0c1tyZXNwb25zZS5yZXF1ZXN0X3F1ZXJ5X2lkXS5wdXNoKHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGEgcXVlcnkgcmVzcG9uc2UgZm9yIGFuIHVua25vd24gcmVxdWVzdC5cIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImNlcnRpZmljYXRlLWFjY2VwdC1jYWxsYmFja1wiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YTogQ2VydGlmaWNhdGVBY2NlcHRDYWxsYmFjayA9IG1lc3NhZ2UuZGF0YTtcclxuICAgICAgICAgICAgICAgICAgICBpZighdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF0pIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGNlcnRpZmljYXRlIGFjY2VwdCBjYWxsYmFjayBmb3IgYW4gdW5rbm93biByZXF1ZXN0IElELlwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF0oKTtcclxuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fY2VydF9hY2NlcHRfY2FsbGJhY2tzW2RhdGEucmVxdWVzdF9pZF07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwiY2VydGlmaWNhdGUtYWNjZXB0LXN1Y2NlZWRlZFwiLCB7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIH0gYXMgQ2VydGlmaWNhdGVBY2NlcHRTdWNjZWVkZWQsIG1lc3NhZ2Uuc2VuZGVyKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImNlcnRpZmljYXRlLWFjY2VwdC1zdWNjZWVkZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9jZXJ0X2FjY2VwdF9zdWNjZWVkZWRbbWVzc2FnZS5zZW5kZXJdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjZXJ0aWZpY2F0ZSBhY2NlcHQgc3VjY2VlZGVkLCBidXQgaGF2ZW4ndCBhIGNhbGxiYWNrLlwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2VydF9hY2NlcHRfc3VjY2VlZGVkW21lc3NhZ2Uuc2VuZGVyXSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT09IFwiY2hhbm5lbFwiKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBkYXRhOiBDaGFubmVsTWVzc2FnZSA9IG1lc3NhZ2UuZGF0YTtcclxuXHJcbiAgICAgICAgICAgICAgICBsZXQgY2hhbm5lbF9pbnZva2VkID0gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICBmb3IoY29uc3QgY2hhbm5lbCBvZiB0aGlzLl9jaGFubmVscylcclxuICAgICAgICAgICAgICAgICAgICBpZihjaGFubmVsLmNoYW5uZWxfaWQgPT09IGRhdGEuY2hhbm5lbF9pZCAmJiAodHlwZW9mKGNoYW5uZWwudGFyZ2V0X2lkKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCBjaGFubmVsLnRhcmdldF9pZCA9PT0gbWVzc2FnZS5zZW5kZXIpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGNoYW5uZWwubWVzc2FnZV9oYW5kbGVyKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIobWVzc2FnZS5zZW5kZXIsIG1lc3NhZ2UucmVjZWl2ZXIgPT09IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lELCBkYXRhKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY2hhbm5lbF9pbnZva2VkID0gdHJ1ZTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZighY2hhbm5lbF9pbnZva2VkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiUmVjZWl2ZWQgY2hhbm5lbCBtZXNzYWdlIGZvciB1bmtub3duIGNoYW5uZWwgKCVzKVwiKSwgZGF0YS5jaGFubmVsX2lkKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY3JlYXRlX2NoYW5uZWwodGFyZ2V0X2lkPzogc3RyaW5nLCBjaGFubmVsX2lkPzogc3RyaW5nKSB7XHJcbiAgICAgICAgICAgIGxldCBjaGFubmVsOiBDaGFubmVsID0ge1xyXG4gICAgICAgICAgICAgICAgdGFyZ2V0X2lkOiB0YXJnZXRfaWQsXHJcbiAgICAgICAgICAgICAgICBjaGFubmVsX2lkOiBjaGFubmVsX2lkIHx8IHV1aWR2NCgpLFxyXG4gICAgICAgICAgICAgICAgbWVzc2FnZV9oYW5kbGVyOiB1bmRlZmluZWQsXHJcbiAgICAgICAgICAgICAgICBzZW5kX21lc3NhZ2U6ICh0eXBlOiBzdHJpbmcsIGRhdGE6IGFueSwgdGFyZ2V0Pzogc3RyaW5nKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodHlwZW9mIHRhcmdldCAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZih0eXBlb2YgY2hhbm5lbC50YXJnZXRfaWQgPT09IFwic3RyaW5nXCIgJiYgdGFyZ2V0ICE9IGNoYW5uZWwudGFyZ2V0X2lkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJ0YXJnZXQgaWQgZG9lcyBub3QgbWF0Y2ggY2hhbm5lbCB0YXJnZXRcIjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwiY2hhbm5lbFwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IHR5cGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IGRhdGEsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNoYW5uZWxfaWQ6IGNoYW5uZWwuY2hhbm5lbF9pZFxyXG4gICAgICAgICAgICAgICAgICAgIH0gYXMgQ2hhbm5lbE1lc3NhZ2UsIHRhcmdldCB8fCBjaGFubmVsLnRhcmdldF9pZCB8fCBCYXNpY0lQQ0hhbmRsZXIuQlJPQURDQVNUX1VOSVFVRV9JRCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICB0aGlzLl9jaGFubmVscy5wdXNoKGNoYW5uZWwpO1xyXG4gICAgICAgICAgICByZXR1cm4gY2hhbm5lbDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNoYW5uZWxzKCkgOiBDaGFubmVsW10geyByZXR1cm4gdGhpcy5fY2hhbm5lbHM7IH1cclxuXHJcbiAgICAgICAgZGVsZXRlX2NoYW5uZWwoY2hhbm5lbDogQ2hhbm5lbCkge1xyXG4gICAgICAgICAgICB0aGlzLl9jaGFubmVscyA9IHRoaXMuX2NoYW5uZWxzLmZpbHRlcihlID0+IGUgIT09IGNoYW5uZWwpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcHJpdmF0ZSBfcXVlcnlfcmVzdWx0czoge1trZXk6IHN0cmluZ106UHJvY2Vzc1F1ZXJ5UmVzcG9uc2VbXX0gPSB7fTtcclxuICAgICAgICBhc3luYyBxdWVyeV9wcm9jZXNzZXModGltZW91dD86IG51bWJlcikgOiBQcm9taXNlPFByb2Nlc3NRdWVyeVJlc3BvbnNlW10+IHtcclxuICAgICAgICAgICAgY29uc3QgcXVlcnlfaWQgPSB1dWlkdjQoKTtcclxuICAgICAgICAgICAgdGhpcy5fcXVlcnlfcmVzdWx0c1txdWVyeV9pZF0gPSBbXTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuc2VuZF9tZXNzYWdlKFwicHJvY2Vzcy1xdWVyeVwiLCB7XHJcbiAgICAgICAgICAgICAgICBxdWVyeV9pZDogcXVlcnlfaWQsXHJcbiAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KClcclxuICAgICAgICAgICAgfSBhcyBQcm9jZXNzUXVlcnkpO1xyXG5cclxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIHRpbWVvdXQgfHwgMjUwKSk7XHJcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuX3F1ZXJ5X3Jlc3VsdHNbcXVlcnlfaWRdO1xyXG4gICAgICAgICAgICBkZWxldGUgdGhpcy5fcXVlcnlfcmVzdWx0c1txdWVyeV9pZF07XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBwcml2YXRlIF9jZXJ0X2FjY2VwdF9jYWxsYmFja3M6IHtba2V5OiBzdHJpbmddOigoKSA9PiBhbnkpfSA9IHt9O1xyXG4gICAgICAgIHJlZ2lzdGVyX2NlcnRpZmljYXRlX2FjY2VwdF9jYWxsYmFjayhjYWxsYmFjazogKCkgPT4gYW55KSA6IHN0cmluZyB7XHJcbiAgICAgICAgICAgIGNvbnN0IGlkID0gdXVpZHY0KCk7XHJcbiAgICAgICAgICAgIHRoaXMuX2NlcnRfYWNjZXB0X2NhbGxiYWNrc1tpZF0gPSBjYWxsYmFjaztcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudW5pcXVlX2lkICsgXCI6XCIgKyBpZDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZDoge1tzZW5kZXI6IHN0cmluZ106KCgpID0+IGFueSl9ID0ge307XHJcbiAgICAgICAgcG9zdF9jZXJ0aWZpY2F0ZV9hY2NwZWN0ZWQoaWQ6IHN0cmluZywgdGltZW91dD86IG51bWJlcikgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBpZC5zcGxpdChcIjpcIik7XHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZFtkYXRhWzBdXTtcclxuICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwidGltZW91dFwiKTtcclxuICAgICAgICAgICAgICAgIH0sIHRpbWVvdXQgfHwgMjUwKTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2NlcnRfYWNjZXB0X3N1Y2NlZWRlZFtkYXRhWzBdXSA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fY2VydF9hY2NlcHRfc3VjY2VlZGVkW2RhdGFbMF1dO1xyXG4gICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgdGhpcy5zZW5kX21lc3NhZ2UoXCJjZXJ0aWZpY2F0ZS1hY2NlcHQtY2FsbGJhY2tcIiwge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlcXVlc3RfaWQ6IGRhdGFbMV1cclxuICAgICAgICAgICAgICAgIH0gYXMgQ2VydGlmaWNhdGVBY2NlcHRDYWxsYmFjaywgZGF0YVswXSk7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ2hhbm5lbCB7XHJcbiAgICAgICAgcmVhZG9ubHkgY2hhbm5lbF9pZDogc3RyaW5nO1xyXG4gICAgICAgIHRhcmdldF9pZD86IHN0cmluZztcclxuXHJcbiAgICAgICAgbWVzc2FnZV9oYW5kbGVyOiAocmVtb3RlX2lkOiBzdHJpbmcsIGJyb2FkY2FzdDogYm9vbGVhbiwgbWVzc2FnZTogQ2hhbm5lbE1lc3NhZ2UpID0+IGFueTtcclxuICAgICAgICBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBtZXNzYWdlOiBhbnksIHRhcmdldD86IHN0cmluZyk7XHJcbiAgICB9XHJcblxyXG4gICAgY2xhc3MgQnJvYWRjYXN0Q2hhbm5lbElQQyBleHRlbmRzIEJhc2ljSVBDSGFuZGxlciB7XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ0hBTk5FTF9OQU1FID0gXCJUZWFTcGVhay1XZWJcIjtcclxuXHJcbiAgICAgICAgcHJpdmF0ZSBjaGFubmVsOiBCcm9hZGNhc3RDaGFubmVsO1xyXG5cclxuICAgICAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHNldHVwKCkge1xyXG4gICAgICAgICAgICBzdXBlci5zZXR1cCgpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jaGFubmVsID0gbmV3IEJyb2FkY2FzdENoYW5uZWwoQnJvYWRjYXN0Q2hhbm5lbElQQy5DSEFOTkVMX05BTUUpO1xyXG4gICAgICAgICAgICB0aGlzLmNoYW5uZWwub25tZXNzYWdlID0gdGhpcy5vbl9tZXNzYWdlLmJpbmQodGhpcyk7XHJcbiAgICAgICAgICAgIHRoaXMuY2hhbm5lbC5vbm1lc3NhZ2VlcnJvciA9IHRoaXMub25fZXJyb3IuYmluZCh0aGlzKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgb25fbWVzc2FnZShldmVudDogTWVzc2FnZUV2ZW50KSB7XHJcbiAgICAgICAgICAgIGlmKHR5cGVvZihldmVudC5kYXRhKSAhPT0gXCJzdHJpbmdcIikge1xyXG4gICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIG1lc3NhZ2Ugd2l0aCBhbiBpbnZhbGlkIHR5cGUgKCVzKTogJW9cIiksIHR5cGVvZihldmVudC5kYXRhKSwgZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGxldCBtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlO1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgbWVzc2FnZSA9IEpTT04ucGFyc2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgYW4gaW52YWxpZCBlbmNvZGVkIG1lc3NhZ2U6ICVvXCIpLCBldmVudC5kYXRhKTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzdXBlci5oYW5kbGVfbWVzc2FnZShtZXNzYWdlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHByaXZhdGUgb25fZXJyb3IoZXZlbnQ6IE1lc3NhZ2VFdmVudCkge1xyXG4gICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgZXJyb3I6ICVvXCIpLCBldmVudCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzZW5kX21lc3NhZ2UodHlwZTogc3RyaW5nLCBkYXRhOiBhbnksIHRhcmdldD86IHN0cmluZykge1xyXG4gICAgICAgICAgICBjb25zdCBtZXNzYWdlOiBCcm9hZGNhc3RNZXNzYWdlID0ge30gYXMgYW55O1xyXG5cclxuICAgICAgICAgICAgbWVzc2FnZS5zZW5kZXIgPSB0aGlzLnVuaXF1ZV9pZDtcclxuICAgICAgICAgICAgbWVzc2FnZS5yZWNlaXZlciA9IHRhcmdldCA/IHRhcmdldCA6IEJhc2ljSVBDSGFuZGxlci5CUk9BRENBU1RfVU5JUVVFX0lEO1xyXG4gICAgICAgICAgICBtZXNzYWdlLnRpbWVzdGFtcCA9IERhdGUubm93KCk7XHJcbiAgICAgICAgICAgIG1lc3NhZ2UudHlwZSA9IHR5cGU7XHJcbiAgICAgICAgICAgIG1lc3NhZ2UuZGF0YSA9IGRhdGE7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNoYW5uZWwucG9zdE1lc3NhZ2UoSlNPTi5zdHJpbmdpZnkobWVzc2FnZSkpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgbmFtZXNwYWNlIGNvbm5lY3Qge1xyXG4gICAgICAgIGV4cG9ydCB0eXBlIENvbm5lY3RSZXF1ZXN0RGF0YSA9IHtcclxuICAgICAgICAgICAgYWRkcmVzczogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgcHJvZmlsZT86IHN0cmluZztcclxuICAgICAgICAgICAgdXNlcm5hbWU/OiBzdHJpbmc7XHJcbiAgICAgICAgICAgIHBhc3N3b3JkPzoge1xyXG4gICAgICAgICAgICAgICAgdmFsdWU6IHN0cmluZztcclxuICAgICAgICAgICAgICAgIGhhc2hlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgQ29ubmVjdE9mZmVyIHtcclxuICAgICAgICAgICAgcmVxdWVzdF9pZDogc3RyaW5nO1xyXG4gICAgICAgICAgICBkYXRhOiBDb25uZWN0UmVxdWVzdERhdGE7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgaW50ZXJmYWNlIENvbm5lY3RPZmZlckFuc3dlciB7XHJcbiAgICAgICAgICAgIHJlcXVlc3RfaWQ6IHN0cmluZztcclxuICAgICAgICAgICAgYWNjZXB0ZWQ6IGJvb2xlYW47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgaW50ZXJmYWNlIENvbm5lY3RFeGVjdXRlIHtcclxuICAgICAgICAgICAgcmVxdWVzdF9pZDogc3RyaW5nO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBDb25uZWN0RXhlY3V0ZWQge1xyXG4gICAgICAgICAgICByZXF1ZXN0X2lkOiBzdHJpbmc7XHJcbiAgICAgICAgICAgIHN1Y2NlZWRlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgbWVzc2FnZT86IHN0cmluZztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8qIFRoZSBjb25uZWN0IHByb2Nlc3M6XHJcbiAgICAgICAgICogIDEuIEJyb2FkY2FzdCBhbiBvZmZlclxyXG4gICAgICAgICAqICAyLiBXYWl0IDUwbXMgZm9yIGFsbCBvZmZlciByZXNwb25zZXMgb3IgdW50aWwgdGhlIGZpcnN0IG9uZSByZXNwb25kIHdpdGggXCJva1wiXHJcbiAgICAgICAgICogIDMuIFNlbGVjdCAoaWYgcG9zc2libGUpIG9uIGFjY2VwdGVkIG9mZmVyIGFuZCBleGVjdXRlIHRoZSBjb25uZWN0XHJcbiAgICAgICAgICovXHJcbiAgICAgICAgZXhwb3J0IGNsYXNzIENvbm5lY3RIYW5kbGVyIHtcclxuICAgICAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ0hBTk5FTF9OQU1FID0gXCJjb25uZWN0XCI7XHJcblxyXG4gICAgICAgICAgICByZWFkb25seSBpcGNfaGFuZGxlcjogQmFzaWNJUENIYW5kbGVyO1xyXG4gICAgICAgICAgICBwcml2YXRlIGlwY19jaGFubmVsOiBDaGFubmVsO1xyXG5cclxuICAgICAgICAgICAgcHVibGljIGNhbGxiYWNrX2F2YWlsYWJsZTogKGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSkgPT4gYm9vbGVhbiA9ICgpID0+IGZhbHNlO1xyXG4gICAgICAgICAgICBwdWJsaWMgY2FsbGJhY2tfZXhlY3V0ZTogKGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSkgPT4gYm9vbGVhbiB8IHN0cmluZyA9ICgpID0+IGZhbHNlO1xyXG5cclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX3BlbmRpbmdfY29ubmVjdF9vZmZlcnM6IHtcclxuICAgICAgICAgICAgICAgIGlkOiBzdHJpbmc7XHJcbiAgICAgICAgICAgICAgICBkYXRhOiBDb25uZWN0UmVxdWVzdERhdGE7XHJcbiAgICAgICAgICAgICAgICB0aW1lb3V0OiBudW1iZXI7XHJcblxyXG4gICAgICAgICAgICAgICAgcmVtb3RlX2hhbmRsZXI6IHN0cmluZztcclxuICAgICAgICAgICAgfVtdID0gW107XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzOiB7XHJcbiAgICAgICAgICAgICAgICBpZDogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgICAgIGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YTtcclxuICAgICAgICAgICAgICAgIHRpbWVvdXQ6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19zdWNjZXNzOiAoKSA9PiBhbnk7XHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19mYWlsZWQ6IChtZXNzYWdlOiBzdHJpbmcpID0+IGFueTtcclxuICAgICAgICAgICAgICAgIGNhbGxiYWNrX2F2YWlsOiAoKSA9PiBQcm9taXNlPGJvb2xlYW4+O1xyXG5cclxuICAgICAgICAgICAgICAgIHJlbW90ZV9oYW5kbGVyPzogc3RyaW5nO1xyXG4gICAgICAgICAgICB9W10gPSBbXTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0cnVjdG9yKGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXIpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaXBjX2hhbmRsZXIgPSBpcGNfaGFuZGxlcjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHVibGljIHNldHVwKCkge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfY2hhbm5lbCA9IHRoaXMuaXBjX2hhbmRsZXIuY3JlYXRlX2NoYW5uZWwodW5kZWZpbmVkLCBDb25uZWN0SGFuZGxlci5DSEFOTkVMX05BTUUpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIgPSB0aGlzLm9uX21lc3NhZ2UuYmluZCh0aGlzKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBvbl9tZXNzYWdlKHNlbmRlcjogc3RyaW5nLCBicm9hZGNhc3Q6IGJvb2xlYW4sIG1lc3NhZ2U6IENoYW5uZWxNZXNzYWdlKSB7XHJcbiAgICAgICAgICAgICAgICBpZihicm9hZGNhc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihtZXNzYWdlLnR5cGUgPT0gXCJvZmZlclwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEgYXMgQ29ubmVjdE9mZmVyO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2NlcHRlZDogdGhpcy5jYWxsYmFja19hdmFpbGFibGUoZGF0YS5kYXRhKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3RfaWQ6IGRhdGEucmVxdWVzdF9pZFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGFzIENvbm5lY3RPZmZlckFuc3dlcjtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlc3BvbnNlLmFjY2VwdGVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIG5ldyBjb25uZWN0IG9mZmVyIGZyb20gJXM6ICVzXCIpLCBzZW5kZXIsIGRhdGEucmVxdWVzdF9pZCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbGQgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3RlX2hhbmRsZXI6IHNlbmRlcixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLmRhdGEsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQ6IGRhdGEucmVxdWVzdF9pZCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lb3V0OiAwXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5wdXNoKGxkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxkLnRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkRyb3BwaW5nIGNvbm5lY3QgcmVxdWVzdCAlcywgYmVjYXVzZSB3ZSBuZXZlciByZWNlaXZlZCBhbiBleGVjdXRlLlwiKSwgbGQuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdfY29ubmVjdF9vZmZlcnMucmVtb3ZlKGxkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIDEyMCAqIDEwMDApIGFzIGFueTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcIm9mZmVyLWFuc3dlclwiLCByZXNwb25zZSwgc2VuZGVyKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKG1lc3NhZ2UudHlwZSA9PSBcIm9mZmVyLWFuc3dlclwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEgYXMgQ29ubmVjdE9mZmVyQW5zd2VyO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gdGhpcy5fcGVuZGluZ19jb25uZWN0c19yZXF1ZXN0cy5maW5kKGUgPT4gZS5pZCA9PT0gZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIXJlcXVlc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjb25uZWN0IG9mZmVyIGFuc3dlciB3aXRoIHVua25vd24gcmVxdWVzdCBpZCAoJXMpLlwiKSwgZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighZGF0YS5hY2NlcHRlZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmRlYnVnKExvZ0NhdGVnb3J5LklQQywgdHIoXCJDbGllbnQgJXMgcmVqZWN0ZWQgdGhlIGNvbm5lY3Qgb2ZmZXIgKCVzKS5cIiksIHNlbmRlciwgcmVxdWVzdC5pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYocmVxdWVzdC5yZW1vdGVfaGFuZGxlcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmRlYnVnKExvZ0NhdGVnb3J5LklQQywgdHIoXCJDbGllbnQgJXMgYWNjZXB0ZWQgdGhlIGNvbm5lY3Qgb2ZmZXIgKCVzKSwgYnV0IG9mZmVyIGhhcyBhbHJlYWR5IGJlZW4gYWNjZXB0ZWQuXCIpLCBzZW5kZXIsIHJlcXVlc3QuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkNsaWVudCAlcyBhY2NlcHRlZCB0aGUgY29ubmVjdCBvZmZlciAoJXMpLiBSZXF1ZXN0IGxvY2FsIGFjY2VwdGFuY2UuXCIpLCBzZW5kZXIsIHJlcXVlc3QuaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyID0gc2VuZGVyO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocmVxdWVzdC50aW1lb3V0KTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfYXZhaWwoKS50aGVuKGZsYWcgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoIWZsYWcpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChcImxvY2FsIGF2YWlsIHJlamVjdGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkV4ZWN1dGluZyBjb25uZWN0IHdpdGggY2xpZW50ICVzXCIpLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXBjX2NoYW5uZWwuc2VuZF9tZXNzYWdlKFwiZXhlY3V0ZVwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogcmVxdWVzdC5pZFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBhcyBDb25uZWN0RXhlY3V0ZSwgcmVxdWVzdC5yZW1vdGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChcImNvbm5lY3QgZXhlY3V0ZSB0aW1lb3V0XCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgMTAwMCkgYXMgYW55O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkxvY2FsIGF2YWlsIGNhbGxiYWNrIGNhdXNlZCBhbiBlcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfZmFpbGVkKHRyKFwibG9jYWwgYXZhaWwgY2FsbGJhY2sgY2F1c2VkIGFuIGVycm9yXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImV4ZWN1dGVkXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgZGF0YSA9IG1lc3NhZ2UuZGF0YSBhcyBDb25uZWN0RXhlY3V0ZWQ7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlcXVlc3QgPSB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLmZpbmQoZSA9PiBlLmlkID09PSBkYXRhLnJlcXVlc3RfaWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighcmVxdWVzdCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlY2VpdmVkIGNvbm5lY3QgZXhlY3V0ZWQgd2l0aCB1bmtub3duIHJlcXVlc3QgaWQgKCVzKS5cIiksIGRhdGEucmVxdWVzdF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlcXVlc3QucmVtb3RlX2hhbmRsZXIgIT0gc2VuZGVyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlZCBmb3IgcmVxdWVzdCAlcywgYnV0IGZyb20gd3JvbmcgY2xpZW50OiAlcyAoZXhwZWN0ZWQgJXMpXCIpLCBkYXRhLnJlcXVlc3RfaWQsIHNlbmRlciwgcmVxdWVzdC5yZW1vdGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5kZWJ1ZyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlZCByZXNwb25zZSBmcm9tIGNsaWVudCAlcyBmb3IgcmVxdWVzdCAlcy4gU3VjY2VlZGVkOiAlbyAoJXMpXCIpLCBzZW5kZXIsIGRhdGEucmVxdWVzdF9pZCwgZGF0YS5zdWNjZWVkZWQsIGRhdGEubWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dChyZXF1ZXN0LnRpbWVvdXQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZihkYXRhLnN1Y2NlZWRlZClcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVlc3QuY2FsbGJhY2tfc3VjY2VzcygpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LmNhbGxiYWNrX2ZhaWxlZChkYXRhLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKG1lc3NhZ2UudHlwZSA9PSBcImV4ZWN1dGVcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBkYXRhID0gbWVzc2FnZS5kYXRhIGFzIENvbm5lY3RFeGVjdXRlO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5maW5kKGUgPT4gZS5pZCA9PT0gZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIXJlcXVlc3QpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZWNlaXZlZCBjb25uZWN0IGV4ZWN1dGUgd2l0aCB1bmtub3duIHJlcXVlc3QgaWQgKCVzKS5cIiksIGRhdGEucmVxdWVzdF9pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlcXVlc3QucmVtb3RlX2hhbmRsZXIgIT0gc2VuZGVyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiUmVjZWl2ZWQgY29ubmVjdCBleGVjdXRlIGZvciByZXF1ZXN0ICVzLCBidXQgZnJvbSB3cm9uZyBjbGllbnQ6ICVzIChleHBlY3RlZCAlcylcIiksIGRhdGEucmVxdWVzdF9pZCwgc2VuZGVyLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocmVxdWVzdC50aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fcGVuZGluZ19jb25uZWN0X29mZmVycy5yZW1vdmUocmVxdWVzdCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIkV4ZWN1dGluZyBjb25uZWN0IGZvciAlc1wiKSwgZGF0YS5yZXF1ZXN0X2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgY3IgPSB0aGlzLmNhbGxiYWNrX2V4ZWN1dGUocmVxdWVzdC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogZGF0YS5yZXF1ZXN0X2lkLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2NlZWRlZDogdHlwZW9mKGNyKSAhPT0gXCJzdHJpbmdcIiAmJiBjcixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U6IHR5cGVvZihjcikgPT09IFwic3RyaW5nXCIgPyBjciA6IFwiXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gYXMgQ29ubmVjdEV4ZWN1dGVkO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcImV4ZWN1dGVkXCIsIHJlc3BvbnNlLCByZXF1ZXN0LnJlbW90ZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHBvc3RfY29ubmVjdF9yZXF1ZXN0KGRhdGE6IENvbm5lY3RSZXF1ZXN0RGF0YSwgY2FsbGJhY2tfYXZhaWw6ICgpID0+IFByb21pc2U8Ym9vbGVhbj4pIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBkID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBkYXRhLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZDogdXVpZHY0KCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVvdXQ6IDAsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19zdWNjZXNzOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLnJlbW92ZShwZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQocGQudGltZW91dCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19mYWlsZWQ6IGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3BlbmRpbmdfY29ubmVjdHNfcmVxdWVzdHMucmVtb3ZlKHBkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dChwZC50aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19hdmFpbDogY2FsbGJhY2tfYXZhaWwsXHJcbiAgICAgICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9wZW5kaW5nX2Nvbm5lY3RzX3JlcXVlc3RzLnB1c2gocGQpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmlwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcIm9mZmVyXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogcGQuaWQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHBkLmRhdGFcclxuICAgICAgICAgICAgICAgICAgICB9IGFzIENvbm5lY3RPZmZlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgcGQudGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBwZC5jYWxsYmFja19mYWlsZWQoXCJyZWNlaXZlZCBubyByZXNwb25zZSB0byBvZmZlclwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9LCA1MCkgYXMgYW55O1xyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgbmFtZXNwYWNlIG1wcm94eSB7XHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBNZXRob2RQcm94eUludm9rZURhdGEge1xyXG4gICAgICAgICAgICBtZXRob2RfbmFtZTogc3RyaW5nO1xyXG4gICAgICAgICAgICBhcmd1bWVudHM6IGFueVtdO1xyXG4gICAgICAgICAgICBwcm9taXNlX2lkOiBzdHJpbmc7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgTWV0aG9kUHJveHlSZXN1bHREYXRhIHtcclxuICAgICAgICAgICAgcHJvbWlzZV9pZDogc3RyaW5nO1xyXG4gICAgICAgICAgICByZXN1bHQ6IGFueTtcclxuICAgICAgICAgICAgc3VjY2VzczogYm9vbGVhbjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZXhwb3J0IGludGVyZmFjZSBNZXRob2RQcm94eUNhbGxiYWNrIHtcclxuICAgICAgICAgICAgcHJvbWlzZTogUHJvbWlzZTxhbnk+O1xyXG4gICAgICAgICAgICBwcm9taXNlX2lkOiBzdHJpbmc7XHJcblxyXG4gICAgICAgICAgICByZXNvbHZlOiAob2JqZWN0OiBhbnkpID0+IGFueTtcclxuICAgICAgICAgICAgcmVqZWN0OiAob2JqZWN0OiBhbnkpID0+IGFueTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCB0eXBlIE1ldGhvZFByb3h5Q29ubmVjdFBhcmFtZXRlcnMgPSB7XHJcbiAgICAgICAgICAgIGNoYW5uZWxfaWQ6IHN0cmluZztcclxuICAgICAgICAgICAgY2xpZW50X2lkOiBzdHJpbmc7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGV4cG9ydCBhYnN0cmFjdCBjbGFzcyBNZXRob2RQcm94eSB7XHJcbiAgICAgICAgICAgIHJlYWRvbmx5IGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXI7XHJcbiAgICAgICAgICAgIHByaXZhdGUgX2lwY19jaGFubmVsOiBDaGFubmVsO1xyXG4gICAgICAgICAgICBwcml2YXRlIF9pcGNfcGFyYW1ldGVyczogTWV0aG9kUHJveHlDb25uZWN0UGFyYW1ldGVycztcclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgcmVhZG9ubHkgX2xvY2FsOiBib29sZWFuO1xyXG4gICAgICAgICAgICBwcml2YXRlIHJlYWRvbmx5IF9zbGF2ZTogYm9vbGVhbjtcclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX2Nvbm5lY3RlZDogYm9vbGVhbjtcclxuICAgICAgICAgICAgcHJpdmF0ZSBfcHJveGllZF9tZXRob2RzOiB7W2tleTogc3RyaW5nXTooKSA9PiBQcm9taXNlPGFueT59ID0ge307XHJcbiAgICAgICAgICAgIHByaXZhdGUgX3Byb3hpZWRfY2FsbGJhY2tzOiB7W2tleTogc3RyaW5nXTpNZXRob2RQcm94eUNhbGxiYWNrfSA9IHt9O1xyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGlwY19oYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXIsIGNvbm5lY3RfcGFyYW1zPzogTWV0aG9kUHJveHlDb25uZWN0UGFyYW1ldGVycykge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5pcGNfaGFuZGxlciA9IGlwY19oYW5kbGVyO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5faXBjX3BhcmFtZXRlcnMgPSBjb25uZWN0X3BhcmFtcztcclxuICAgICAgICAgICAgICAgIHRoaXMuX2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fc2xhdmUgPSB0eXBlb2YoY29ubmVjdF9wYXJhbXMpICE9PSBcInVuZGVmaW5lZFwiO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fbG9jYWwgPSB0eXBlb2YoY29ubmVjdF9wYXJhbXMpICE9PSBcInVuZGVmaW5lZFwiICYmIGNvbm5lY3RfcGFyYW1zLmNoYW5uZWxfaWQgPT09IFwibG9jYWxcIiAmJiBjb25uZWN0X3BhcmFtcy5jbGllbnRfaWQgPT09IFwibG9jYWxcIjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIHNldHVwKCkge1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy5fbG9jYWwpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jb25uZWN0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub25fY29ubmVjdGVkKCk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbCA9IHRoaXMuaXBjX2hhbmRsZXIuY3JlYXRlX2NoYW5uZWwodGhpcy5faXBjX3BhcmFtZXRlcnMuY2xpZW50X2lkLCB0aGlzLl9pcGNfcGFyYW1ldGVycy5jaGFubmVsX2lkKTtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsID0gdGhpcy5pcGNfaGFuZGxlci5jcmVhdGVfY2hhbm5lbCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5tZXNzYWdlX2hhbmRsZXIgPSB0aGlzLl9oYW5kbGVfbWVzc2FnZS5iaW5kKHRoaXMpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5zZW5kX21lc3NhZ2UoXCJpbml0aWFsaXplXCIsIHt9KTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGZpbmFsaXplKCkge1xyXG4gICAgICAgICAgICAgICAgaWYoIXRoaXMuX2xvY2FsKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYodGhpcy5fY29ubmVjdGVkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC5zZW5kX21lc3NhZ2UoXCJmaW5hbGl6ZVwiLCB7fSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaXBjX2hhbmRsZXIuZGVsZXRlX2NoYW5uZWwodGhpcy5faXBjX2NoYW5uZWwpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHByb21pc2Ugb2YgT2JqZWN0LnZhbHVlcyh0aGlzLl9wcm94aWVkX2NhbGxiYWNrcykpXHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZS5yZWplY3QoXCJkaXNjb25uZWN0ZWRcIik7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX2NhbGxiYWNrcyA9IHt9O1xyXG5cclxuICAgICAgICAgICAgICAgIHRoaXMuX2Nvbm5lY3RlZCA9IGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5vbl9kaXNjb25uZWN0ZWQoKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIHJlZ2lzdGVyX21ldGhvZDxSPihtZXRob2Q6ICguLi5hcmdzOiBhbnlbXSkgPT4gUHJvbWlzZTxSPiB8IHN0cmluZykge1xyXG4gICAgICAgICAgICAgICAgbGV0IG1ldGhvZF9uYW1lOiBzdHJpbmc7XHJcbiAgICAgICAgICAgICAgICBpZih0eXBlb2YgbWV0aG9kID09PSBcImZ1bmN0aW9uXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlZ2lzdGVyaW5nIG1ldGhvZCBwcm94eSBmb3IgJXNcIiksIG1ldGhvZC5uYW1lKTtcclxuICAgICAgICAgICAgICAgICAgICBtZXRob2RfbmFtZSA9IG1ldGhvZC5uYW1lO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuZGVidWcoTG9nQ2F0ZWdvcnkuSVBDLCB0cihcIlJlZ2lzdGVyaW5nIG1ldGhvZCBwcm94eSBmb3IgJXNcIiksIG1ldGhvZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kX25hbWUgPSBtZXRob2Q7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIXRoaXNbbWV0aG9kX25hbWVdKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwibWV0aG9kIGlzIG1pc3NpbmcgaW4gY3VycmVudCBvYmplY3RcIjtcclxuXHJcbiAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX21ldGhvZHNbbWV0aG9kX25hbWVdID0gdGhpc1ttZXRob2RfbmFtZV07XHJcbiAgICAgICAgICAgICAgICBpZighdGhpcy5fbG9jYWwpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzW21ldGhvZF9uYW1lXSA9ICguLi5hcmdzOiBhbnlbXSkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZighdGhpcy5fY29ubmVjdGVkKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KFwibm90IGNvbm5lY3RlZFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHByb3h5X2NhbGxiYWNrID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvbWlzZV9pZDogdXVpZHY0KClcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBhcyBNZXRob2RQcm94eUNhbGxiYWNrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9wcm94aWVkX2NhbGxiYWNrc1twcm94eV9jYWxsYmFjay5wcm9taXNlX2lkXSA9IHByb3h5X2NhbGxiYWNrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBwcm94eV9jYWxsYmFjay5wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJveHlfY2FsbGJhY2sucmVzb2x2ZSA9IHJlc29sdmU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm94eV9jYWxsYmFjay5yZWplY3QgPSByZWplY3Q7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5faXBjX2NoYW5uZWwuc2VuZF9tZXNzYWdlKFwiaW52b2tlXCIsIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21pc2VfaWQ6IHByb3h5X2NhbGxiYWNrLnByb21pc2VfaWQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmd1bWVudHM6IFsuLi5hcmdzXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZF9uYW1lOiBtZXRob2RfbmFtZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9IGFzIE1ldGhvZFByb3h5SW52b2tlRGF0YSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBwcm94eV9jYWxsYmFjay5wcm9taXNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfaGFuZGxlX21lc3NhZ2UocmVtb3RlX2lkOiBzdHJpbmcsIGJvcmFkY2FzdDogYm9vbGVhbiwgbWVzc2FnZTogQ2hhbm5lbE1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIGlmKG1lc3NhZ2UudHlwZSA9PT0gXCJmaW5hbGl6ZVwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlX2ZpbmFsaXplKCk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYobWVzc2FnZS50eXBlID09PSBcImluaXRpYWxpemVcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2hhbmRsZV9yZW1vdGVfY2FsbGJhY2socmVtb3RlX2lkKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZihtZXNzYWdlLnR5cGUgPT09IFwiaW52b2tlXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9oYW5kbGVfaW52b2tlKG1lc3NhZ2UuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYobWVzc2FnZS50eXBlID09PSBcInJlc3VsdFwiKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faGFuZGxlX3Jlc3VsdChtZXNzYWdlLmRhdGEpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9oYW5kbGVfZmluYWxpemUoKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLm9uX2Rpc2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fY29ubmVjdGVkID0gZmFsc2U7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHByaXZhdGUgX2hhbmRsZV9yZW1vdGVfY2FsbGJhY2socmVtb3RlX2lkOiBzdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9pcGNfY2hhbm5lbC50YXJnZXRfaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZih0aGlzLl9zbGF2ZSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJpbml0aWFsaXplIHdyb25nIHN0YXRlIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pcGNfY2hhbm5lbC50YXJnZXRfaWQgPSByZW1vdGVfaWQ7IC8qIG5vdyB3ZSdyZSBhYmxlIHRvIHNlbmQgbWVzc2FnZXMgKi9cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcImluaXRpYWxpemVcIiwgdHJ1ZSk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9zbGF2ZSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJpbml0aWFsaXplIHdyb25nIHN0YXRlIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLm9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5fY29ubmVjdGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfc2VuZF9yZXN1bHQocHJvbWlzZV9pZDogc3RyaW5nLCBzdWNjZXNzOiBib29sZWFuLCBtZXNzYWdlOiBhbnkpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2lwY19jaGFubmVsLnNlbmRfbWVzc2FnZShcInJlc3VsdFwiLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZV9pZDogcHJvbWlzZV9pZCxcclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQ6IG1lc3NhZ2UsXHJcbiAgICAgICAgICAgICAgICAgICAgc3VjY2Vzczogc3VjY2Vzc1xyXG4gICAgICAgICAgICAgICAgfSBhcyBNZXRob2RQcm94eVJlc3VsdERhdGEpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBwcml2YXRlIF9oYW5kbGVfaW52b2tlKGRhdGE6IE1ldGhvZFByb3h5SW52b2tlRGF0YSkge1xyXG4gICAgICAgICAgICAgICAgaWYodGhpcy5fcHJveGllZF9tZXRob2RzW2RhdGEubWV0aG9kX25hbWVdKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwid2UgY291bGQgbm90IGludm9rZSBhIGxvY2FsIHByb3hpZWQgbWV0aG9kIVwiO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzW2RhdGEubWV0aG9kX25hbWVdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2VuZF9yZXN1bHQoZGF0YS5wcm9taXNlX2lkLCBmYWxzZSwgXCJtaXNzaW5nIG1ldGhvZFwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiSW52b2tpbmcgbWV0aG9kICVzIHdpdGggYXJndW1lbnRzOiAlb1wiKSwgZGF0YS5tZXRob2RfbmFtZSwgZGF0YS5hcmd1bWVudHMpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBwcm9taXNlID0gdGhpc1tkYXRhLm1ldGhvZF9uYW1lXSguLi5kYXRhLmFyZ3VtZW50cyk7XHJcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZS50aGVuKHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJSZXN1bHQ6ICVvXCIpLCByZXN1bHQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9zZW5kX3Jlc3VsdChkYXRhLnByb21pc2VfaWQsIHRydWUsIHJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9zZW5kX3Jlc3VsdChkYXRhLnByb21pc2VfaWQsIGZhbHNlLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc2VuZF9yZXN1bHQoZGF0YS5wcm9taXNlX2lkLCBmYWxzZSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcHJpdmF0ZSBfaGFuZGxlX3Jlc3VsdChkYXRhOiBNZXRob2RQcm94eVJlc3VsdERhdGEpIHtcclxuICAgICAgICAgICAgICAgIGlmKCF0aGlzLl9wcm94aWVkX2NhbGxiYWNrc1tkYXRhLnByb21pc2VfaWRdKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiUmVjZWl2ZWQgcHJveHkgbWV0aG9kIHJlc3VsdCBmb3IgdW5rbm93biBwcm9taXNlXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBjb25zdCBjYWxsYmFjayA9IHRoaXMuX3Byb3hpZWRfY2FsbGJhY2tzW2RhdGEucHJvbWlzZV9pZF07XHJcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy5fcHJveGllZF9jYWxsYmFja3NbZGF0YS5wcm9taXNlX2lkXTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZihkYXRhLnN1Y2Nlc3MpXHJcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sucmVzb2x2ZShkYXRhLnJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgY2FsbGJhY2sucmVqZWN0KGRhdGEucmVzdWx0KTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZ2VuZXJhdGVfY29ubmVjdF9wYXJhbWV0ZXJzKCkgOiBNZXRob2RQcm94eUNvbm5lY3RQYXJhbWV0ZXJzIHtcclxuICAgICAgICAgICAgICAgIGlmKHRoaXMuX3NsYXZlKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IFwib25seSBtYXN0ZXJzIGNhbiBnZW5lcmF0ZSBjb25uZWN0IHBhcmFtZXRlcnMhXCI7XHJcbiAgICAgICAgICAgICAgICBpZighdGhpcy5faXBjX2NoYW5uZWwpXHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJwbGVhc2UgY2FsbCBzZXR1cCgpIGJlZm9yZVwiO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY2hhbm5lbF9pZDogdGhpcy5faXBjX2NoYW5uZWwuY2hhbm5lbF9pZCxcclxuICAgICAgICAgICAgICAgICAgICBjbGllbnRfaWQ6IHRoaXMuaXBjX2hhbmRsZXIuZ2V0X2xvY2FsX2FkZHJlc3MoKVxyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaXNfc2xhdmUoKSB7IHJldHVybiB0aGlzLl9sb2NhbCB8fCB0aGlzLl9zbGF2ZTsgfSAvKiB0aGUgcG9wb3V0IG1vZGFsICovXHJcbiAgICAgICAgICAgIGlzX21hc3RlcigpIHsgcmV0dXJuIHRoaXMuX2xvY2FsIHx8ICF0aGlzLl9zbGF2ZTsgfSAvKiB0aGUgaG9zdCAodGVhd2ViIGFwcGxpY2F0aW9uKSAqL1xyXG5cclxuICAgICAgICAgICAgcHJvdGVjdGVkIGFic3RyYWN0IG9uX2Nvbm5lY3RlZCgpO1xyXG4gICAgICAgICAgICBwcm90ZWN0ZWQgYWJzdHJhY3Qgb25fZGlzY29ubmVjdGVkKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGxldCBoYW5kbGVyOiBCYXNpY0lQQ0hhbmRsZXI7XHJcbiAgICBsZXQgY29ubmVjdF9oYW5kbGVyOiBjb25uZWN0LkNvbm5lY3RIYW5kbGVyO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBzZXR1cCgpIHtcclxuICAgICAgICBpZighc3VwcG9ydGVkKCkpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaGFuZGxlciA9IG5ldyBCcm9hZGNhc3RDaGFubmVsSVBDKCk7XHJcbiAgICAgICAgaGFuZGxlci5zZXR1cCgpO1xyXG5cclxuICAgICAgICBjb25uZWN0X2hhbmRsZXIgPSBuZXcgY29ubmVjdC5Db25uZWN0SGFuZGxlcihoYW5kbGVyKTtcclxuICAgICAgICBjb25uZWN0X2hhbmRsZXIuc2V0dXAoKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gZ2V0X2hhbmRsZXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIGhhbmRsZXI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGdldF9jb25uZWN0X2hhbmRsZXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIGNvbm5lY3RfaGFuZGxlcjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc3VwcG9ydGVkKCkge1xyXG4gICAgICAgIC8qIGlvcyBkb2VzIG5vdCBzdXBwb3J0IHRoaXMgKi9cclxuICAgICAgICByZXR1cm4gdHlwZW9mKHdpbmRvdy5Ccm9hZGNhc3RDaGFubmVsKSAhPT0gXCJ1bmRlZmluZWRcIjtcclxuICAgIH1cclxufSIsImV4cG9ydCBlbnVtIEtleUNvZGUge1xyXG4gICAgS0VZX0NBTkNFTCA9IDMsXHJcbiAgICBLRVlfSEVMUCA9IDYsXHJcbiAgICBLRVlfQkFDS19TUEFDRSA9IDgsXHJcbiAgICBLRVlfVEFCID0gOSxcclxuICAgIEtFWV9DTEVBUiA9IDEyLFxyXG4gICAgS0VZX1JFVFVSTiA9IDEzLFxyXG4gICAgS0VZX0VOVEVSID0gMTQsXHJcbiAgICBLRVlfU0hJRlQgPSAxNixcclxuICAgIEtFWV9DT05UUk9MID0gMTcsXHJcbiAgICBLRVlfQUxUID0gMTgsXHJcbiAgICBLRVlfUEFVU0UgPSAxOSxcclxuICAgIEtFWV9DQVBTX0xPQ0sgPSAyMCxcclxuICAgIEtFWV9FU0NBUEUgPSAyNyxcclxuICAgIEtFWV9TUEFDRSA9IDMyLFxyXG4gICAgS0VZX1BBR0VfVVAgPSAzMyxcclxuICAgIEtFWV9QQUdFX0RPV04gPSAzNCxcclxuICAgIEtFWV9FTkQgPSAzNSxcclxuICAgIEtFWV9IT01FID0gMzYsXHJcbiAgICBLRVlfTEVGVCA9IDM3LFxyXG4gICAgS0VZX1VQID0gMzgsXHJcbiAgICBLRVlfUklHSFQgPSAzOSxcclxuICAgIEtFWV9ET1dOID0gNDAsXHJcbiAgICBLRVlfUFJJTlRTQ1JFRU4gPSA0NCxcclxuICAgIEtFWV9JTlNFUlQgPSA0NSxcclxuICAgIEtFWV9ERUxFVEUgPSA0NixcclxuICAgIEtFWV8wID0gNDgsXHJcbiAgICBLRVlfMSA9IDQ5LFxyXG4gICAgS0VZXzIgPSA1MCxcclxuICAgIEtFWV8zID0gNTEsXHJcbiAgICBLRVlfNCA9IDUyLFxyXG4gICAgS0VZXzUgPSA1MyxcclxuICAgIEtFWV82ID0gNTQsXHJcbiAgICBLRVlfNyA9IDU1LFxyXG4gICAgS0VZXzggPSA1NixcclxuICAgIEtFWV85ID0gNTcsXHJcbiAgICBLRVlfU0VNSUNPTE9OID0gNTksXHJcbiAgICBLRVlfRVFVQUxTID0gNjEsXHJcbiAgICBLRVlfQSA9IDY1LFxyXG4gICAgS0VZX0IgPSA2NixcclxuICAgIEtFWV9DID0gNjcsXHJcbiAgICBLRVlfRCA9IDY4LFxyXG4gICAgS0VZX0UgPSA2OSxcclxuICAgIEtFWV9GID0gNzAsXHJcbiAgICBLRVlfRyA9IDcxLFxyXG4gICAgS0VZX0ggPSA3MixcclxuICAgIEtFWV9JID0gNzMsXHJcbiAgICBLRVlfSiA9IDc0LFxyXG4gICAgS0VZX0sgPSA3NSxcclxuICAgIEtFWV9MID0gNzYsXHJcbiAgICBLRVlfTSA9IDc3LFxyXG4gICAgS0VZX04gPSA3OCxcclxuICAgIEtFWV9PID0gNzksXHJcbiAgICBLRVlfUCA9IDgwLFxyXG4gICAgS0VZX1EgPSA4MSxcclxuICAgIEtFWV9SID0gODIsXHJcbiAgICBLRVlfUyA9IDgzLFxyXG4gICAgS0VZX1QgPSA4NCxcclxuICAgIEtFWV9VID0gODUsXHJcbiAgICBLRVlfViA9IDg2LFxyXG4gICAgS0VZX1cgPSA4NyxcclxuICAgIEtFWV9YID0gODgsXHJcbiAgICBLRVlfWSA9IDg5LFxyXG4gICAgS0VZX1ogPSA5MCxcclxuICAgIEtFWV9MRUZUX0NNRCA9IDkxLFxyXG4gICAgS0VZX1JJR0hUX0NNRCA9IDkzLFxyXG4gICAgS0VZX0NPTlRFWFRfTUVOVSA9IDkzLFxyXG4gICAgS0VZX05VTVBBRDAgPSA5NixcclxuICAgIEtFWV9OVU1QQUQxID0gOTcsXHJcbiAgICBLRVlfTlVNUEFEMiA9IDk4LFxyXG4gICAgS0VZX05VTVBBRDMgPSA5OSxcclxuICAgIEtFWV9OVU1QQUQ0ID0gMTAwLFxyXG4gICAgS0VZX05VTVBBRDUgPSAxMDEsXHJcbiAgICBLRVlfTlVNUEFENiA9IDEwMixcclxuICAgIEtFWV9OVU1QQUQ3ID0gMTAzLFxyXG4gICAgS0VZX05VTVBBRDggPSAxMDQsXHJcbiAgICBLRVlfTlVNUEFEOSA9IDEwNSxcclxuICAgIEtFWV9NVUxUSVBMWSA9IDEwNixcclxuICAgIEtFWV9BREQgPSAxMDcsXHJcbiAgICBLRVlfU0VQQVJBVE9SID0gMTA4LFxyXG4gICAgS0VZX1NVQlRSQUNUID0gMTA5LFxyXG4gICAgS0VZX0RFQ0lNQUwgPSAxMTAsXHJcbiAgICBLRVlfRElWSURFID0gMTExLFxyXG4gICAgS0VZX0YxID0gMTEyLFxyXG4gICAgS0VZX0YyID0gMTEzLFxyXG4gICAgS0VZX0YzID0gMTE0LFxyXG4gICAgS0VZX0Y0ID0gMTE1LFxyXG4gICAgS0VZX0Y1ID0gMTE2LFxyXG4gICAgS0VZX0Y2ID0gMTE3LFxyXG4gICAgS0VZX0Y3ID0gMTE4LFxyXG4gICAgS0VZX0Y4ID0gMTE5LFxyXG4gICAgS0VZX0Y5ID0gMTIwLFxyXG4gICAgS0VZX0YxMCA9IDEyMSxcclxuICAgIEtFWV9GMTEgPSAxMjIsXHJcbiAgICBLRVlfRjEyID0gMTIzLFxyXG4gICAgS0VZX0YxMyA9IDEyNCxcclxuICAgIEtFWV9GMTQgPSAxMjUsXHJcbiAgICBLRVlfRjE1ID0gMTI2LFxyXG4gICAgS0VZX0YxNiA9IDEyNyxcclxuICAgIEtFWV9GMTcgPSAxMjgsXHJcbiAgICBLRVlfRjE4ID0gMTI5LFxyXG4gICAgS0VZX0YxOSA9IDEzMCxcclxuICAgIEtFWV9GMjAgPSAxMzEsXHJcbiAgICBLRVlfRjIxID0gMTMyLFxyXG4gICAgS0VZX0YyMiA9IDEzMyxcclxuICAgIEtFWV9GMjMgPSAxMzQsXHJcbiAgICBLRVlfRjI0ID0gMTM1LFxyXG4gICAgS0VZX05VTV9MT0NLID0gMTQ0LFxyXG4gICAgS0VZX1NDUk9MTF9MT0NLID0gMTQ1LFxyXG4gICAgS0VZX0NPTU1BID0gMTg4LFxyXG4gICAgS0VZX1BFUklPRCA9IDE5MCxcclxuICAgIEtFWV9TTEFTSCA9IDE5MSxcclxuICAgIEtFWV9CQUNLX1FVT1RFID0gMTkyLFxyXG4gICAgS0VZX09QRU5fQlJBQ0tFVCA9IDIxOSxcclxuICAgIEtFWV9CQUNLX1NMQVNIID0gMjIwLFxyXG4gICAgS0VZX0NMT1NFX0JSQUNLRVQgPSAyMjEsXHJcbiAgICBLRVlfUVVPVEUgPSAyMjIsXHJcbiAgICBLRVlfTUVUQSA9IDIyNFxyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHBwdCB7XHJcbiAgICBleHBvcnQgZW51bSBFdmVudFR5cGUge1xyXG4gICAgICAgIEtFWV9QUkVTUyxcclxuICAgICAgICBLRVlfUkVMRUFTRSxcclxuICAgICAgICBLRVlfVFlQRURcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZW51bSBTcGVjaWFsS2V5IHtcclxuICAgICAgICBDVFJMLFxyXG4gICAgICAgIFdJTkRPV1MsXHJcbiAgICAgICAgU0hJRlQsXHJcbiAgICAgICAgQUxUXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBLZXlEZXNjcmlwdG9yIHtcclxuICAgICAgICBrZXlfY29kZTogc3RyaW5nO1xyXG5cclxuICAgICAgICBrZXlfY3RybDogYm9vbGVhbjtcclxuICAgICAgICBrZXlfd2luZG93czogYm9vbGVhbjtcclxuICAgICAgICBrZXlfc2hpZnQ6IGJvb2xlYW47XHJcbiAgICAgICAga2V5X2FsdDogYm9vbGVhbjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIEtleUV2ZW50IGV4dGVuZHMgS2V5RGVzY3JpcHRvciB7XHJcbiAgICAgICAgcmVhZG9ubHkgdHlwZTogRXZlbnRUeXBlO1xyXG5cclxuICAgICAgICByZWFkb25seSBrZXk6IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIEtleUhvb2sgZXh0ZW5kcyBLZXlEZXNjcmlwdG9yIHtcclxuICAgICAgICBjYW5jZWw6IGJvb2xlYW47XHJcblxyXG5cclxuICAgICAgICBjYWxsYmFja19wcmVzczogKCkgPT4gYW55O1xyXG4gICAgICAgIGNhbGxiYWNrX3JlbGVhc2U6ICgpID0+IGFueTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24ga2V5X2Rlc2NyaXB0aW9uKGtleTogS2V5RGVzY3JpcHRvcikge1xyXG4gICAgICAgIGxldCByZXN1bHQgPSBcIlwiO1xyXG4gICAgICAgIGlmKGtleS5rZXlfc2hpZnQpXHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiArIFwiICsgdHIoXCJTaGlmdFwiKTtcclxuICAgICAgICBpZihrZXkua2V5X2FsdClcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiICsgXCIgKyB0cihcIkFsdFwiKTtcclxuICAgICAgICBpZihrZXkua2V5X2N0cmwpXHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiArIFwiICsgdHIoXCJDVFJMXCIpO1xyXG4gICAgICAgIGlmKGtleS5rZXlfd2luZG93cylcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiICsgXCIgKyB0cihcIldpblwiKTtcclxuXHJcbiAgICAgICAgaWYoIXJlc3VsdCAmJiAha2V5LmtleV9jb2RlKVxyXG4gICAgICAgICAgICByZXR1cm4gdHIoXCJ1bnNldFwiKTtcclxuXHJcbiAgICAgICAgaWYoa2V5LmtleV9jb2RlKVxyXG4gICAgICAgICAgICByZXN1bHQgKz0gXCIgKyBcIiArIGtleS5rZXlfY29kZTtcclxuICAgICAgICByZXR1cm4gcmVzdWx0LnN1YnN0cigzKTtcclxuICAgIH1cclxufSIsImltcG9ydCB7XHJcbiAgICBDbGllbnROYW1lSW5mbyxcclxuICAgIENvbW1hbmRSZXN1bHQsXHJcbiAgICBFcnJvcklELFxyXG4gICAgUGxheWxpc3QsIFBsYXlsaXN0SW5mbywgUGxheWxpc3RTb25nLFxyXG4gICAgUXVlcnlMaXN0LFxyXG4gICAgUXVlcnlMaXN0RW50cnksIFNlcnZlckdyb3VwQ2xpZW50XHJcbn0gZnJvbSBcIi4vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7Q2hhbm5lbEVudHJ5fSBmcm9tIFwiLi4vY2hhbm5lbC10cmVlL2NoYW5uZWxcIjtcclxuaW1wb3J0IHtDaGF0VHlwZX0gZnJvbSBcIi4uL3VpL2ZyYW1lcy9jaGF0XCI7XHJcbmltcG9ydCB7Q2xpZW50RW50cnl9IGZyb20gXCIuLi9jaGFubmVsLXRyZWUvY2xpZW50XCI7XHJcbmltcG9ydCB7QWJzdHJhY3RDb21tYW5kSGFuZGxlciwgU2VydmVyQ29tbWFuZCwgU2luZ2xlQ29tbWFuZEhhbmRsZXJ9IGZyb20gXCIuL0Nvbm5lY3Rpb25CYXNlXCI7XHJcbmltcG9ydCB7bG9nLCBMb2dDYXRlZ29yeX0gZnJvbSBcIi4uL2xvZ1wiO1xyXG5cclxuZXhwb3J0IGNsYXNzIENvbW1hbmRIZWxwZXIgZXh0ZW5kcyBBYnN0cmFjdENvbW1hbmRIYW5kbGVyIHtcclxuICAgIHByaXZhdGUgX3dob19hbV9pOiBhbnk7XHJcbiAgICBwcml2YXRlIF9hd2FpdGVyc191bmlxdWVfaWRzOiB7W3VuaXF1ZV9pZDogc3RyaW5nXTooKHJlc29sdmVkOiBDbGllbnROYW1lSW5mbykgPT4gYW55KVtdfSA9IHt9O1xyXG4gICAgcHJpdmF0ZSBfYXdhaXRlcnNfdW5pcXVlX2RiaWQ6IHtbZGF0YWJhc2VfaWQ6IG51bWJlcl06KChyZXNvbHZlZDogQ2xpZW50TmFtZUluZm8pID0+IGFueSlbXX0gPSB7fTtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uKSB7XHJcbiAgICAgICAgc3VwZXIoY29ubmVjdGlvbik7XHJcblxyXG4gICAgICAgIHRoaXMudm9sYXRpbGVfaGFuZGxlcl9ib3NzID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5pZ25vcmVfY29uc3VtZWQgPSB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIGluaXRpYWxpemUoKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLmNvbW1hbmRfaGFuZGxlcl9ib3NzKCkucmVnaXN0ZXJfaGFuZGxlcih0aGlzKTtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIGlmKHRoaXMuY29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICBjb25zdCBoYm9zcyA9IHRoaXMuY29ubmVjdGlvbi5jb21tYW5kX2hhbmRsZXJfYm9zcygpO1xyXG4gICAgICAgICAgICBoYm9zcyAmJiBoYm9zcy51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9pZHMgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgaGFuZGxlX2NvbW1hbmQoY29tbWFuZDogU2VydmVyQ29tbWFuZCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIGlmKGNvbW1hbmQuY29tbWFuZCA9PSBcIm5vdGlmeWNsaWVudG5hbWVmcm9tdWlkXCIpXHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlX25vdGlmeWNsaWVudG5hbWVmcm9tdWlkKGNvbW1hbmQuYXJndW1lbnRzKTtcclxuICAgICAgICBpZihjb21tYW5kLmNvbW1hbmQgPT0gXCJub3RpZnljbGllbnRnZXRuYW1lZnJvbWRiaWRcIilcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVfbm90aWZ5Y2xpZW50Z2V0bmFtZWZyb21kYmlkKGNvbW1hbmQuYXJndW1lbnRzKTtcclxuICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICBqb2luQ2hhbm5lbChjaGFubmVsOiBDaGFubmVsRW50cnksIHBhc3N3b3JkPzogc3RyaW5nKSA6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwiY2xpZW50bW92ZVwiLCB7XHJcbiAgICAgICAgICAgIFwiY2xpZFwiOiB0aGlzLmNvbm5lY3Rpb24uY2xpZW50LmdldENsaWVudElkKCksXHJcbiAgICAgICAgICAgIFwiY2lkXCI6IGNoYW5uZWwuZ2V0Q2hhbm5lbElkKCksXHJcbiAgICAgICAgICAgIFwiY3B3XCI6IHBhc3N3b3JkIHx8IFwiXCJcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzZW5kTWVzc2FnZShtZXNzYWdlOiBzdHJpbmcsIHR5cGU6IENoYXRUeXBlLCB0YXJnZXQ/OiBDaGFubmVsRW50cnkgfCBDbGllbnRFbnRyeSkgOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ+IHtcclxuICAgICAgICBpZih0eXBlID09IENoYXRUeXBlLlNFUlZFUilcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJzZW5kdGV4dG1lc3NhZ2VcIiwge1widGFyZ2V0bW9kZVwiOiAzLCBcInRhcmdldFwiOiAwLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlID09IENoYXRUeXBlLkNIQU5ORUwpXHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwic2VuZHRleHRtZXNzYWdlXCIsIHtcInRhcmdldG1vZGVcIjogMiwgXCJ0YXJnZXRcIjogKHRhcmdldCBhcyBDaGFubmVsRW50cnkpLmdldENoYW5uZWxJZCgpLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlID09IENoYXRUeXBlLkNMSUVOVClcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJzZW5kdGV4dG1lc3NhZ2VcIiwge1widGFyZ2V0bW9kZVwiOiAxLCBcInRhcmdldFwiOiAodGFyZ2V0IGFzIENsaWVudEVudHJ5KS5jbGllbnRJZCgpLCBcIm1zZ1wiOiBtZXNzYWdlfSk7XHJcbiAgICB9XHJcblxyXG4gICAgdXBkYXRlQ2xpZW50KGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKSA6IFByb21pc2U8Q29tbWFuZFJlc3VsdD4ge1xyXG4gICAgICAgIGxldCBkYXRhID0ge307XHJcbiAgICAgICAgZGF0YVtrZXldID0gdmFsdWU7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJjbGllbnR1cGRhdGVcIiwgZGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgaW5mb19mcm9tX3VpZCguLi5fdW5pcXVlX2lkczogc3RyaW5nW10pIDogUHJvbWlzZTxDbGllbnROYW1lSW5mb1tdPiB7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2U6IENsaWVudE5hbWVJbmZvW10gPSBbXTtcclxuICAgICAgICBjb25zdCByZXF1ZXN0ID0gW107XHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2lkcyA9IG5ldyBTZXQoX3VuaXF1ZV9pZHMpO1xyXG4gICAgICAgIGlmKCF1bmlxdWVfaWRzLnNpemUpIHJldHVybiBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2lkX3Jlc29sdmVyczoge1t1bmlxdWVfaWQ6IHN0cmluZ106IChyZXNvbHZlZDogQ2xpZW50TmFtZUluZm8pID0+IGFueX0gPSB7fTtcclxuXHJcblxyXG4gICAgICAgIGZvcihjb25zdCB1bmlxdWVfaWQgb2YgdW5pcXVlX2lkcykge1xyXG4gICAgICAgICAgICByZXF1ZXN0LnB1c2goeydjbHVpZCc6IHVuaXF1ZV9pZH0pO1xyXG4gICAgICAgICAgICAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2lkc1t1bmlxdWVfaWRdIHx8ICh0aGlzLl9hd2FpdGVyc191bmlxdWVfaWRzW3VuaXF1ZV9pZF0gPSBbXSkpXHJcbiAgICAgICAgICAgICAgICAucHVzaCh1bmlxdWVfaWRfcmVzb2x2ZXJzW3VuaXF1ZV9pZF0gPSBpbmZvID0+IHJlc3BvbnNlLnB1c2goaW5mbykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImNsaWVudGdldG5hbWVmcm9tdWlkXCIsIHJlcXVlc3QpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBDb21tYW5kUmVzdWx0ICYmIGVycm9yLmlkID09IEVycm9ySUQuRU1QVFlfUkVTVUxUKSB7XHJcbiAgICAgICAgICAgICAgICAvKiBub3RoaW5nICovXHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0gZmluYWxseSB7XHJcbiAgICAgICAgICAgIC8qIGNsZWFudXAgKi9cclxuICAgICAgICAgICAgZm9yKGNvbnN0IHVuaXF1ZV9pZCBvZiBPYmplY3Qua2V5cyh1bmlxdWVfaWRfcmVzb2x2ZXJzKSlcclxuICAgICAgICAgICAgICAgICh0aGlzLl9hd2FpdGVyc191bmlxdWVfaWRzW3VuaXF1ZV9pZF0gfHwgW10pLnJlbW92ZSh1bmlxdWVfaWRfcmVzb2x2ZXJzW3VuaXF1ZV9pZF0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgaGFuZGxlX25vdGlmeWNsaWVudGdldG5hbWVmcm9tZGJpZChqc29uOiBhbnlbXSkge1xyXG4gICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm86IENsaWVudE5hbWVJbmZvID0ge1xyXG4gICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZDogZW50cnlbXCJjbHVpZFwiXSxcclxuICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbG5hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IHBhcnNlSW50KGVudHJ5W1wiY2xkYmlkXCJdKVxyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgZnVuY3Rpb25zID0gdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbaW5mby5jbGllbnRfZGF0YWJhc2VfaWRdIHx8IFtdO1xyXG4gICAgICAgICAgICBkZWxldGUgdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbaW5mby5jbGllbnRfZGF0YWJhc2VfaWRdO1xyXG5cclxuICAgICAgICAgICAgZm9yKGNvbnN0IGZuIG9mIGZ1bmN0aW9ucylcclxuICAgICAgICAgICAgICAgIGZuKGluZm8pO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBpbmZvX2Zyb21fY2xkYmlkKC4uLl9jbGRiaWQ6IG51bWJlcltdKSA6IFByb21pc2U8Q2xpZW50TmFtZUluZm9bXT4ge1xyXG4gICAgICAgIGNvbnN0IHJlc3BvbnNlOiBDbGllbnROYW1lSW5mb1tdID0gW107XHJcbiAgICAgICAgY29uc3QgcmVxdWVzdCA9IFtdO1xyXG4gICAgICAgIGNvbnN0IHVuaXF1ZV9jbGRiaWQgPSBuZXcgU2V0KF9jbGRiaWQpO1xyXG4gICAgICAgIGlmKCF1bmlxdWVfY2xkYmlkLnNpemUpIHJldHVybiBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgdW5pcXVlX2NsZGJpZF9yZXNvbHZlcnM6IHtbZGJpZDogbnVtYmVyXTogKHJlc29sdmVkOiBDbGllbnROYW1lSW5mbykgPT4gYW55fSA9IHt9O1xyXG5cclxuXHJcbiAgICAgICAgZm9yKGNvbnN0IGNsZGJpZCBvZiB1bmlxdWVfY2xkYmlkKSB7XHJcbiAgICAgICAgICAgIHJlcXVlc3QucHVzaCh7J2NsZGJpZCc6IGNsZGJpZH0pO1xyXG4gICAgICAgICAgICAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbY2xkYmlkXSB8fCAodGhpcy5fYXdhaXRlcnNfdW5pcXVlX2RiaWRbY2xkYmlkXSA9IFtdKSlcclxuICAgICAgICAgICAgICAgIC5wdXNoKHVuaXF1ZV9jbGRiaWRfcmVzb2x2ZXJzW2NsZGJpZF0gPSBpbmZvID0+IHJlc3BvbnNlLnB1c2goaW5mbykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImNsaWVudGdldG5hbWVmcm9tZGJpZFwiLCByZXF1ZXN0KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdCAmJiBlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgLyogbm90aGluZyAqL1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGZpbmFsbHkge1xyXG4gICAgICAgICAgICAvKiBjbGVhbnVwICovXHJcbiAgICAgICAgICAgIGZvcihjb25zdCBjbGRiaWQgb2YgT2JqZWN0LmtleXModW5pcXVlX2NsZGJpZF9yZXNvbHZlcnMpKVxyXG4gICAgICAgICAgICAgICAgKHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9kYmlkW2NsZGJpZF0gfHwgW10pLnJlbW92ZSh1bmlxdWVfY2xkYmlkX3Jlc29sdmVyc1tjbGRiaWRdKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9ub3RpZnljbGllbnRuYW1lZnJvbXVpZChqc29uOiBhbnlbXSkge1xyXG4gICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm86IENsaWVudE5hbWVJbmZvID0ge1xyXG4gICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZDogZW50cnlbXCJjbHVpZFwiXSxcclxuICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbG5hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IHBhcnNlSW50KGVudHJ5W1wiY2xkYmlkXCJdKVxyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgZnVuY3Rpb25zID0gdGhpcy5fYXdhaXRlcnNfdW5pcXVlX2lkc1tlbnRyeVtcImNsdWlkXCJdXSB8fCBbXTtcclxuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2F3YWl0ZXJzX3VuaXF1ZV9pZHNbZW50cnlbXCJjbHVpZFwiXV07XHJcblxyXG4gICAgICAgICAgICBmb3IoY29uc3QgZm4gb2YgZnVuY3Rpb25zKVxyXG4gICAgICAgICAgICAgICAgZm4oaW5mbyk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcXVlcnlfbGlzdChzZXJ2ZXJfaWQ6IG51bWJlciA9IHVuZGVmaW5lZCkgOiBQcm9taXNlPFF1ZXJ5TGlzdD4ge1xyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxRdWVyeUxpc3Q+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBjb21tYW5kOiBcIm5vdGlmeXF1ZXJ5bGlzdFwiLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGNvbW1hbmQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGpzb24gPSBjb21tYW5kLmFyZ3VtZW50cztcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0ge30gYXMgUXVlcnlMaXN0O1xyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQuZmxhZ19hbGwgPSBqc29uWzBdW1wiZmxhZ19hbGxcIl07XHJcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LmZsYWdfb3duID0ganNvblswXVtcImZsYWdfb3duXCJdO1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdC5xdWVyaWVzID0gW107XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGZvcihjb25zdCBlbnRyeSBvZiBqc29uKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlbnRyeSA9IHt9IGFzIFF1ZXJ5TGlzdEVudHJ5O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkuYm91bmRlZF9zZXJ2ZXIgPSBwYXJzZUludChlbnRyeVtcImNsaWVudF9ib3VuZF9zZXJ2ZXJcIl0pO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkudXNlcm5hbWUgPSBlbnRyeVtcImNsaWVudF9sb2dpbl9uYW1lXCJdO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW50cnkudW5pcXVlX2lkID0gZW50cnlbXCJjbGllbnRfdW5pcXVlX2lkZW50aWZpZXJcIl07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQucXVlcmllcy5wdXNoKHJlbnRyeSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3VsdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgIGxldCBkYXRhID0ge307XHJcbiAgICAgICAgICAgIGlmKHNlcnZlcl9pZCAhPT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICAgICAgZGF0YVtcInNlcnZlcl9pZFwiXSA9IHNlcnZlcl9pZDtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJxdWVyeWxpc3RcIiwgZGF0YSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVtb3ZlX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICByZXF1ZXN0X3BsYXlsaXN0X2xpc3QoKSA6IFByb21pc2U8UGxheWxpc3RbXT4ge1xyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IHNpbmdsZV9oYW5kbGVyOiBTaW5nbGVDb21tYW5kSGFuZGxlciA9IHtcclxuICAgICAgICAgICAgICAgIGNvbW1hbmQ6IFwibm90aWZ5cGxheWxpc3RsaXN0XCIsXHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QganNvbiA9IGNvbW1hbmQuYXJndW1lbnRzO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdDogUGxheWxpc3RbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2lkOiBwYXJzZUludChlbnRyeVtcInBsYXlsaXN0X2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF9ib3RfaWQ6IHBhcnNlSW50KGVudHJ5W1wicGxheWxpc3RfYm90X2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90aXRsZTogZW50cnlbXCJwbGF5bGlzdF90aXRsZVwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90eXBlOiBwYXJzZUludChlbnRyeVtcInBsYXlsaXN0X3R5cGVcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X293bmVyX2RiaWQ6IHBhcnNlSW50KGVudHJ5W1wicGxheWxpc3Rfb3duZXJfZGJpZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfbmFtZTogZW50cnlbXCJwbGF5bGlzdF9vd25lcl9uYW1lXCJdLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWVkZWRfcG93ZXJfbW9kaWZ5OiBwYXJzZUludChlbnRyeVtcIm5lZWRlZF9wb3dlcl9tb2RpZnlcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lZWRlZF9wb3dlcl9wZXJtaXNzaW9uX21vZGlmeTogcGFyc2VJbnQoZW50cnlbXCJuZWVkZWRfcG93ZXJfcGVybWlzc2lvbl9tb2RpZnlcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lZWRlZF9wb3dlcl9kZWxldGU6IHBhcnNlSW50KGVudHJ5W1wibmVlZGVkX3Bvd2VyX2RlbGV0ZVwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVlZGVkX3Bvd2VyX3NvbmdfYWRkOiBwYXJzZUludChlbnRyeVtcIm5lZWRlZF9wb3dlcl9zb25nX2FkZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVlZGVkX3Bvd2VyX3NvbmdfbW92ZTogcGFyc2VJbnQoZW50cnlbXCJuZWVkZWRfcG93ZXJfc29uZ19tb3ZlXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWVkZWRfcG93ZXJfc29uZ19yZW1vdmU6IHBhcnNlSW50KGVudHJ5W1wibmVlZGVkX3Bvd2VyX3NvbmdfcmVtb3ZlXCJdKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIkZhaWxlZCB0byBwYXJzZSBwbGF5bGlzdCBlbnRyeTogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZWdpc3Rlcl9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwicGxheWxpc3RsaXN0XCIpLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBDb21tYW5kUmVzdWx0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoZXJyb3IuaWQgPT0gRXJyb3JJRC5FTVBUWV9SRVNVTFQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShbXSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcGxheWxpc3Rfc29uZ3MocGxheWxpc3RfaWQ6IG51bWJlcikgOiBQcm9taXNlPFBsYXlsaXN0U29uZ1tdPiB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlwbGF5bGlzdHNvbmdsaXN0XCIsXHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QganNvbiA9IGNvbW1hbmQuYXJndW1lbnRzO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZihqc29uWzBdW1wicGxheWxpc3RfaWRcIl0gIT0gcGxheWxpc3RfaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5Lk5FVFdPUktJTkcsIHRyKFwiUmVjZWl2ZWQgaW52YWxpZCBub3RpZmljYXRpb24gZm9yIHBsYXlsaXN0IHNvbmdzXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBQbGF5bGlzdFNvbmdbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvbmdfaWQ6IHBhcnNlSW50KGVudHJ5W1wic29uZ19pZFwiXSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29uZ19pbnZva2VyOiBlbnRyeVtcInNvbmdfaW52b2tlclwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3ByZXZpb3VzX3NvbmdfaWQ6IHBhcnNlSW50KGVudHJ5W1wic29uZ19wcmV2aW91c19zb25nX2lkXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3VybDogZW50cnlbXCJzb25nX3VybFwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX3VybF9sb2FkZXI6IGVudHJ5W1wic29uZ191cmxfbG9hZGVyXCJdLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb25nX2xvYWRlZDogZW50cnlbXCJzb25nX2xvYWRlZFwiXSA9PSB0cnVlIHx8IGVudHJ5W1wic29uZ19sb2FkZWRcIl0gPT0gXCIxXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29uZ19tZXRhZGF0YTogZW50cnlbXCJzb25nX21ldGFkYXRhXCJdXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5Lk5FVFdPUktJTkcsIHRyKFwiRmFpbGVkIHRvIHBhcnNlIHBsYXlsaXN0IHNvbmcgZW50cnk6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcInBsYXlsaXN0c29uZ2xpc3RcIiwge3BsYXlsaXN0X2lkOiBwbGF5bGlzdF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZihlcnJvci5pZCA9PSBFcnJvcklELkVNUFRZX1JFU1VMVCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKFtdKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmVxdWVzdF9wbGF5bGlzdF9jbGllbnRfbGlzdChwbGF5bGlzdF9pZDogbnVtYmVyKSA6IFByb21pc2U8bnVtYmVyW10+IHtcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBzaW5nbGVfaGFuZGxlcjogU2luZ2xlQ29tbWFuZEhhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBjb21tYW5kOiBcIm5vdGlmeXBsYXlsaXN0Y2xpZW50bGlzdFwiLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGNvbW1hbmQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGpzb24gPSBjb21tYW5kLmFyZ3VtZW50cztcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYoanNvblswXVtcInBsYXlsaXN0X2lkXCJdICE9IHBsYXlsaXN0X2lkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIlJlY2VpdmVkIGludmFsaWQgbm90aWZpY2F0aW9uIGZvciBwbGF5bGlzdCBjbGllbnRzXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzdWx0OiBudW1iZXJbXSA9IFtdO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YganNvbilcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2gocGFyc2VJbnQoZW50cnlbXCJjbGRiaWRcIl0pKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShyZXN1bHQuZmlsdGVyKGUgPT4gIWlzTmFOKGUpKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kX2NvbW1hbmQoXCJwbGF5bGlzdGNsaWVudGxpc3RcIiwge3BsYXlsaXN0X2lkOiBwbGF5bGlzdF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQgJiYgZXJyb3IuaWQgPT0gRXJyb3JJRC5FTVBUWV9SRVNVTFQpIHtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKFtdKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIHJlcXVlc3RfY2xpZW50c19ieV9zZXJ2ZXJfZ3JvdXAoZ3JvdXBfaWQ6IG51bWJlcikgOiBQcm9taXNlPFNlcnZlckdyb3VwQ2xpZW50W10+IHtcclxuICAgICAgICAvL3NlcnZlcmdyb3VwY2xpZW50bGlzdCBzZ2lkPTJcclxuICAgICAgICAvL25vdGlmeXNlcnZlcmdyb3VwY2xpZW50bGlzdCBzZ2lkPTYgY2xkYmlkPTIgY2xpZW50X25pY2tuYW1lPVdvbHZlcmluREVWIGNsaWVudF91bmlxdWVfaWRlbnRpZmllcj14eGpuYzE0TG12VGsrTHlybThPT2VvNHRPcXc9XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPFNlcnZlckdyb3VwQ2xpZW50W10+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlzZXJ2ZXJncm91cGNsaWVudGxpc3RcIixcclxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uOiBjb21tYW5kID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoY29tbWFuZC5hcmd1bWVudHNbMF1bXCJzZ2lkXCJdICE9IGdyb3VwX2lkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5ORVRXT1JLSU5HLCB0cihcIlJlY2VpdmVkIGludmFsaWQgbm90aWZpY2F0aW9uIGZvciBzZXJ2ZXIgZ3JvdXAgY2xpZW50IGxpc3RcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQ6IFNlcnZlckdyb3VwQ2xpZW50W10gPSBbXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgZm9yKGNvbnN0IGVudHJ5IG9mIGNvbW1hbmQuYXJndW1lbnRzKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudF9kYXRhYmFzZV9pZDogcGFyc2VJbnQoZW50cnlbXCJjbGRiaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaWVudF9uaWNrbmFtZTogZW50cnlbXCJjbGllbnRfbmlja25hbWVcIl0sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpZW50X3VuaXF1ZV9pZGVudGlmaWVyOiBlbnRyeVtcImNsaWVudF91bmlxdWVfaWRlbnRpZmllclwiXVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJGYWlsZWQgdG8gcGFyc2Ugc2VydmVyIGdyb3VwIGNsaWVudCBsaXN0OiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJmYWlsZWQgdG8gcGFyc2UgaW5mb1wiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZWdpc3Rlcl9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwic2VydmVyZ3JvdXBjbGllbnRsaXN0XCIsIHtzZ2lkOiBncm91cF9pZH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlcl9ib3NzLnJlbW92ZV9zaW5nbGVfaGFuZGxlcihzaW5nbGVfaGFuZGxlcik7XHJcbiAgICAgICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHJlcXVlc3RfcGxheWxpc3RfaW5mbyhwbGF5bGlzdF9pZDogbnVtYmVyKSA6IFByb21pc2U8UGxheWxpc3RJbmZvPiB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3Qgc2luZ2xlX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyID0ge1xyXG4gICAgICAgICAgICAgICAgY29tbWFuZDogXCJub3RpZnlwbGF5bGlzdGluZm9cIixcclxuICAgICAgICAgICAgICAgIGZ1bmN0aW9uOiBjb21tYW5kID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBqc29uID0gY29tbWFuZC5hcmd1bWVudHNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGpzb25bXCJwbGF5bGlzdF9pZFwiXSAhPSBwbGF5bGlzdF9pZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJSZWNlaXZlZCBpbnZhbGlkIG5vdGlmaWNhdGlvbiBmb3IgcGxheWxpc3QgaW5mb1wiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vcmVzb2x2ZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2lkOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfdGl0bGU6IGpzb25bXCJwbGF5bGlzdF90aXRsZVwiXSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2Rlc2NyaXB0aW9uOiBqc29uW1wicGxheWxpc3RfZGVzY3JpcHRpb25cIl0sXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF90eXBlOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfdHlwZVwiXSksXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfZGJpZDogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X293bmVyX2RiaWRcIl0pLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3Rfb3duZXJfbmFtZToganNvbltcInBsYXlsaXN0X293bmVyX25hbWVcIl0sXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfZmxhZ19kZWxldGVfcGxheWVkOiBqc29uW1wicGxheWxpc3RfZmxhZ19kZWxldGVfcGxheWVkXCJdID09IHRydWUgfHwganNvbltcInBsYXlsaXN0X2ZsYWdfZGVsZXRlX3BsYXllZFwiXSA9PSBcIjFcIixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2ZsYWdfZmluaXNoZWQ6IGpzb25bXCJwbGF5bGlzdF9mbGFnX2ZpbmlzaGVkXCJdID09IHRydWUgfHwganNvbltcInBsYXlsaXN0X2ZsYWdfZmluaXNoZWRcIl0gPT0gXCIxXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF5bGlzdF9yZXBsYXlfbW9kZTogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X3JlcGxheV9tb2RlXCJdKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZDogcGFyc2VJbnQoanNvbltcInBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZFwiXSksXHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxheWxpc3RfbWF4X3NvbmdzOiBwYXJzZUludChqc29uW1wicGxheWxpc3RfbWF4X3NvbmdzXCJdKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuTkVUV09SS0lORywgdHIoXCJGYWlsZWQgdG8gcGFyc2UgcGxheWxpc3QgaW5mbzogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiZmFpbGVkIHRvIHBhcnNlIGluZm9cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcInBsYXlsaXN0aW5mb1wiLCB7cGxheWxpc3RfaWQ6IHBsYXlsaXN0X2lkfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVtb3ZlX3NpbmdsZV9oYW5kbGVyKHNpbmdsZV9oYW5kbGVyKTtcclxuICAgICAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgIH0pXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBAZGVwcmVjYXRlZFxyXG4gICAgICogIEl0cyBqdXN0IGEgd29ya2Fyb3VuZCBmb3IgdGhlIHF1ZXJ5IG1hbmFnZW1lbnQuXHJcbiAgICAgKiAgVGhlcmUgaXMgbm8gZ2FyYW50ZSB0aGF0IHRoZSB3aG9hbWkgdHJpY2sgd2lsbCB3b3JrIGZvcmV2ZXJcclxuICAgICAqL1xyXG4gICAgY3VycmVudF92aXJ0dWFsX3NlcnZlcl9pZCgpIDogUHJvbWlzZTxudW1iZXI+IHtcclxuICAgICAgICBpZih0aGlzLl93aG9fYW1faSlcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShwYXJzZUludCh0aGlzLl93aG9fYW1faVtcInZpcnR1YWxzZXJ2ZXJfaWRcIl0pKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPG51bWJlcj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBzaW5nbGVfaGFuZGxlcjogU2luZ2xlQ29tbWFuZEhhbmRsZXIgPSB7XHJcbiAgICAgICAgICAgICAgICBmdW5jdGlvbjogY29tbWFuZCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoY29tbWFuZC5jb21tYW5kICE9IFwiXCIgJiYgY29tbWFuZC5jb21tYW5kLmluZGV4T2YoXCI9XCIpID09IC0xKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3dob19hbV9pID0gY29tbWFuZC5hcmd1bWVudHNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShwYXJzZUludCh0aGlzLl93aG9fYW1faVtcInZpcnR1YWxzZXJ2ZXJfaWRcIl0pKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgdGhpcy5oYW5kbGVyX2Jvc3MucmVnaXN0ZXJfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcIndob2FtaVwiKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZXJfYm9zcy5yZW1vdmVfc2luZ2xlX2hhbmRsZXIoc2luZ2xlX2hhbmRsZXIpO1xyXG4gICAgICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbn0iLCJpbXBvcnQge0Nvbm5lY3Rpb25IYW5kbGVyLCBDb25uZWN0aW9uU3RhdGV9IGZyb20gXCIuLi9Db25uZWN0aW9uSGFuZGxlclwiO1xyXG5pbXBvcnQge1NlcnZlckFkZHJlc3N9IGZyb20gXCIuLi9jaGFubmVsLXRyZWUvc2VydmVyXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7UmVjb3JkZXJQcm9maWxlfSBmcm9tIFwiLi4vdm9pY2UvUmVjb3JkZXJQcm9maWxlXCI7XHJcbmltcG9ydCB7Q29tbWFuZEhlbHBlcn0gZnJvbSBcIi4vQ29tbWFuZEhlbHBlclwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuaW1wb3J0IEhhbmRzaGFrZUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUhhbmRsZXI7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIENvbW1hbmRPcHRpb25zIHtcclxuICAgIGZsYWdzZXQ/OiBzdHJpbmdbXTsgLyogZGVmYXVsdDogW10gKi9cclxuICAgIHByb2Nlc3NfcmVzdWx0PzogYm9vbGVhbjsgLyogZGVmYXVsdDogdHJ1ZSAqL1xyXG5cclxuICAgIHRpbWVvdXQ/OiBudW1iZXIgLyogZGVmYXVsdDogMTAwMCAqLztcclxufVxyXG5leHBvcnQgY29uc3QgQ29tbWFuZE9wdGlvbkRlZmF1bHRzOiBDb21tYW5kT3B0aW9ucyA9IHtcclxuICAgIGZsYWdzZXQ6IFtdLFxyXG4gICAgcHJvY2Vzc19yZXN1bHQ6IHRydWUsXHJcbiAgICB0aW1lb3V0OiAxMDAwXHJcbn07XHJcblxyXG5leHBvcnQgdHlwZSBDb25uZWN0aW9uU3RhdGVMaXN0ZW5lciA9IChvbGRfc3RhdGU6IENvbm5lY3Rpb25TdGF0ZSwgbmV3X3N0YXRlOiBDb25uZWN0aW9uU3RhdGUpID0+IGFueTtcclxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiB7XHJcbiAgICByZWFkb25seSBjbGllbnQ6IENvbm5lY3Rpb25IYW5kbGVyO1xyXG4gICAgcmVhZG9ubHkgY29tbWFuZF9oZWxwZXI6IENvbW1hbmRIZWxwZXI7XHJcblxyXG4gICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGNsaWVudDogQ29ubmVjdGlvbkhhbmRsZXIpIHtcclxuICAgICAgICB0aGlzLmNsaWVudCA9IGNsaWVudDtcclxuXHJcbiAgICAgICAgdGhpcy5jb21tYW5kX2hlbHBlciA9IG5ldyBDb21tYW5kSGVscGVyKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qIHJlc29sdmVkIGFzIHNvb24gYSBjb25uZWN0aW9uIGhhcyBiZWVuIGVzdGFibGlzaGVkLiBUaGlzIGRvZXMgbm90IG1lYW5zIHRoYXQgdGhlIGF1dGhlbnRpY2F0aW9uIGhhZCB5ZXQgYmVlbiBkb25lISAqL1xyXG4gICAgYWJzdHJhY3QgY29ubmVjdChhZGRyZXNzOiBTZXJ2ZXJBZGRyZXNzLCBoYW5kc2hha2U6IEhhbmRzaGFrZUhhbmRsZXIsIHRpbWVvdXQ/OiBudW1iZXIpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICBhYnN0cmFjdCBjb25uZWN0ZWQoKSA6IGJvb2xlYW47XHJcbiAgICBhYnN0cmFjdCBkaXNjb25uZWN0KHJlYXNvbj86IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+O1xyXG5cclxuICAgIGFic3RyYWN0IHN1cHBvcnRfdm9pY2UoKSA6IGJvb2xlYW47XHJcbiAgICBhYnN0cmFjdCB2b2ljZV9jb25uZWN0aW9uKCkgOiB2b2ljZS5BYnN0cmFjdFZvaWNlQ29ubmVjdGlvbiB8IHVuZGVmaW5lZDtcclxuXHJcbiAgICBhYnN0cmFjdCBjb21tYW5kX2hhbmRsZXJfYm9zcygpIDogQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3M7XHJcbiAgICBhYnN0cmFjdCBzZW5kX2NvbW1hbmQoY29tbWFuZDogc3RyaW5nLCBkYXRhPzogYW55IHwgYW55W10sIG9wdGlvbnM/OiBDb21tYW5kT3B0aW9ucykgOiBQcm9taXNlPENvbW1hbmRSZXN1bHQ+O1xyXG5cclxuICAgIGFic3RyYWN0IGdldCBvbmNvbm5lY3Rpb25zdGF0ZWNoYW5nZWQoKSA6IENvbm5lY3Rpb25TdGF0ZUxpc3RlbmVyO1xyXG4gICAgYWJzdHJhY3Qgc2V0IG9uY29ubmVjdGlvbnN0YXRlY2hhbmdlZChsaXN0ZW5lcjogQ29ubmVjdGlvblN0YXRlTGlzdGVuZXIpO1xyXG5cclxuICAgIGFic3RyYWN0IHJlbW90ZV9hZGRyZXNzKCkgOiBTZXJ2ZXJBZGRyZXNzOyAvKiBvbmx5IHZhbGlkIHdoZW4gY29ubmVjdGVkICovXHJcbiAgICBhYnN0cmFjdCBoYW5kc2hha2VfaGFuZGxlcigpIDogSGFuZHNoYWtlSGFuZGxlcjsgLyogb25seSB2YWxpZCB3aGVuIGNvbm5lY3RlZCAqL1xyXG5cclxuICAgIGFic3RyYWN0IHBpbmcoKSA6IHtcclxuICAgICAgICBuYXRpdmU6IG51bWJlcixcclxuICAgICAgICBqYXZhc2NyaXB0PzogbnVtYmVyXHJcbiAgICB9O1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHZvaWNlIHtcclxuICAgIGV4cG9ydCBlbnVtIFBsYXllclN0YXRlIHtcclxuICAgICAgICBQUkVCVUZGRVJJTkcsXHJcbiAgICAgICAgUExBWUlORyxcclxuICAgICAgICBCVUZGRVJJTkcsXHJcbiAgICAgICAgU1RPUFBJTkcsXHJcbiAgICAgICAgU1RPUFBFRFxyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCB0eXBlIExhdGVuY3lTZXR0aW5ncyA9IHtcclxuICAgICAgICBtaW5fYnVmZmVyOiBudW1iZXI7IC8qIG1pbGxpc2Vjb25kcyAqL1xyXG4gICAgICAgIG1heF9idWZmZXI6IG51bWJlcjsgLyogbWlsbGlzZWNvbmRzICovXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBWb2ljZUNsaWVudCB7XHJcbiAgICAgICAgY2xpZW50X2lkOiBudW1iZXI7XHJcblxyXG4gICAgICAgIGNhbGxiYWNrX3BsYXliYWNrOiAoKSA9PiBhbnk7XHJcbiAgICAgICAgY2FsbGJhY2tfc3RvcHBlZDogKCkgPT4gYW55O1xyXG5cclxuICAgICAgICBjYWxsYmFja19zdGF0ZV9jaGFuZ2VkOiAobmV3X3N0YXRlOiBQbGF5ZXJTdGF0ZSkgPT4gYW55O1xyXG5cclxuICAgICAgICBnZXRfc3RhdGUoKSA6IFBsYXllclN0YXRlO1xyXG5cclxuICAgICAgICBnZXRfdm9sdW1lKCkgOiBudW1iZXI7XHJcbiAgICAgICAgc2V0X3ZvbHVtZSh2b2x1bWU6IG51bWJlcikgOiB2b2lkO1xyXG5cclxuICAgICAgICBhYm9ydF9yZXBsYXkoKTtcclxuXHJcbiAgICAgICAgc3VwcG9ydF9sYXRlbmN5X3NldHRpbmdzKCkgOiBib29sZWFuO1xyXG5cclxuICAgICAgICByZXNldF9sYXRlbmN5X3NldHRpbmdzKCk7XHJcbiAgICAgICAgbGF0ZW5jeV9zZXR0aW5ncyhzZXR0aW5ncz86IExhdGVuY3lTZXR0aW5ncykgOiBMYXRlbmN5U2V0dGluZ3M7XHJcblxyXG4gICAgICAgIHN1cHBvcnRfZmx1c2goKSA6IGJvb2xlYW47XHJcbiAgICAgICAgZmx1c2goKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RWb2ljZUNvbm5lY3Rpb24ge1xyXG4gICAgICAgIHJlYWRvbmx5IGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbjtcclxuXHJcbiAgICAgICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgYWJzdHJhY3QgY29ubmVjdGVkKCkgOiBib29sZWFuO1xyXG4gICAgICAgIGFic3RyYWN0IGVuY29kaW5nX3N1cHBvcnRlZChjb2RlYzogbnVtYmVyKSA6IGJvb2xlYW47XHJcbiAgICAgICAgYWJzdHJhY3QgZGVjb2Rpbmdfc3VwcG9ydGVkKGNvZGVjOiBudW1iZXIpIDogYm9vbGVhbjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3QgcmVnaXN0ZXJfY2xpZW50KGNsaWVudF9pZDogbnVtYmVyKSA6IFZvaWNlQ2xpZW50O1xyXG4gICAgICAgIGFic3RyYWN0IGF2YWlsYWJsZV9jbGllbnRzKCkgOiBWb2ljZUNsaWVudFtdO1xyXG4gICAgICAgIGFic3RyYWN0IHVucmVnaXN0ZXJfY2xpZW50KGNsaWVudDogVm9pY2VDbGllbnQpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3Qgdm9pY2VfcmVjb3JkZXIoKSA6IFJlY29yZGVyUHJvZmlsZTtcclxuICAgICAgICBhYnN0cmFjdCBhY3F1aXJlX3ZvaWNlX3JlY29yZGVyKHJlY29yZGVyOiBSZWNvcmRlclByb2ZpbGUgfCB1bmRlZmluZWQpIDogUHJvbWlzZTx2b2lkPjtcclxuXHJcbiAgICAgICAgYWJzdHJhY3QgZ2V0X2VuY29kZXJfY29kZWMoKSA6IG51bWJlcjtcclxuICAgICAgICBhYnN0cmFjdCBzZXRfZW5jb2Rlcl9jb2RlYyhjb2RlYzogbnVtYmVyKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFNlcnZlckNvbW1hbmQge1xyXG4gICAgY29tbWFuZDogc3RyaW5nO1xyXG4gICAgYXJndW1lbnRzOiBhbnlbXTtcclxufVxyXG5cclxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEFic3RyYWN0Q29tbWFuZEhhbmRsZXIge1xyXG4gICAgcmVhZG9ubHkgY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uO1xyXG5cclxuICAgIGhhbmRsZXJfYm9zczogQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3MgfCB1bmRlZmluZWQ7XHJcbiAgICB2b2xhdGlsZV9oYW5kbGVyX2Jvc3M6IGJvb2xlYW4gPSBmYWxzZTsgLyogaWYgdHJ1ZSB0aGFuIHRoZSBjb21tYW5kIGhhbmRsZXIgY291bGQgYmUgcmVnaXN0ZXJlZCB0d2ljZSB0byB0d28gb3IgbW9yZSBoYW5kbGVycyAqL1xyXG5cclxuICAgIGlnbm9yZV9jb25zdW1lZDogYm9vbGVhbiA9IGZhbHNlO1xyXG5cclxuICAgIHByb3RlY3RlZCBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogQHJldHVybiBJZiB0aGUgY29tbWFuZCBzaG91bGQgYmUgY29uc3VtZWRcclxuICAgICAqL1xyXG4gICAgYWJzdHJhY3QgaGFuZGxlX2NvbW1hbmQoY29tbWFuZDogU2VydmVyQ29tbWFuZCkgOiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNpbmdsZUNvbW1hbmRIYW5kbGVyIHtcclxuICAgIG5hbWU/OiBzdHJpbmc7XHJcbiAgICBjb21tYW5kPzogc3RyaW5nO1xyXG4gICAgdGltZW91dD86IG51bWJlcjtcclxuXHJcbiAgICAvKiBpZiB0aGUgcmV0dXJuIGlzIHRydWUgdGhlbiB0aGUgY29tbWFuZCBoYW5kbGVyIHdpbGwgYmUgcmVtb3ZlZCAqL1xyXG4gICAgZnVuY3Rpb246IChjb21tYW5kOiBTZXJ2ZXJDb21tYW5kKSA9PiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RDb21tYW5kSGFuZGxlckJvc3Mge1xyXG4gICAgcmVhZG9ubHkgY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uO1xyXG4gICAgcHJvdGVjdGVkIGNvbW1hbmRfaGFuZGxlcnM6IEFic3RyYWN0Q29tbWFuZEhhbmRsZXJbXSA9IFtdO1xyXG4gICAgLyogVE9ETzogVGltZW91dCAqL1xyXG4gICAgcHJvdGVjdGVkIHNpbmdsZV9jb21tYW5kX2hhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyW10gPSBbXTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycyA9IHVuZGVmaW5lZDtcclxuICAgICAgICB0aGlzLnNpbmdsZV9jb21tYW5kX2hhbmRsZXIgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgcmVnaXN0ZXJfaGFuZGxlcihoYW5kbGVyOiBBYnN0cmFjdENvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgaWYoIWhhbmRsZXIudm9sYXRpbGVfaGFuZGxlcl9ib3NzICYmIGhhbmRsZXIuaGFuZGxlcl9ib3NzKVxyXG4gICAgICAgICAgICB0aHJvdyBcImhhbmRsZXIgYWxyZWFkeSByZWdpc3RlcmVkXCI7XHJcblxyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5yZW1vdmUoaGFuZGxlcik7IC8qIGp1c3QgdG8gYmUgc3VyZSAqL1xyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5wdXNoKGhhbmRsZXIpO1xyXG4gICAgICAgIGhhbmRsZXIuaGFuZGxlcl9ib3NzID0gdGhpcztcclxuICAgIH1cclxuXHJcbiAgICB1bnJlZ2lzdGVyX2hhbmRsZXIoaGFuZGxlcjogQWJzdHJhY3RDb21tYW5kSGFuZGxlcikge1xyXG4gICAgICAgIGlmKCFoYW5kbGVyLnZvbGF0aWxlX2hhbmRsZXJfYm9zcyAmJiBoYW5kbGVyLmhhbmRsZXJfYm9zcyAhPT0gdGhpcykge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJUcmllZCB0byB1bnJlZ2lzdGVyIGNvbW1hbmQgaGFuZGxlciB3aGljaCBkb2VzIG5vdCBiZWxvbmcgdG8gdGhlIGhhbmRsZXIgYm9zc1wiKSk7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuY29tbWFuZF9oYW5kbGVycy5yZW1vdmUoaGFuZGxlcik7XHJcbiAgICAgICAgaGFuZGxlci5oYW5kbGVyX2Jvc3MgPSB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG5cclxuICAgIHJlZ2lzdGVyX3NpbmdsZV9oYW5kbGVyKGhhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgdGhpcy5zaW5nbGVfY29tbWFuZF9oYW5kbGVyLnB1c2goaGFuZGxlcik7XHJcbiAgICB9XHJcblxyXG4gICAgcmVtb3ZlX3NpbmdsZV9oYW5kbGVyKGhhbmRsZXI6IFNpbmdsZUNvbW1hbmRIYW5kbGVyKSB7XHJcbiAgICAgICAgdGhpcy5zaW5nbGVfY29tbWFuZF9oYW5kbGVyLnJlbW92ZShoYW5kbGVyKTtcclxuICAgIH1cclxuXHJcbiAgICBoYW5kbGVycygpIDogQWJzdHJhY3RDb21tYW5kSGFuZGxlcltdIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5jb21tYW5kX2hhbmRsZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIGludm9rZV9oYW5kbGUoY29tbWFuZDogU2VydmVyQ29tbWFuZCkgOiBib29sZWFuIHtcclxuICAgICAgICBsZXQgZmxhZ19jb25zdW1lZCA9IGZhbHNlO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgaGFuZGxlciBvZiB0aGlzLmNvbW1hbmRfaGFuZGxlcnMpIHtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmKCFmbGFnX2NvbnN1bWVkIHx8IGhhbmRsZXIuaWdub3JlX2NvbnN1bWVkKVxyXG4gICAgICAgICAgICAgICAgICAgIGZsYWdfY29uc3VtZWQgPSBmbGFnX2NvbnN1bWVkIHx8IGhhbmRsZXIuaGFuZGxlX2NvbW1hbmQoY29tbWFuZCk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW52b2tlIGNvbW1hbmQgaGFuZGxlci4gSW52b2NhdGlvbiByZXN1bHRzIGluIGFuIGV4Y2VwdGlvbjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yKGNvbnN0IGhhbmRsZXIgb2YgWy4uLnRoaXMuc2luZ2xlX2NvbW1hbmRfaGFuZGxlcl0pIHtcclxuICAgICAgICAgICAgaWYoaGFuZGxlci5jb21tYW5kICYmIGhhbmRsZXIuY29tbWFuZCAhPSBjb21tYW5kLmNvbW1hbmQpXHJcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcclxuXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBpZihoYW5kbGVyLmZ1bmN0aW9uKGNvbW1hbmQpKVxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2luZ2xlX2NvbW1hbmRfaGFuZGxlci5yZW1vdmUoaGFuZGxlcik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW52b2tlIHNpbmdsZSBjb21tYW5kIGhhbmRsZXIuIEludm9jYXRpb24gcmVzdWx0cyBpbiBhbiBleGNlcHRpb246ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmbGFnX2NvbnN1bWVkO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtMYXRlclByb21pc2V9IGZyb20gXCIuLi91dGlscy9oZWxwZXJzXCI7XHJcblxyXG5leHBvcnQgZW51bSBFcnJvcklEIHtcclxuICAgIE5PVF9JTVBMRU1FTlRFRCA9IDB4MixcclxuICAgIENPTU1BTkRfTk9UX0ZPVU5EID0gMHgxMDAsXHJcblxyXG4gICAgUEVSTUlTU0lPTl9FUlJPUiA9IDI1NjgsXHJcbiAgICBFTVBUWV9SRVNVTFQgPSAweDA1MDEsXHJcbiAgICBQTEFZTElTVF9JU19JTl9VU0UgPSAweDIxMDMsXHJcblxyXG4gICAgRklMRV9BTFJFQURZX0VYSVNUUyA9IDIwNTAsXHJcblxyXG4gICAgQ0xJRU5UX0lOVkFMSURfSUQgPSAweDAyMDAsXHJcblxyXG4gICAgQ09OVkVSU0FUSU9OX0lOVkFMSURfSUQgPSAweDIyMDAsXHJcbiAgICBDT05WRVJTQVRJT05fTU9SRV9EQVRBID0gMHgyMjAxLFxyXG4gICAgQ09OVkVSU0FUSU9OX0lTX1BSSVZBVEUgPSAweDIyMDJcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIENvbW1hbmRSZXN1bHQge1xyXG4gICAgc3VjY2VzczogYm9vbGVhbjtcclxuICAgIGlkOiBudW1iZXI7XHJcbiAgICBtZXNzYWdlOiBzdHJpbmc7XHJcbiAgICBleHRyYV9tZXNzYWdlOiBzdHJpbmc7XHJcblxyXG4gICAganNvbjogYW55O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGpzb24pIHtcclxuICAgICAgICB0aGlzLmpzb24gPSBqc29uO1xyXG4gICAgICAgIHRoaXMuaWQgPSBwYXJzZUludChqc29uW1wiaWRcIl0pO1xyXG4gICAgICAgIHRoaXMubWVzc2FnZSA9IGpzb25bXCJtc2dcIl07XHJcblxyXG4gICAgICAgIHRoaXMuZXh0cmFfbWVzc2FnZSA9IFwiXCI7XHJcbiAgICAgICAgaWYoanNvbltcImV4dHJhX21zZ1wiXSkgdGhpcy5leHRyYV9tZXNzYWdlID0ganNvbltcImV4dHJhX21zZ1wiXTtcclxuXHJcbiAgICAgICAgdGhpcy5zdWNjZXNzID0gdGhpcy5pZCA9PSAwO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIENsaWVudE5hbWVJbmZvIHtcclxuICAgIC8vY2x1aWQ9dFl6S1VyeW5cXC9cXC9ZOFZCTWY4UEhVVDZCMWVpRT0gbmFtZT1FeHAgY2xuYW1lPUV4cCBjbGRiaWQ9OVxyXG4gICAgY2xpZW50X3VuaXF1ZV9pZDogc3RyaW5nO1xyXG4gICAgY2xpZW50X25pY2tuYW1lOiBzdHJpbmc7XHJcbiAgICBjbGllbnRfZGF0YWJhc2VfaWQ6IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDbGllbnROYW1lRnJvbVVpZCB7XHJcbiAgICBwcm9taXNlOiBMYXRlclByb21pc2U8Q2xpZW50TmFtZUluZm9bXT4sXHJcbiAgICBrZXlzOiBzdHJpbmdbXSxcclxuICAgIHJlc3BvbnNlOiBDbGllbnROYW1lSW5mb1tdXHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVyR3JvdXBDbGllbnQge1xyXG4gICAgY2xpZW50X25pY2tuYW1lOiBzdHJpbmc7XHJcbiAgICBjbGllbnRfdW5pcXVlX2lkZW50aWZpZXI6IHN0cmluZztcclxuICAgIGNsaWVudF9kYXRhYmFzZV9pZDogbnVtYmVyO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFF1ZXJ5TGlzdEVudHJ5IHtcclxuICAgIHVzZXJuYW1lOiBzdHJpbmc7XHJcbiAgICB1bmlxdWVfaWQ6IHN0cmluZztcclxuICAgIGJvdW5kZWRfc2VydmVyOiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUXVlcnlMaXN0IHtcclxuICAgIGZsYWdfb3duOiBib29sZWFuO1xyXG4gICAgZmxhZ19hbGw6IGJvb2xlYW47XHJcblxyXG4gICAgcXVlcmllczogUXVlcnlMaXN0RW50cnlbXTtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQbGF5bGlzdCB7XHJcbiAgICBwbGF5bGlzdF9pZDogbnVtYmVyO1xyXG4gICAgcGxheWxpc3RfYm90X2lkOiBudW1iZXI7XHJcbiAgICBwbGF5bGlzdF90aXRsZTogc3RyaW5nO1xyXG4gICAgcGxheWxpc3RfdHlwZTogbnVtYmVyO1xyXG4gICAgcGxheWxpc3Rfb3duZXJfZGJpZDogbnVtYmVyO1xyXG4gICAgcGxheWxpc3Rfb3duZXJfbmFtZTogc3RyaW5nO1xyXG5cclxuICAgIG5lZWRlZF9wb3dlcl9tb2RpZnk6IG51bWJlcjtcclxuICAgIG5lZWRlZF9wb3dlcl9wZXJtaXNzaW9uX21vZGlmeTogbnVtYmVyO1xyXG4gICAgbmVlZGVkX3Bvd2VyX2RlbGV0ZTogbnVtYmVyO1xyXG4gICAgbmVlZGVkX3Bvd2VyX3NvbmdfYWRkOiBudW1iZXI7XHJcbiAgICBuZWVkZWRfcG93ZXJfc29uZ19tb3ZlOiBudW1iZXI7XHJcbiAgICBuZWVkZWRfcG93ZXJfc29uZ19yZW1vdmU6IG51bWJlcjtcclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBQbGF5bGlzdEluZm8ge1xyXG4gICAgcGxheWxpc3RfaWQ6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X3RpdGxlOiBzdHJpbmcsXHJcbiAgICBwbGF5bGlzdF9kZXNjcmlwdGlvbjogc3RyaW5nLFxyXG4gICAgcGxheWxpc3RfdHlwZTogbnVtYmVyLFxyXG5cclxuICAgIHBsYXlsaXN0X293bmVyX2RiaWQ6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X293bmVyX25hbWU6IHN0cmluZyxcclxuXHJcbiAgICBwbGF5bGlzdF9mbGFnX2RlbGV0ZV9wbGF5ZWQ6IGJvb2xlYW4sXHJcbiAgICBwbGF5bGlzdF9mbGFnX2ZpbmlzaGVkOiBib29sZWFuLFxyXG4gICAgcGxheWxpc3RfcmVwbGF5X21vZGU6IG51bWJlcixcclxuICAgIHBsYXlsaXN0X2N1cnJlbnRfc29uZ19pZDogbnVtYmVyLFxyXG5cclxuICAgIHBsYXlsaXN0X21heF9zb25nczogbnVtYmVyXHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUGxheWxpc3RTb25nIHtcclxuICAgIHNvbmdfaWQ6IG51bWJlcjtcclxuICAgIHNvbmdfcHJldmlvdXNfc29uZ19pZDogbnVtYmVyO1xyXG4gICAgc29uZ19pbnZva2VyOiBzdHJpbmc7XHJcbiAgICBzb25nX3VybDogc3RyaW5nO1xyXG4gICAgc29uZ191cmxfbG9hZGVyOiBzdHJpbmc7XHJcbiAgICBzb25nX2xvYWRlZDogYm9vbGVhbjtcclxuICAgIHNvbmdfbWV0YWRhdGE6IHN0cmluZztcclxufSIsIi8vIEFTTi4xIEphdmFTY3JpcHQgZGVjb2RlclxyXG4vLyBDb3B5cmlnaHQgKGMpIDIwMDgtMjAxOCBMYXBvIEx1Y2hpbmkgPGxhcG9AbGFwby5pdD5cclxuLy8gQ29weXJpZ2h0IChjKSAyMDE5LTIwMTkgTWFya3VzIEhhZGVuZmVsZHQgPGdpdEB0ZWFzcGVhay5kZT5cclxuXHJcbi8vIFBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG4vLyBwdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQsIHByb3ZpZGVkIHRoYXQgdGhlIGFib3ZlXHJcbi8vIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2UgYXBwZWFyIGluIGFsbCBjb3BpZXMuXHJcbi8vXHJcbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIgQU5EIFRIRSBBVVRIT1IgRElTQ0xBSU1TIEFMTCBXQVJSQU5USUVTXHJcbi8vIFdJVEggUkVHQVJEIFRPIFRISVMgU09GVFdBUkUgSU5DTFVESU5HIEFMTCBJTVBMSUVEIFdBUlJBTlRJRVMgT0ZcclxuLy8gTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTLiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SIEJFIExJQUJMRSBGT1JcclxuLy8gQU5ZIFNQRUNJQUwsIERJUkVDVCwgSU5ESVJFQ1QsIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyBPUiBBTlkgREFNQUdFU1xyXG4vLyBXSEFUU09FVkVSIFJFU1VMVElORyBGUk9NIExPU1MgT0YgVVNFLCBEQVRBIE9SIFBST0ZJVFMsIFdIRVRIRVIgSU4gQU5cclxuLy8gQUNUSU9OIE9GIENPTlRSQUNULCBORUdMSUdFTkNFIE9SIE9USEVSIFRPUlRJT1VTIEFDVElPTiwgQVJJU0lORyBPVVQgT0ZcclxuLy8gT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBVU0UgT1IgUEVSRk9STUFOQ0UgT0YgVEhJUyBTT0ZUV0FSRS5cclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgYXNuMSB7XHJcbiAgICBkZWNsYXJlIGNsYXNzIEludDEwIHtcclxuICAgICAgICBjb25zdHJ1Y3Rvcih2YWx1ZT86IGFueSk7XHJcblxyXG4gICAgICAgIHN1YihzdWI6IG51bWJlcik7XHJcbiAgICAgICAgbXVsQWRkKG11bDogbnVtYmVyLCBhZGQ6IG51bWJlcik7XHJcbiAgICAgICAgc2ltcGxpZnkoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBlbGxpcHNpcyA9IFwiXFx1MjAyNlwiO1xyXG5cclxuICAgIGZ1bmN0aW9uIHN0cmluZ19jdXQoc3RyLCBsZW4pIHtcclxuICAgICAgICBpZiAoc3RyLmxlbmd0aCA+IGxlbilcclxuICAgICAgICAgICAgc3RyID0gc3RyLnN1YnN0cmluZygwLCBsZW4pICsgZWxsaXBzaXM7XHJcbiAgICAgICAgcmV0dXJuIHN0cjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY2xhc3MgU3RyZWFtIHtcclxuICAgICAgICBwcml2YXRlIHN0YXRpYyBIRVhfRElHSVRTID0gXCIwMTIzNDU2Nzg5QUJDREVGXCI7XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVUaW1lUyA9IC9eKFxcZFxcZCkoMFsxLTldfDFbMC0yXSkoMFsxLTldfFsxMl1cXGR8M1swMV0pKFswMV1cXGR8MlswLTNdKSg/OihbMC01XVxcZCkoPzooWzAtNV1cXGQpKD86Wy4sXShcXGR7MSwzfSkpPyk/KT8oWnxbLStdKD86WzBdXFxkfDFbMC0yXSkoWzAtNV1cXGQpPyk/JC87XHJcbiAgICAgICAgcHJpdmF0ZSBzdGF0aWMgcmVUaW1lTCA9IC9eKFxcZFxcZFxcZFxcZCkoMFsxLTldfDFbMC0yXSkoMFsxLTldfFsxMl1cXGR8M1swMV0pKFswMV1cXGR8MlswLTNdKSg/OihbMC01XVxcZCkoPzooWzAtNV1cXGQpKD86Wy4sXShcXGR7MSwzfSkpPyk/KT8oWnxbLStdKD86WzBdXFxkfDFbMC0yXSkoWzAtNV1cXGQpPyk/JC87XHJcblxyXG4gICAgICAgIHBvc2l0aW9uOiBudW1iZXI7XHJcbiAgICAgICAgZGF0YTogc3RyaW5nIHwgQXJyYXlCdWZmZXI7XHJcblxyXG4gICAgICAgIGNvbnN0cnVjdG9yKGRhdGE6IHN0cmluZyB8IFN0cmVhbSB8IEFycmF5QnVmZmVyLCBwb3NpdGlvbjogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmIChkYXRhIGluc3RhbmNlb2YgU3RyZWFtKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhID0gZGF0YS5kYXRhO1xyXG4gICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICB0aGlzLmRhdGEgPSBkYXRhO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5wb3NpdGlvbiA9IHBvc2l0aW9uO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGVuZ3RoKCkgOiBudW1iZXIge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5kYXRhIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5kYXRhLmJ5dGVMZW5ndGg7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmRhdGEubGVuZ3RoO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZ2V0KHBvc2l0aW9uPzogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmIChwb3NpdGlvbiA9PT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICAgICAgcG9zaXRpb24gPSB0aGlzLnBvc2l0aW9uKys7XHJcblxyXG4gICAgICAgICAgICBpZiAocG9zaXRpb24gPj0gdGhpcy5sZW5ndGgoKSlcclxuICAgICAgICAgICAgICAgIHRocm93ICdSZXF1ZXN0aW5nIGJ5dGUgb2Zmc2V0ICcgKyB0aGlzLnBvc2l0aW9uICsgJyBvbiBhIHN0cmVhbSBvZiBsZW5ndGggJyArIHRoaXMubGVuZ3RoKCk7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gKHR5cGVvZih0aGlzLmRhdGEpID09PSBcInN0cmluZ1wiKSA/IHRoaXMuZGF0YS5jaGFyQ29kZUF0KHBvc2l0aW9uKSA6IHRoaXMuZGF0YVtwb3NpdGlvbl07XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBoZXhCeXRlKGJ5dGU6IG51bWJlcikge1xyXG4gICAgICAgICAgICByZXR1cm4gU3RyZWFtLkhFWF9ESUdJVFMuY2hhckF0KChieXRlID4+IDQpICYgMHhGKSArIFN0cmVhbS5IRVhfRElHSVRTLmNoYXJBdChieXRlICYgMHhGKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHBhcnNlU3RyaW5nSVNPKHN0YXJ0LCBlbmQpIHtcclxuICAgICAgICAgICAgbGV0IHMgPSBcIlwiO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSlcclxuICAgICAgICAgICAgICAgIHMgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSh0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcGFyc2VTdHJpbmdVVEYoc3RhcnQsIGVuZCkge1xyXG4gICAgICAgICAgICBsZXQgcyA9IFwiXCI7XHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSBzdGFydDsgaSA8IGVuZDspIHtcclxuICAgICAgICAgICAgICAgIGxldCBjID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIGlmIChjIDwgMTI4KVxyXG4gICAgICAgICAgICAgICAgICAgIHMgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShjKTtcclxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKChjID4gMTkxKSAmJiAoYyA8IDIyNCkpXHJcbiAgICAgICAgICAgICAgICAgICAgcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCgoYyAmIDB4MUYpIDw8IDYpIHwgKHRoaXMuZ2V0KGkrKykgJiAweDNGKSk7XHJcbiAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCgoYyAmIDB4MEYpIDw8IDEyKSB8ICgodGhpcy5nZXQoaSsrKSAmIDB4M0YpIDw8IDYpIHwgKHRoaXMuZ2V0KGkrKykgJiAweDNGKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHM7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBwYXJzZVN0cmluZ0JNUChzdGFydCwgZW5kKSB7XHJcbiAgICAgICAgICAgIGxldCBzdHIgPSBcIlwiLCBoaSwgbG87XHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSBzdGFydDsgaSA8IGVuZDspIHtcclxuICAgICAgICAgICAgICAgIGhpID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIGxvID0gdGhpcy5nZXQoaSsrKTtcclxuICAgICAgICAgICAgICAgIHN0ciArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChoaSA8PCA4KSB8IGxvKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gc3RyO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcGFyc2VUaW1lKHN0YXJ0LCBlbmQsIHNob3J0WWVhcikge1xyXG4gICAgICAgICAgICBsZXQgcyA9IHRoaXMucGFyc2VTdHJpbmdJU08oc3RhcnQsIGVuZCksXHJcbiAgICAgICAgICAgICAgICBtID0gKHNob3J0WWVhciA/IFN0cmVhbS5yZVRpbWVTIDogU3RyZWFtLnJlVGltZUwpLmV4ZWMocyk7XHJcbiAgICAgICAgICAgIGlmICghbSlcclxuICAgICAgICAgICAgICAgIHJldHVybiBcIlVucmVjb2duaXplZCB0aW1lOiBcIiArIHM7XHJcbiAgICAgICAgICAgIGlmIChzaG9ydFllYXIpIHtcclxuICAgICAgICAgICAgICAgIC8vIHRvIGF2b2lkIHF1ZXJ5aW5nIHRoZSB0aW1lciwgdXNlIHRoZSBmaXhlZCByYW5nZSBbMTk3MCwgMjA2OV1cclxuICAgICAgICAgICAgICAgIC8vIGl0IHdpbGwgY29uZm9ybSB3aXRoIElUVSBYLjQwMCBbLTEwLCArNDBdIHNsaWRpbmcgd2luZG93IHVudGlsIDIwMzBcclxuICAgICAgICAgICAgICAgIC8vbVsxXSA9ICttWzFdO1xyXG4gICAgICAgICAgICAgICAgLy9tWzFdICs9IChwYXJzZUludChtWzFdKSA8IDcwKSA/IDIwMDAgOiAxOTAwO1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmaXhtZSFcIjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzID0gbVsxXSArIFwiLVwiICsgbVsyXSArIFwiLVwiICsgbVszXSArIFwiIFwiICsgbVs0XTtcclxuICAgICAgICAgICAgaWYgKG1bNV0pIHtcclxuICAgICAgICAgICAgICAgIHMgKz0gXCI6XCIgKyBtWzVdO1xyXG4gICAgICAgICAgICAgICAgaWYgKG1bNl0pIHtcclxuICAgICAgICAgICAgICAgICAgICBzICs9IFwiOlwiICsgbVs2XTtcclxuICAgICAgICAgICAgICAgICAgICBpZiAobVs3XSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgcyArPSBcIi5cIiArIG1bN107XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgaWYgKG1bOF0pIHtcclxuICAgICAgICAgICAgICAgIHMgKz0gXCIgVVRDXCI7XHJcbiAgICAgICAgICAgICAgICBpZiAobVs4XSAhPSAnWicpIHtcclxuICAgICAgICAgICAgICAgICAgICBzICs9IG1bOF07XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG1bOV0pXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHMgKz0gXCI6XCIgKyBtWzldO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHBhcnNlSW50ZWdlcihzdGFydCwgZW5kKSB7XHJcbiAgICAgICAgICAgIGxldCBjdXJyZW50OiBudW1iZXIgPSB0aGlzLmdldChzdGFydCk7XHJcblxyXG4gICAgICAgICAgICBsZXQgbmVnYXRpdmUgPSAoY3VycmVudCA+IDEyNyk7XHJcbiAgICAgICAgICAgIGxldCBwYWRkaW5nID0gbmVnYXRpdmUgPyAyNTUgOiAwO1xyXG4gICAgICAgICAgICBsZXQgbGVuZ3RoO1xyXG4gICAgICAgICAgICBsZXQgZGVzY3JpcHRvcjogbnVtYmVyIHwgc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgLy8gc2tpcCB1bnVzZWZ1bCBiaXRzIChub3QgYWxsb3dlZCBpbiBERVIpXHJcbiAgICAgICAgICAgIHdoaWxlIChjdXJyZW50ID09IHBhZGRpbmcgJiYgKytzdGFydCA8IGVuZClcclxuICAgICAgICAgICAgICAgIGN1cnJlbnQgPSB0aGlzLmdldChzdGFydCk7XHJcblxyXG4gICAgICAgICAgICBsZW5ndGggPSBlbmQgLSBzdGFydDtcclxuICAgICAgICAgICAgaWYgKGxlbmd0aCA9PT0gMClcclxuICAgICAgICAgICAgICAgIHJldHVybiBuZWdhdGl2ZSA/ICctMScgOiAnMCc7XHJcblxyXG4gICAgICAgICAgICAvLyBzaG93IGJpdCBsZW5ndGggb2YgaHVnZSBpbnRlZ2Vyc1xyXG4gICAgICAgICAgICBpZiAobGVuZ3RoID4gNCkge1xyXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRvciA9IGN1cnJlbnQ7XHJcbiAgICAgICAgICAgICAgICBsZW5ndGggPDw9IDM7IC8qIGNhbGN1bGF0ZSBiaXQgbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICAgICAgd2hpbGUgKCgoZGVzY3JpcHRvciBeIHBhZGRpbmcpICYgMHg4MCkgPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0b3IgPDw9IDE7XHJcbiAgICAgICAgICAgICAgICAgICAgLS1sZW5ndGg7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBkZXNjcmlwdG9yID0gXCIoXCIgKyBsZW5ndGggKyBcIiBiaXQpXFxuXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgLy8gZGVjb2RlIHRoZSBpbnRlZ2VyXHJcbiAgICAgICAgICAgIGlmIChuZWdhdGl2ZSkgY3VycmVudCA9IGN1cnJlbnQgLSAyNTY7XHJcblxyXG4gICAgICAgICAgICBsZXQgbnVtYmVyID0gXCJcIjtcclxuICAgICAgICAgICAgaWYodHlwZW9mKEludDEwKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgbGV0IG4gPSBuZXcgSW50MTAoY3VycmVudCk7XHJcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpXHJcbiAgICAgICAgICAgICAgICAgICAgbi5tdWxBZGQoMjU2LCB0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgICAgICBudW1iZXIgPSBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgbiA9IDA7XHJcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpIHtcclxuICAgICAgICAgICAgICAgICAgICBuIDw8PSA4O1xyXG4gICAgICAgICAgICAgICAgICAgIG4gKz0gdGhpcy5nZXQoaSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBudW1iZXIgPSBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIGRlc2NyaXB0b3IgKyBudW1iZXI7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaXNBU0NJSShzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlcikge1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgYyA9IHRoaXMuZ2V0KGkpO1xyXG4gICAgICAgICAgICAgICAgaWYgKGMgPCAzMiB8fCBjID4gMTc2KVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9O1xyXG4gICAgICAgIFxyXG4gICAgICAgIHBhcnNlQml0U3RyaW5nKHN0YXJ0LCBlbmQsIG1heExlbmd0aCkge1xyXG4gICAgICAgICAgICBsZXQgdW51c2VkQml0ID0gdGhpcy5nZXQoc3RhcnQpLFxyXG4gICAgICAgICAgICAgICAgbGVuQml0ID0gKChlbmQgLSBzdGFydCAtIDEpIDw8IDMpIC0gdW51c2VkQml0LFxyXG4gICAgICAgICAgICAgICAgaW50cm8gPSBcIihcIiArIGxlbkJpdCArIFwiIGJpdClcXG5cIixcclxuICAgICAgICAgICAgICAgIHMgPSBcIlwiO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQgKyAxOyBpIDwgZW5kOyArK2kpIHtcclxuICAgICAgICAgICAgICAgIGxldCBiID0gdGhpcy5nZXQoaSksXHJcbiAgICAgICAgICAgICAgICAgICAgc2tpcCA9IChpID09IGVuZCAtIDEpID8gdW51c2VkQml0IDogMDtcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGogPSA3OyBqID49IHNraXA7IC0tailcclxuICAgICAgICAgICAgICAgICAgICBzICs9IChiID4+IGopICYgMSA/IFwiMVwiIDogXCIwXCI7XHJcbiAgICAgICAgICAgICAgICBpZiAocy5sZW5ndGggPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGludHJvICsgc3RyaW5nX2N1dChzLCBtYXhMZW5ndGgpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBpbnRybyArIHM7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcGFyc2VPY3RldFN0cmluZyhzdGFydCwgZW5kLCBtYXhMZW5ndGgpIHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuaXNBU0NJSShzdGFydCwgZW5kKSlcclxuICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHRoaXMucGFyc2VTdHJpbmdJU08oc3RhcnQsIGVuZCksIG1heExlbmd0aCk7XHJcbiAgICAgICAgICAgIGxldCBsZW4gPSBlbmQgLSBzdGFydCxcclxuICAgICAgICAgICAgICAgIHMgPSBcIihcIiArIGxlbiArIFwiIGJ5dGUpXFxuXCI7XHJcbiAgICAgICAgICAgIG1heExlbmd0aCAvPSAyOyAvLyB3ZSB3b3JrIGluIGJ5dGVzXHJcbiAgICAgICAgICAgIGlmIChsZW4gPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICBlbmQgPSBzdGFydCArIG1heExlbmd0aDtcclxuICAgICAgICAgICAgZm9yIChsZXQgaSA9IHN0YXJ0OyBpIDwgZW5kOyArK2kpXHJcbiAgICAgICAgICAgICAgICBzICs9IHRoaXMuaGV4Qnl0ZSh0aGlzLmdldChpKSk7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICBzICs9IGVsbGlwc2lzO1xyXG4gICAgICAgICAgICByZXR1cm4gcztcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBwYXJzZU9JRChzdGFydCwgZW5kLCBtYXhMZW5ndGgpIHtcclxuICAgICAgICAgICAgbGV0IHMgPSAnJyxcclxuICAgICAgICAgICAgICAgIG4gPSBuZXcgSW50MTAoKSxcclxuICAgICAgICAgICAgICAgIGJpdHMgPSAwO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgbGV0IHYgPSB0aGlzLmdldChpKTtcclxuICAgICAgICAgICAgICAgIG4ubXVsQWRkKDEyOCwgdiAmIDB4N0YpO1xyXG4gICAgICAgICAgICAgICAgYml0cyArPSA3O1xyXG4gICAgICAgICAgICAgICAgaWYgKCEodiAmIDB4ODApKSB7IC8vIGZpbmlzaGVkXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHMgPT09ICcnKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuLnNpbXBsaWZ5KCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChuIGluc3RhbmNlb2YgSW50MTApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uc3ViKDgwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBcIjIuXCIgKyBuLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgbSA9IG4gPCA4MCA/IG4gPCA0MCA/IDAgOiAxIDogMjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMgPSBtICsgXCIuXCIgKyAobiAtIG0gKiA0MCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2VcclxuICAgICAgICAgICAgICAgICAgICAgICAgcyArPSBcIi5cIiArIG4udG9TdHJpbmcoKTtcclxuICAgICAgICAgICAgICAgICAgICBpZiAocy5sZW5ndGggPiBtYXhMZW5ndGgpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHMsIG1heExlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICAgICAgbiA9IG5ldyBJbnQxMCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJpdHMgPSAwO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmIChiaXRzID4gMClcclxuICAgICAgICAgICAgICAgIHMgKz0gXCIuaW5jb21wbGV0ZVwiO1xyXG4gICAgICAgICAgICAvKiBGSVhNRVxyXG4gICAgICAgICAgICBpZiAodHlwZW9mIG9pZHMgPT09ICdvYmplY3QnKSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgb2lkID0gb2lkc1tzXTtcclxuICAgICAgICAgICAgICAgIGlmIChvaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAob2lkLmQpIHMgKz0gXCJcXG5cIiArIG9pZC5kO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChvaWQuYykgcyArPSBcIlxcblwiICsgb2lkLmM7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKG9pZC53KSBzICs9IFwiXFxuKHdhcm5pbmchKVwiO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICovXHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGVudW0gVGFnQ2xhc3Mge1xyXG4gICAgICAgIFVOSVZFUlNBTCA9IDB4MDAsXHJcbiAgICAgICAgQVBQTElDQVRJT04gPSAweDAxLFxyXG4gICAgICAgIENPTlRFWFQgPSAweDAyLFxyXG4gICAgICAgIFBSSVZBVEUgPSAweDAzXHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGVudW0gVGFnVHlwZSB7XHJcbiAgICAgICAgRU9DID0gMHgwMCxcclxuICAgICAgICBCT09MRUFOID0gMHgwMSxcclxuICAgICAgICBJTlRFR0VSID0gMHgwMixcclxuICAgICAgICBCSVRfU1RSSU5HID0gMHgwMyxcclxuICAgICAgICBPQ1RFVF9TVFJJTkcgPSAweDA0LFxyXG4gICAgICAgIE5VTEwgPSAweDA1LFxyXG4gICAgICAgIE9CSkVDVF9JREVOVElGSUVSID0gMHgwNixcclxuICAgICAgICBPYmplY3REZXNjcmlwdG9yID0gMHgwNyxcclxuICAgICAgICBFWFRFUk5BTCA9IDB4MDgsXHJcbiAgICAgICAgUkVBTCA9IDB4MDksXHJcbiAgICAgICAgRU5VTUVSQVRFRCA9IDB4MEEsXHJcbiAgICAgICAgRU1CRURERURfUERWID0gMHgwQixcclxuICAgICAgICBVVEY4U3RyaW5nID0gMHgwQyxcclxuICAgICAgICBTRVFVRU5DRSA9IDB4MTAsXHJcbiAgICAgICAgU0VUID0gMHgxMSxcclxuICAgICAgICBOdW1lcmljU3RyaW5nID0gMHgxMixcclxuICAgICAgICBQcmludGFibGVTdHJpbmcgPSAweDEzLCAvLyBBU0NJSSBzdWJzZXRcclxuICAgICAgICBUZWxldGV4dFN0cmluZyA9IDB4MTQsIC8vIGFrYSBUNjFTdHJpbmdcclxuICAgICAgICBWaWRlb3RleFN0cmluZyA9IDB4MTUsXHJcbiAgICAgICAgSUE1U3RyaW5nID0gMHgxNiwgLy8gQVNDSUlcclxuICAgICAgICBVVENUaW1lID0gMHgxNyxcclxuICAgICAgICBHZW5lcmFsaXplZFRpbWUgPSAweDE4LFxyXG4gICAgICAgIEdyYXBoaWNTdHJpbmcgPSAweDE5LFxyXG4gICAgICAgIFZpc2libGVTdHJpbmcgPSAweDFBLCAvLyBBU0NJSSBzdWJzZXRcclxuICAgICAgICBHZW5lcmFsU3RyaW5nID0gMHgxQixcclxuICAgICAgICBVbml2ZXJzYWxTdHJpbmcgPSAweDFDLFxyXG4gICAgICAgIEJNUFN0cmluZyA9IDB4MUVcclxuICAgIH1cclxuXHJcbiAgICBjbGFzcyBBU04xVGFnIHtcclxuICAgICAgICB0YWdDbGFzczogVGFnQ2xhc3M7XHJcbiAgICAgICAgdHlwZTogVGFnVHlwZTtcclxuICAgICAgICB0YWdDb25zdHJ1Y3RlZDogYm9vbGVhbjtcclxuICAgICAgICB0YWdOdW1iZXI6IG51bWJlcjtcclxuXHJcbiAgICAgICAgY29uc3RydWN0b3Ioc3RyZWFtOiBTdHJlYW0pIHtcclxuICAgICAgICAgICAgbGV0IGJ1ZiA9IHN0cmVhbS5nZXQoKTtcclxuICAgICAgICAgICAgdGhpcy50YWdDbGFzcyA9IGJ1ZiA+PiA2O1xyXG4gICAgICAgICAgICB0aGlzLnRhZ0NvbnN0cnVjdGVkID0gKChidWYgJiAweDIwKSAhPT0gMCk7XHJcbiAgICAgICAgICAgIHRoaXMudGFnTnVtYmVyID0gYnVmICYgMHgxRjtcclxuICAgICAgICAgICAgaWYgKHRoaXMudGFnTnVtYmVyID09IDB4MUYpIHsgLy8gbG9uZyB0YWdcclxuICAgICAgICAgICAgICAgIGxldCBuID0gbmV3IEludDEwKCk7XHJcbiAgICAgICAgICAgICAgICBkbyB7XHJcbiAgICAgICAgICAgICAgICAgICAgYnVmID0gc3RyZWFtLmdldCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIG4ubXVsQWRkKDEyOCwgYnVmICYgMHg3Rik7XHJcbiAgICAgICAgICAgICAgICB9IHdoaWxlIChidWYgJiAweDgwKTtcclxuICAgICAgICAgICAgICAgIHRoaXMudGFnTnVtYmVyID0gbi5zaW1wbGlmeSgpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpc1VuaXZlcnNhbCgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudGFnQ2xhc3MgPT09IDB4MDA7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaXNFT0MoKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnRhZ0NsYXNzID09PSAweDAwICYmIHRoaXMudGFnTnVtYmVyID09PSAweDAwO1xyXG4gICAgICAgIH07XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGNsYXNzIEFTTjEge1xyXG4gICAgICAgIHN0cmVhbTogU3RyZWFtO1xyXG4gICAgICAgIGhlYWRlcjogbnVtYmVyO1xyXG4gICAgICAgIGxlbmd0aDogbnVtYmVyO1xyXG4gICAgICAgIHRhZzogQVNOMVRhZztcclxuICAgICAgICBjaGlsZHJlbjogQVNOMVtdO1xyXG5cclxuICAgICAgICBjb25zdHJ1Y3RvcihzdHJlYW06IFN0cmVhbSwgaGVhZGVyOiBudW1iZXIsIGxlbmd0aDogbnVtYmVyLCB0YWc6IEFTTjFUYWcsIGNoaWxkcmVuOiBBU04xW10pIHtcclxuICAgICAgICAgICAgdGhpcy5zdHJlYW0gPSBzdHJlYW07XHJcbiAgICAgICAgICAgIHRoaXMuaGVhZGVyID0gaGVhZGVyO1xyXG4gICAgICAgICAgICB0aGlzLmxlbmd0aCA9IGxlbmd0aDtcclxuICAgICAgICAgICAgdGhpcy50YWcgPSB0YWc7XHJcbiAgICAgICAgICAgIHRoaXMuY2hpbGRyZW4gPSBjaGlsZHJlbjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnRlbnQobWF4X2xlbmd0aD86IG51bWJlciwgdHlwZT86IFRhZ1R5cGUpIHsgLy8gYSBwcmV2aWV3IG9mIHRoZSBjb250ZW50IChpbnRlbmRlZCBmb3IgaHVtYW5zKVxyXG4gICAgICAgICAgICBpZiAodGhpcy50YWcgPT09IHVuZGVmaW5lZCkgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgICAgIGlmIChtYXhfbGVuZ3RoID09PSB1bmRlZmluZWQpXHJcbiAgICAgICAgICAgICAgICBtYXhfbGVuZ3RoID0gSW5maW5pdHk7XHJcblxyXG4gICAgICAgICAgICBsZXQgY29udGVudCA9IHRoaXMucG9zQ29udGVudCgpLFxyXG4gICAgICAgICAgICAgICAgbGVuID0gTWF0aC5hYnModGhpcy5sZW5ndGgpO1xyXG5cclxuICAgICAgICAgICAgaWYgKCF0aGlzLnRhZy5pc1VuaXZlcnNhbCgpKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5jaGlsZHJlbiAhPT0gbnVsbClcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCIoXCIgKyB0aGlzLmNoaWxkcmVuLmxlbmd0aCArIFwiIGVsZW0pXCI7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucGFyc2VPY3RldFN0cmluZyhjb250ZW50LCBjb250ZW50ICsgbGVuLCBtYXhfbGVuZ3RoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBzd2l0Y2ggKHR5cGUgfHwgdGhpcy50YWcudGFnTnVtYmVyKSB7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDE6IC8vIEJPT0xFQU5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gKHRoaXMuc3RyZWFtLmdldChjb250ZW50KSA9PT0gMCkgPyBcImZhbHNlXCIgOiBcInRydWVcIjtcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgwMjogLy8gSU5URUdFUlxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5wYXJzZUludGVnZXIoY29udGVudCwgY29udGVudCArIGxlbik7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDM6IC8vIEJJVF9TVFJJTkdcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5jaGlsZHJlbiA/IFwiKFwiICsgdGhpcy5jaGlsZHJlbi5sZW5ndGggKyBcIiBlbGVtKVwiIDpcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zdHJlYW0ucGFyc2VCaXRTdHJpbmcoY29udGVudCwgY29udGVudCArIGxlbiwgbWF4X2xlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MDQ6IC8vIE9DVEVUX1NUUklOR1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmNoaWxkcmVuID8gXCIoXCIgKyB0aGlzLmNoaWxkcmVuLmxlbmd0aCArIFwiIGVsZW0pXCIgOlxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnN0cmVhbS5wYXJzZU9jdGV0U3RyaW5nKGNvbnRlbnQsIGNvbnRlbnQgKyBsZW4sIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MDU6IC8vIE5VTExcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgwNjogLy8gT0JKRUNUX0lERU5USUZJRVJcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucGFyc2VPSUQoY29udGVudCwgY29udGVudCArIGxlbiwgbWF4X2xlbmd0aCk7XHJcbiAgICAgICAgICAgICAgICAvL2Nhc2UgMHgwNzogLy8gT2JqZWN0RGVzY3JpcHRvclxyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MDg6IC8vIEVYVEVSTkFMXHJcbiAgICAgICAgICAgICAgICAvL2Nhc2UgMHgwOTogLy8gUkVBTFxyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MEE6IC8vIEVOVU1FUkFURURcclxuICAgICAgICAgICAgICAgIC8vY2FzZSAweDBCOiAvLyBFTUJFRERFRF9QRFZcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxMDogLy8gU0VRVUVOQ0VcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxMTogLy8gU0VUXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuY2hpbGRyZW4gIT09IG51bGwpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBcIihcIiArIHRoaXMuY2hpbGRyZW4ubGVuZ3RoICsgXCIgZWxlbSlcIjtcclxuICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBcIihubyBlbGVtKVwiO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDBDOiAvLyBVVEY4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHN0cmluZ19jdXQodGhpcy5zdHJlYW0ucGFyc2VTdHJpbmdVVEYoY29udGVudCwgY29udGVudCArIGxlbiksIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDEyOiAvLyBOdW1lcmljU3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTM6IC8vIFByaW50YWJsZVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDE0OiAvLyBUZWxldGV4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTU6IC8vIFZpZGVvdGV4U3RyaW5nXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTY6IC8vIElBNVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgLy9jYXNlIDB4MTk6IC8vIEdyYXBoaWNTdHJpbmdcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxQTogLy8gVmlzaWJsZVN0cmluZ1xyXG4gICAgICAgICAgICAgICAgICAgIC8vY2FzZSAweDFCOiAvLyBHZW5lcmFsU3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgLy9jYXNlIDB4MUM6IC8vIFVuaXZlcnNhbFN0cmluZ1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBzdHJpbmdfY3V0KHRoaXMuc3RyZWFtLnBhcnNlU3RyaW5nSVNPKGNvbnRlbnQsIGNvbnRlbnQgKyBsZW4pLCBtYXhfbGVuZ3RoKTtcclxuICAgICAgICAgICAgICAgIGNhc2UgMHgxRTogLy8gQk1QU3RyaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHN0cmluZ19jdXQodGhpcy5zdHJlYW0ucGFyc2VTdHJpbmdCTVAoY29udGVudCwgY29udGVudCArIGxlbiksIG1heF9sZW5ndGgpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAweDE3OiAvLyBVVENUaW1lXHJcbiAgICAgICAgICAgICAgICBjYXNlIDB4MTg6IC8vIEdlbmVyYWxpemVkVGltZVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnN0cmVhbS5wYXJzZVRpbWUoY29udGVudCwgY29udGVudCArIGxlbiwgKHRoaXMudGFnLnRhZ051bWJlciA9PSAweDE3KSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgdHlwZU5hbWUoKTogc3RyaW5nIHtcclxuICAgICAgICAgICAgc3dpdGNoICh0aGlzLnRhZy50YWdDbGFzcykge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAwOiAvLyB1bml2ZXJzYWxcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gVGFnVHlwZVt0aGlzLnRhZy50YWdOdW1iZXJdIHx8IChcIlVuaXZlcnNhbF9cIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpKTtcclxuICAgICAgICAgICAgICAgIGNhc2UgMTpcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJBcHBsaWNhdGlvbl9cIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpO1xyXG4gICAgICAgICAgICAgICAgY2FzZSAyOlxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIltcIiArIHRoaXMudGFnLnRhZ051bWJlci50b1N0cmluZygpICsgXCJdXCI7IC8vIENvbnRleHRcclxuICAgICAgICAgICAgICAgIGNhc2UgMzpcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJQcml2YXRlX1wiICsgdGhpcy50YWcudGFnTnVtYmVyLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB0b1N0cmluZygpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMudHlwZU5hbWUoKSArIFwiQFwiICsgdGhpcy5zdHJlYW0ucG9zaXRpb24gKyBcIltoZWFkZXI6XCIgKyB0aGlzLmhlYWRlciArIFwiLGxlbmd0aDpcIiArIHRoaXMubGVuZ3RoICsgXCIsc3ViOlwiICsgKCh0aGlzLmNoaWxkcmVuID09PSBudWxsKSA/ICdudWxsJyA6IHRoaXMuY2hpbGRyZW4ubGVuZ3RoKSArIFwiXVwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdG9QcmV0dHlTdHJpbmcoaW5kZW50KSB7XHJcbiAgICAgICAgICAgIGlmIChpbmRlbnQgPT09IHVuZGVmaW5lZCkgaW5kZW50ID0gJyc7XHJcbiAgICAgICAgICAgIGxldCBzID0gaW5kZW50ICsgdGhpcy50eXBlTmFtZSgpICsgXCIgQFwiICsgdGhpcy5zdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmxlbmd0aCA+PSAwKVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIitcIjtcclxuICAgICAgICAgICAgcyArPSB0aGlzLmxlbmd0aDtcclxuICAgICAgICAgICAgaWYgKHRoaXMudGFnLnRhZ0NvbnN0cnVjdGVkKVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIiAoY29uc3RydWN0ZWQpXCI7XHJcbiAgICAgICAgICAgIGVsc2UgaWYgKCh0aGlzLnRhZy5pc1VuaXZlcnNhbCgpICYmICgodGhpcy50YWcudGFnTnVtYmVyID09IDB4MDMpIHx8ICh0aGlzLnRhZy50YWdOdW1iZXIgPT0gMHgwNCkpKSAmJiAodGhpcy5jaGlsZHJlbiAhPT0gbnVsbCkpXHJcbiAgICAgICAgICAgICAgICBzICs9IFwiIChlbmNhcHN1bGF0ZXMpXCI7XHJcbiAgICAgICAgICAgIGxldCBjb250ZW50ID0gdGhpcy5jb250ZW50KCk7XHJcbiAgICAgICAgICAgIGlmIChjb250ZW50KVxyXG4gICAgICAgICAgICAgICAgcyArPSBcIjogXCIgKyBjb250ZW50LnJlcGxhY2UoL1xcbi9nLCAnfCcpO1xyXG4gICAgICAgICAgICBzICs9IFwiXFxuXCI7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmNoaWxkcmVuICE9PSBudWxsKSB7XHJcbiAgICAgICAgICAgICAgICBpbmRlbnQgKz0gJyAgJztcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwLCBtYXggPSB0aGlzLmNoaWxkcmVuLmxlbmd0aDsgaSA8IG1heDsgKytpKVxyXG4gICAgICAgICAgICAgICAgICAgIHMgKz0gdGhpcy5jaGlsZHJlbltpXS50b1ByZXR0eVN0cmluZyhpbmRlbnQpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBzO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHBvc1N0YXJ0KCkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcG9zQ29udGVudCgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtLnBvc2l0aW9uICsgdGhpcy5oZWFkZXI7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcG9zRW5kKCkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zdHJlYW0ucG9zaXRpb24gKyB0aGlzLmhlYWRlciArIE1hdGguYWJzKHRoaXMubGVuZ3RoKTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBzdGF0aWMgZGVjb2RlTGVuZ3RoKHN0cmVhbTogU3RyZWFtKSB7XHJcbiAgICAgICAgICAgIGxldCBidWYgPSBzdHJlYW0uZ2V0KCk7XHJcbiAgICAgICAgICAgIGNvbnN0IGxlbiA9IGJ1ZiAmIDB4N0Y7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPT0gYnVmKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGxlbjtcclxuICAgICAgICAgICAgaWYgKGxlbiA+IDYpIC8vIG5vIHJlYXNvbiB0byB1c2UgSW50MTAsIGFzIGl0IHdvdWxkIGJlIGEgaHVnZSBidWZmZXIgYW55d2F5c1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJMZW5ndGggb3ZlciA0OCBiaXRzIG5vdCBzdXBwb3J0ZWQgYXQgcG9zaXRpb24gXCIgKyAoc3RyZWFtLnBvc2l0aW9uIC0gMSk7XHJcbiAgICAgICAgICAgIGlmIChsZW4gPT09IDApXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gbnVsbDsgLy8gdW5kZWZpbmVkXHJcblxyXG4gICAgICAgICAgICBidWYgPSAwO1xyXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlbjsgKytpKVxyXG4gICAgICAgICAgICAgICAgYnVmID0gKGJ1ZiA8PCA4KSArIHN0cmVhbS5nZXQoKTtcclxuICAgICAgICAgICAgcmV0dXJuIGJ1ZjtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBzdGF0aWMgZW5jb2RlTGVuZ3RoKGJ1ZmZlcjogVWludDhBcnJheSwgb2Zmc2V0OiBudW1iZXIsIGxlbmd0aDogbnVtYmVyKSB7XHJcbiAgICAgICAgICAgIGlmKGxlbmd0aCA8IDB4N0YpIHtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltvZmZzZXRdID0gbGVuZ3RoO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW29mZnNldF0gPSAweDgwO1xyXG4gICAgICAgICAgICAgICAgbGV0IGluZGV4ID0gMTtcclxuICAgICAgICAgICAgICAgIHdoaWxlKGxlbmd0aCA+IDApIHtcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbb2Zmc2V0ICsgaW5kZXgrK10gPSBsZW5ndGggJiAweEZGO1xyXG4gICAgICAgICAgICAgICAgICAgIGxlbmd0aCA+Pj0gODtcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbb2Zmc2V0XSArPSAxO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGRlY29kZTAoc3RyZWFtOiBTdHJlYW0pIHtcclxuICAgICAgICBjb25zdCBzdHJlYW1TdGFydCA9IG5ldyBTdHJlYW0oc3RyZWFtLCAwKTsgLyogY29weSAqL1xyXG4gICAgICAgIGNvbnN0IHRhZyA9IG5ldyBBU04xVGFnKHN0cmVhbSk7XHJcbiAgICAgICAgbGV0IGxlbiA9IEFTTjEuZGVjb2RlTGVuZ3RoKHN0cmVhbSk7XHJcbiAgICAgICAgY29uc3Qgc3RhcnQgPSBzdHJlYW0ucG9zaXRpb247XHJcbiAgICAgICAgY29uc3QgbGVuZ3RoX2hlYWRlciA9IHN0YXJ0IC0gc3RyZWFtU3RhcnQucG9zaXRpb247XHJcbiAgICAgICAgbGV0IGNoaWxkcmVuID0gbnVsbDtcclxuICAgICAgICBjb25zdCBxdWVyeV9jaGlsZHJlbiA9ICgpID0+IHtcclxuICAgICAgICAgICAgY2hpbGRyZW4gPSBbXTtcclxuICAgICAgICAgICAgaWYgKGxlbiAhPT0gbnVsbCkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgZW5kID0gc3RhcnQgKyBsZW47XHJcbiAgICAgICAgICAgICAgICBpZiAoZW5kID4gc3RyZWFtLmxlbmd0aCgpKVxyXG4gICAgICAgICAgICAgICAgICAgIHRocm93ICdDb250YWluZXIgYXQgb2Zmc2V0ICcgKyBzdGFydCArICcgaGFzIGEgbGVuZ3RoIG9mICcgKyBsZW4gKyAnLCB3aGljaCBpcyBwYXN0IHRoZSBlbmQgb2YgdGhlIHN0cmVhbSc7XHJcbiAgICAgICAgICAgICAgICB3aGlsZSAoc3RyZWFtLnBvc2l0aW9uIDwgZW5kKVxyXG4gICAgICAgICAgICAgICAgICAgIGNoaWxkcmVuW2NoaWxkcmVuLmxlbmd0aF0gPSBkZWNvZGUwKHN0cmVhbSk7XHJcbiAgICAgICAgICAgICAgICBpZiAoc3RyZWFtLnBvc2l0aW9uICE9IGVuZClcclxuICAgICAgICAgICAgICAgICAgICB0aHJvdyAnQ29udGVudCBzaXplIGlzIG5vdCBjb3JyZWN0IGZvciBjb250YWluZXIgYXQgb2Zmc2V0ICcgKyBzdGFydDtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIC8vIHVuZGVmaW5lZCBsZW5ndGhcclxuICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgd2hpbGUgKHRydWUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcyA9IGRlY29kZTAoc3RyZWFtKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHMudGFnLmlzRU9DKCkpIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjaGlsZHJlbltjaGlsZHJlbi5sZW5ndGhdID0gcztcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgbGVuID0gc3RhcnQgLSBzdHJlYW0ucG9zaXRpb247IC8vIHVuZGVmaW5lZCBsZW5ndGhzIGFyZSByZXByZXNlbnRlZCBhcyBuZWdhdGl2ZSB2YWx1ZXNcclxuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aHJvdyAnRXhjZXB0aW9uIHdoaWxlIGRlY29kaW5nIHVuZGVmaW5lZCBsZW5ndGggY29udGVudCBhdCBvZmZzZXQgJyArIHN0YXJ0ICsgJzogJyArIGU7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG4gICAgICAgIGlmICh0YWcudGFnQ29uc3RydWN0ZWQpIHtcclxuICAgICAgICAgICAgLy8gbXVzdCBoYXZlIHZhbGlkIGNvbnRlbnRcclxuICAgICAgICAgICAgcXVlcnlfY2hpbGRyZW4oKTtcclxuICAgICAgICB9IGVsc2UgaWYgKHRhZy5pc1VuaXZlcnNhbCgpICYmICgodGFnLnRhZ051bWJlciA9PSAweDAzKSB8fCAodGFnLnRhZ051bWJlciA9PSAweDA0KSkpIHtcclxuICAgICAgICAgICAgLy8gc29tZXRpbWVzIEJpdFN0cmluZyBhbmQgT2N0ZXRTdHJpbmcgYXJlIHVzZWQgdG8gZW5jYXBzdWxhdGUgQVNOLjFcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmICh0YWcudGFnTnVtYmVyID09IDB4MDMpXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHN0cmVhbS5nZXQoKSAhPSAwKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBcIkJJVCBTVFJJTkdzIHdpdGggdW51c2VkIGJpdHMgY2Fubm90IGVuY2Fwc3VsYXRlLlwiO1xyXG4gICAgICAgICAgICAgICAgcXVlcnlfY2hpbGRyZW4oKTtcclxuICAgICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY2hpbGRyZW4ubGVuZ3RoOyArK2kpXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNoaWxkcmVuW2ldLnRhZy5pc0VPQygpKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyAnRU9DIGlzIG5vdCBzdXBwb3NlZCB0byBiZSBhY3R1YWwgY29udGVudC4nO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICAvLyBidXQgc2lsZW50bHkgaWdub3JlIHdoZW4gdGhleSBkb24ndFxyXG4gICAgICAgICAgICAgICAgY2hpbGRyZW4gPSBudWxsO1xyXG4gICAgICAgICAgICAgICAgLy9ERUJVRyBjb25zb2xlLmxvZygnQ291bGQgbm90IGRlY29kZSBzdHJ1Y3R1cmUgYXQgJyArIHN0YXJ0ICsgJzonLCBlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoY2hpbGRyZW4gPT09IG51bGwpIHtcclxuICAgICAgICAgICAgaWYgKGxlbiA9PT0gbnVsbClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiV2UgY2FuJ3Qgc2tpcCBvdmVyIGFuIGludmFsaWQgdGFnIHdpdGggdW5kZWZpbmVkIGxlbmd0aCBhdCBvZmZzZXQgXCIgKyBzdGFydDtcclxuICAgICAgICAgICAgc3RyZWFtLnBvc2l0aW9uID0gc3RhcnQgKyBNYXRoLmFicyhsZW4pO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbmV3IEFTTjEoc3RyZWFtU3RhcnQsIGxlbmd0aF9oZWFkZXIsIGxlbiwgdGFnLCBjaGlsZHJlbik7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlY29kZShzdHJlYW06IHN0cmluZyB8IEFycmF5QnVmZmVyKSB7XHJcbiAgICAgICAgcmV0dXJuIGRlY29kZTAobmV3IFN0cmVhbShzdHJlYW0sIDApKTtcclxuICAgIH1cclxufSIsImRlY2xhcmUgZnVuY3Rpb24gZGVmaW5lKCQpO1xyXG5kZWNsYXJlIGZ1bmN0aW9uIHVuZXNjYXBlKHN0cmluZzogc3RyaW5nKTogc3RyaW5nO1xyXG5kZWNsYXJlIGNsYXNzIF9zaGExIHtcclxuICAgIHN0YXRpYyBhcnJheUJ1ZmZlcigkOiBBcnJheUJ1ZmZlcikgOiBBcnJheUJ1ZmZlcjtcclxufVxyXG5cclxuLypcclxuaW50ZXJmYWNlIFdpbmRvdyB7XHJcbiAgICBUZXh0RW5jb2RlcjogYW55O1xyXG59XHJcbiovXHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIHNoYSB7XHJcbiAgICAvKlxyXG4gICAgICogW2pzLXNoYTFde0BsaW5rIGh0dHBzOi8vZ2l0aHViLmNvbS9lbW4xNzgvanMtc2hhMX1cclxuICAgICAqXHJcbiAgICAgKiBAdmVyc2lvbiAwLjYuMFxyXG4gICAgICogQGF1dGhvciBDaGVuLCBZaS1DeXVhbiBbZW1uMTc4QGdtYWlsLmNvbV1cclxuICAgICAqIEBjb3B5cmlnaHQgQ2hlbiwgWWktQ3l1YW4gMjAxNC0yMDE3XHJcbiAgICAgKiBAbGljZW5zZSBNSVRcclxuICAgICAqL1xyXG4gICAgLypqc2xpbnQgYml0d2lzZTogdHJ1ZSAqL1xyXG4gICAgKGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICd1c2Ugc3RyaWN0JztcclxuXHJcbiAgICAgICAgbGV0IHJvb3Q6IGFueSA9IHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnID8gd2luZG93IDoge307XHJcbiAgICAgICAgbGV0IE5PREVfSlMgPSAhcm9vdC5KU19TSEExX05PX05PREVfSlMgJiYgdHlwZW9mIHByb2Nlc3MgPT09ICdvYmplY3QnICYmIHByb2Nlc3MudmVyc2lvbnMgJiYgcHJvY2Vzcy52ZXJzaW9ucy5ub2RlO1xyXG4gICAgICAgIGlmIChOT0RFX0pTKSB7XHJcbiAgICAgICAgICAgIHJvb3QgPSBnbG9iYWw7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxldCBDT01NT05fSlMgPSAhcm9vdC5KU19TSEExX05PX0NPTU1PTl9KUyAmJiB0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0JyAmJiBtb2R1bGUuZXhwb3J0cztcclxuICAgICAgICBsZXQgQU1EID0gdHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiAoZGVmaW5lIGFzIGFueSkuYW1kO1xyXG4gICAgICAgIGxldCBIRVhfQ0hBUlMgPSAnMDEyMzQ1Njc4OWFiY2RlZicuc3BsaXQoJycpO1xyXG4gICAgICAgIGxldCBFWFRSQSA9IFstMjE0NzQ4MzY0OCwgODM4ODYwOCwgMzI3NjgsIDEyOF07XHJcbiAgICAgICAgbGV0IFNISUZUID0gWzI0LCAxNiwgOCwgMF07XHJcbiAgICAgICAgbGV0IE9VVFBVVF9UWVBFUyA9IFsnaGV4JywgJ2FycmF5JywgJ2RpZ2VzdCcsICdhcnJheUJ1ZmZlciddO1xyXG5cclxuICAgICAgICBsZXQgYmxvY2tzID0gW107XHJcblxyXG4gICAgICAgIGxldCBjcmVhdGVPdXRwdXRNZXRob2QgPSBmdW5jdGlvbiAob3V0cHV0VHlwZSkge1xyXG4gICAgICAgICAgICByZXR1cm4gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBuZXcgU2hhMSh0cnVlKS51cGRhdGUobWVzc2FnZSlbb3V0cHV0VHlwZV0oKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBsZXQgY3JlYXRlTWV0aG9kID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICBsZXQgbWV0aG9kOiBhbnkgPSBjcmVhdGVPdXRwdXRNZXRob2QoJ2hleCcpO1xyXG4gICAgICAgICAgICBpZiAoTk9ERV9KUykge1xyXG4gICAgICAgICAgICAgICAgbWV0aG9kID0gbm9kZVdyYXAobWV0aG9kKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBtZXRob2QuY3JlYXRlID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyAoU2hhMSBhcyBhbnkpKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIG1ldGhvZC51cGRhdGUgPSBmdW5jdGlvbiAobWVzc2FnZSkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIG1ldGhvZC5jcmVhdGUoKS51cGRhdGUobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgT1VUUFVUX1RZUEVTLmxlbmd0aDsgKytpKSB7XHJcbiAgICAgICAgICAgICAgICB2YXIgdHlwZSA9IE9VVFBVVF9UWVBFU1tpXTtcclxuICAgICAgICAgICAgICAgIG1ldGhvZFt0eXBlXSA9IGNyZWF0ZU91dHB1dE1ldGhvZCh0eXBlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gbWV0aG9kO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHZhciBub2RlV3JhcCA9IGZ1bmN0aW9uIChtZXRob2QpIHtcclxuICAgICAgICAgICAgdmFyIGNyeXB0byA9IGV2YWwoXCJyZXF1aXJlKCdjcnlwdG8nKVwiKTtcclxuICAgICAgICAgICAgdmFyIEJ1ZmZlciA9IGV2YWwoXCJyZXF1aXJlKCdidWZmZXInKS5CdWZmZXJcIik7XHJcbiAgICAgICAgICAgIHZhciBub2RlTWV0aG9kID0gZnVuY3Rpb24gKG1lc3NhZ2UpIHtcclxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTEnKS51cGRhdGUobWVzc2FnZSwgJ3V0ZjgnKS5kaWdlc3QoJ2hleCcpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChtZXNzYWdlLmNvbnN0cnVjdG9yID09PSBBcnJheUJ1ZmZlcikge1xyXG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBuZXcgVWludDhBcnJheShtZXNzYWdlKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAobWVzc2FnZS5sZW5ndGggPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBtZXRob2QobWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTEnKS51cGRhdGUobmV3IEJ1ZmZlcihtZXNzYWdlKSkuZGlnZXN0KCdoZXgnKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgcmV0dXJuIG5vZGVNZXRob2Q7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgZnVuY3Rpb24gU2hhMShzaGFyZWRNZW1vcnkpIHtcclxuICAgICAgICAgICAgaWYgKHNoYXJlZE1lbW9yeSkge1xyXG4gICAgICAgICAgICAgICAgYmxvY2tzWzBdID0gYmxvY2tzWzE2XSA9IGJsb2Nrc1sxXSA9IGJsb2Nrc1syXSA9IGJsb2Nrc1szXSA9XHJcbiAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzRdID0gYmxvY2tzWzVdID0gYmxvY2tzWzZdID0gYmxvY2tzWzddID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzhdID0gYmxvY2tzWzldID0gYmxvY2tzWzEwXSA9IGJsb2Nrc1sxMV0gPVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzEyXSA9IGJsb2Nrc1sxM10gPSBibG9ja3NbMTRdID0gYmxvY2tzWzE1XSA9IDA7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmJsb2NrcyA9IGJsb2NrcztcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuYmxvY2tzID0gWzAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDBdO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB0aGlzLmgwID0gMHg2NzQ1MjMwMTtcclxuICAgICAgICAgICAgdGhpcy5oMSA9IDB4RUZDREFCODk7XHJcbiAgICAgICAgICAgIHRoaXMuaDIgPSAweDk4QkFEQ0ZFO1xyXG4gICAgICAgICAgICB0aGlzLmgzID0gMHgxMDMyNTQ3NjtcclxuICAgICAgICAgICAgdGhpcy5oNCA9IDB4QzNEMkUxRjA7XHJcblxyXG4gICAgICAgICAgICB0aGlzLmJsb2NrID0gdGhpcy5zdGFydCA9IHRoaXMuYnl0ZXMgPSB0aGlzLmhCeXRlcyA9IDA7XHJcbiAgICAgICAgICAgIHRoaXMuZmluYWxpemVkID0gdGhpcy5oYXNoZWQgPSBmYWxzZTtcclxuICAgICAgICAgICAgdGhpcy5maXJzdCA9IHRydWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbiAobWVzc2FnZSkge1xyXG4gICAgICAgICAgICBpZiAodGhpcy5maW5hbGl6ZWQpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB2YXIgbm90U3RyaW5nID0gdHlwZW9mKG1lc3NhZ2UpICE9PSAnc3RyaW5nJztcclxuICAgICAgICAgICAgaWYgKG5vdFN0cmluZyAmJiBtZXNzYWdlLmNvbnN0cnVjdG9yID09PSByb290LkFycmF5QnVmZmVyKSB7XHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlID0gbmV3IFVpbnQ4QXJyYXkobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgdmFyIGNvZGUsIGluZGV4ID0gMCwgaSwgbGVuZ3RoID0gbWVzc2FnZS5sZW5ndGggfHwgMCwgYmxvY2tzID0gdGhpcy5ibG9ja3M7XHJcblxyXG4gICAgICAgICAgICB3aGlsZSAoaW5kZXggPCBsZW5ndGgpIHtcclxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmhhc2hlZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGFzaGVkID0gZmFsc2U7XHJcbiAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzBdID0gdGhpcy5ibG9jaztcclxuICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTZdID0gYmxvY2tzWzFdID0gYmxvY2tzWzJdID0gYmxvY2tzWzNdID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzWzRdID0gYmxvY2tzWzVdID0gYmxvY2tzWzZdID0gYmxvY2tzWzddID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1s4XSA9IGJsb2Nrc1s5XSA9IGJsb2Nrc1sxMF0gPSBibG9ja3NbMTFdID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTJdID0gYmxvY2tzWzEzXSA9IGJsb2Nrc1sxNF0gPSBibG9ja3NbMTVdID0gMDtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZihub3RTdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGkgPSB0aGlzLnN0YXJ0OyBpbmRleCA8IGxlbmd0aCAmJiBpIDwgNjQ7ICsraW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gbWVzc2FnZVtpbmRleF0gPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3IgKGkgPSB0aGlzLnN0YXJ0OyBpbmRleCA8IGxlbmd0aCAmJiBpIDwgNjQ7ICsraW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29kZSA9IG1lc3NhZ2UuY2hhckNvZGVBdChpbmRleCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb2RlIDwgMHg4MCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gY29kZSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjb2RlIDwgMHg4MDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweGMwIHwgKGNvZGUgPj4gNikpIDw8IFNISUZUW2krKyAmIDNdO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gKDB4ODAgfCAoY29kZSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChjb2RlIDwgMHhkODAwIHx8IGNvZGUgPj0gMHhlMDAwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHhlMCB8IChjb2RlID4+IDEyKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiA2KSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweDgwIHwgKGNvZGUgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2RlID0gMHgxMDAwMCArICgoKGNvZGUgJiAweDNmZikgPDwgMTApIHwgKG1lc3NhZ2UuY2hhckNvZGVBdCgrK2luZGV4KSAmIDB4M2ZmKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHhmMCB8IChjb2RlID4+IDE4KSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiAxMikgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbaSA+PiAyXSB8PSAoMHg4MCB8ICgoY29kZSA+PiA2KSAmIDB4M2YpKSA8PCBTSElGVFtpKysgJiAzXTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2Nrc1tpID4+IDJdIHw9ICgweDgwIHwgKGNvZGUgJiAweDNmKSkgPDwgU0hJRlRbaSsrICYgM107XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5sYXN0Qnl0ZUluZGV4ID0gaTtcclxuICAgICAgICAgICAgICAgIHRoaXMuYnl0ZXMgKz0gaSAtIHRoaXMuc3RhcnQ7XHJcbiAgICAgICAgICAgICAgICBpZiAoaSA+PSA2NCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYmxvY2sgPSBibG9ja3NbMTZdO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhcnQgPSBpIC0gNjQ7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oYXNoKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5oYXNoZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLnN0YXJ0ID0gaTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAodGhpcy5ieXRlcyA+IDQyOTQ5NjcyOTUpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuaEJ5dGVzICs9IHRoaXMuYnl0ZXMgLyA0Mjk0OTY3Mjk2IDw8IDA7XHJcbiAgICAgICAgICAgICAgICB0aGlzLmJ5dGVzID0gdGhpcy5ieXRlcyAlIDQyOTQ5NjcyOTY7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgU2hhMS5wcm90b3R5cGUuZmluYWxpemUgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLmZpbmFsaXplZCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHRoaXMuZmluYWxpemVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgdmFyIGJsb2NrcyA9IHRoaXMuYmxvY2tzLCBpID0gdGhpcy5sYXN0Qnl0ZUluZGV4O1xyXG4gICAgICAgICAgICBibG9ja3NbMTZdID0gdGhpcy5ibG9jaztcclxuICAgICAgICAgICAgYmxvY2tzW2kgPj4gMl0gfD0gRVhUUkFbaSAmIDNdO1xyXG4gICAgICAgICAgICB0aGlzLmJsb2NrID0gYmxvY2tzWzE2XTtcclxuICAgICAgICAgICAgaWYgKGkgPj0gNTYpIHtcclxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5oYXNoZWQpIHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLmhhc2goKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGJsb2Nrc1swXSA9IHRoaXMuYmxvY2s7XHJcbiAgICAgICAgICAgICAgICBibG9ja3NbMTZdID0gYmxvY2tzWzFdID0gYmxvY2tzWzJdID0gYmxvY2tzWzNdID1cclxuICAgICAgICAgICAgICAgICAgICBibG9ja3NbNF0gPSBibG9ja3NbNV0gPSBibG9ja3NbNl0gPSBibG9ja3NbN10gPVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbOF0gPSBibG9ja3NbOV0gPSBibG9ja3NbMTBdID0gYmxvY2tzWzExXSA9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBibG9ja3NbMTJdID0gYmxvY2tzWzEzXSA9IGJsb2Nrc1sxNF0gPSBibG9ja3NbMTVdID0gMDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBibG9ja3NbMTRdID0gdGhpcy5oQnl0ZXMgPDwgMyB8IHRoaXMuYnl0ZXMgPj4+IDI5O1xyXG4gICAgICAgICAgICBibG9ja3NbMTVdID0gdGhpcy5ieXRlcyA8PCAzO1xyXG4gICAgICAgICAgICB0aGlzLmhhc2goKTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS5oYXNoID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICB2YXIgYSA9IHRoaXMuaDAsIGIgPSB0aGlzLmgxLCBjID0gdGhpcy5oMiwgZCA9IHRoaXMuaDMsIGUgPSB0aGlzLmg0O1xyXG4gICAgICAgICAgICB2YXIgZiwgaiwgdCwgYmxvY2tzID0gdGhpcy5ibG9ja3M7XHJcblxyXG4gICAgICAgICAgICBmb3IoaiA9IDE2OyBqIDwgODA7ICsraikge1xyXG4gICAgICAgICAgICAgICAgdCA9IGJsb2Nrc1tqIC0gM10gXiBibG9ja3NbaiAtIDhdIF4gYmxvY2tzW2ogLSAxNF0gXiBibG9ja3NbaiAtIDE2XTtcclxuICAgICAgICAgICAgICAgIGJsb2Nrc1tqXSA9ICAodCA8PCAxKSB8ICh0ID4+PiAzMSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcihqID0gMDsgaiA8IDIwOyBqICs9IDUpIHtcclxuICAgICAgICAgICAgICAgIGYgPSAoYiAmIGMpIHwgKCh+YikgJiBkKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYSA8PCA1KSB8IChhID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBlID0gdCArIGYgKyBlICsgMTUxODUwMDI0OSArIGJsb2Nrc1tqXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYiA9IChiIDw8IDMwKSB8IChiID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gKGEgJiBiKSB8ICgofmEpICYgYyk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGUgPDwgNSkgfCAoZSA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgZCA9IHQgKyBmICsgZCArIDE1MTg1MDAyNDkgKyBibG9ja3NbaiArIDFdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBhID0gKGEgPDwgMzApIHwgKGEgPj4+IDIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGYgPSAoZSAmIGEpIHwgKCh+ZSkgJiBiKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZCA8PCA1KSB8IChkID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBjID0gdCArIGYgKyBjICsgMTUxODUwMDI0OSArIGJsb2Nrc1tqICsgMl0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGUgPSAoZSA8PCAzMCkgfCAoZSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChkICYgZSkgfCAoKH5kKSAmIGEpO1xyXG4gICAgICAgICAgICAgICAgdCA9IChjIDw8IDUpIHwgKGMgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGIgPSB0ICsgZiArIGIgKyAxNTE4NTAwMjQ5ICsgYmxvY2tzW2ogKyAzXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZCA9IChkIDw8IDMwKSB8IChkID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gKGMgJiBkKSB8ICgofmMpICYgZSk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGIgPDwgNSkgfCAoYiA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgYSA9IHQgKyBmICsgYSArIDE1MTg1MDAyNDkgKyBibG9ja3NbaiArIDRdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBjID0gKGMgPDwgMzApIHwgKGMgPj4+IDIpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBmb3IoOyBqIDwgNDA7IGogKz0gNSkge1xyXG4gICAgICAgICAgICAgICAgZiA9IGIgXiBjIF4gZDtcclxuICAgICAgICAgICAgICAgIHQgPSAoYSA8PCA1KSB8IChhID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBlID0gdCArIGYgKyBlICsgMTg1OTc3NTM5MyArIGJsb2Nrc1tqXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYiA9IChiIDw8IDMwKSB8IChiID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gYSBeIGIgXiBjO1xyXG4gICAgICAgICAgICAgICAgdCA9IChlIDw8IDUpIHwgKGUgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGQgPSB0ICsgZiArIGQgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAxXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYSA9IChhIDw8IDMwKSB8IChhID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZSBeIGEgXiBiO1xyXG4gICAgICAgICAgICAgICAgdCA9IChkIDw8IDUpIHwgKGQgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGMgPSB0ICsgZiArIGMgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAyXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZSA9IChlIDw8IDMwKSB8IChlID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZCBeIGUgXiBhO1xyXG4gICAgICAgICAgICAgICAgdCA9IChjIDw8IDUpIHwgKGMgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGIgPSB0ICsgZiArIGIgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyAzXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgZCA9IChkIDw8IDMwKSB8IChkID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gYyBeIGQgXiBlO1xyXG4gICAgICAgICAgICAgICAgdCA9IChiIDw8IDUpIHwgKGIgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGEgPSB0ICsgZiArIGEgKyAxODU5Nzc1MzkzICsgYmxvY2tzW2ogKyA0XSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYyA9IChjIDw8IDMwKSB8IChjID4+PiAyKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKDsgaiA8IDYwOyBqICs9IDUpIHtcclxuICAgICAgICAgICAgICAgIGYgPSAoYiAmIGMpIHwgKGIgJiBkKSB8IChjICYgZCk7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGEgPDwgNSkgfCAoYSA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgZSA9IHQgKyBmICsgZSAtIDE4OTQwMDc1ODggKyBibG9ja3Nbal0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGIgPSAoYiA8PCAzMCkgfCAoYiA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChhICYgYikgfCAoYSAmIGMpIHwgKGIgJiBjKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZSA8PCA1KSB8IChlID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBkID0gdCArIGYgKyBkIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgMV0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGEgPSAoYSA8PCAzMCkgfCAoYSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChlICYgYSkgfCAoZSAmIGIpIHwgKGEgJiBiKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoZCA8PCA1KSB8IChkID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBjID0gdCArIGYgKyBjIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgMl0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGUgPSAoZSA8PCAzMCkgfCAoZSA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChkICYgZSkgfCAoZCAmIGEpIHwgKGUgJiBhKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYyA8PCA1KSB8IChjID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBiID0gdCArIGYgKyBiIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgM10gPDwgMDtcclxuICAgICAgICAgICAgICAgIGQgPSAoZCA8PCAzMCkgfCAoZCA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IChjICYgZCkgfCAoYyAmIGUpIHwgKGQgJiBlKTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYiA8PCA1KSB8IChiID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBhID0gdCArIGYgKyBhIC0gMTg5NDAwNzU4OCArIGJsb2Nrc1tqICsgNF0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGMgPSAoYyA8PCAzMCkgfCAoYyA+Pj4gMik7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcig7IGogPCA4MDsgaiArPSA1KSB7XHJcbiAgICAgICAgICAgICAgICBmID0gYiBeIGMgXiBkO1xyXG4gICAgICAgICAgICAgICAgdCA9IChhIDw8IDUpIHwgKGEgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGUgPSB0ICsgZiArIGUgLSA4OTk0OTc1MTQgKyBibG9ja3Nbal0gPDwgMDtcclxuICAgICAgICAgICAgICAgIGIgPSAoYiA8PCAzMCkgfCAoYiA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IGEgXiBiIF4gYztcclxuICAgICAgICAgICAgICAgIHQgPSAoZSA8PCA1KSB8IChlID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBkID0gdCArIGYgKyBkIC0gODk5NDk3NTE0ICsgYmxvY2tzW2ogKyAxXSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYSA9IChhIDw8IDMwKSB8IChhID4+PiAyKTtcclxuXHJcbiAgICAgICAgICAgICAgICBmID0gZSBeIGEgXiBiO1xyXG4gICAgICAgICAgICAgICAgdCA9IChkIDw8IDUpIHwgKGQgPj4+IDI3KTtcclxuICAgICAgICAgICAgICAgIGMgPSB0ICsgZiArIGMgLSA4OTk0OTc1MTQgKyBibG9ja3NbaiArIDJdIDw8IDA7XHJcbiAgICAgICAgICAgICAgICBlID0gKGUgPDwgMzApIHwgKGUgPj4+IDIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGYgPSBkIF4gZSBeIGE7XHJcbiAgICAgICAgICAgICAgICB0ID0gKGMgPDwgNSkgfCAoYyA+Pj4gMjcpO1xyXG4gICAgICAgICAgICAgICAgYiA9IHQgKyBmICsgYiAtIDg5OTQ5NzUxNCArIGJsb2Nrc1tqICsgM10gPDwgMDtcclxuICAgICAgICAgICAgICAgIGQgPSAoZCA8PCAzMCkgfCAoZCA+Pj4gMik7XHJcblxyXG4gICAgICAgICAgICAgICAgZiA9IGMgXiBkIF4gZTtcclxuICAgICAgICAgICAgICAgIHQgPSAoYiA8PCA1KSB8IChiID4+PiAyNyk7XHJcbiAgICAgICAgICAgICAgICBhID0gdCArIGYgKyBhIC0gODk5NDk3NTE0ICsgYmxvY2tzW2ogKyA0XSA8PCAwO1xyXG4gICAgICAgICAgICAgICAgYyA9IChjIDw8IDMwKSB8IChjID4+PiAyKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgdGhpcy5oMCA9IHRoaXMuaDAgKyBhIDw8IDA7XHJcbiAgICAgICAgICAgIHRoaXMuaDEgPSB0aGlzLmgxICsgYiA8PCAwO1xyXG4gICAgICAgICAgICB0aGlzLmgyID0gdGhpcy5oMiArIGMgPDwgMDtcclxuICAgICAgICAgICAgdGhpcy5oMyA9IHRoaXMuaDMgKyBkIDw8IDA7XHJcbiAgICAgICAgICAgIHRoaXMuaDQgPSB0aGlzLmg0ICsgZSA8PCAwO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIFNoYTEucHJvdG90eXBlLmhleCA9IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG5cclxuICAgICAgICAgICAgdmFyIGgwID0gdGhpcy5oMCwgaDEgPSB0aGlzLmgxLCBoMiA9IHRoaXMuaDIsIGgzID0gdGhpcy5oMywgaDQgPSB0aGlzLmg0O1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIEhFWF9DSEFSU1soaDAgPj4gMjgpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDI0KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDAgPj4gMjApICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDE2KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDAgPj4gMTIpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgwID4+IDgpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMCA+PiA0KSAmIDB4MEZdICsgSEVYX0NIQVJTW2gwICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAyOCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gMjQpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAyMCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gMTYpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMSA+PiAxMikgJiAweDBGXSArIEhFWF9DSEFSU1soaDEgPj4gOCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgxID4+IDQpICYgMHgwRl0gKyBIRVhfQ0hBUlNbaDEgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDI4KSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiAyNCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDIwKSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiAxNikgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGgyID4+IDEyKSAmIDB4MEZdICsgSEVYX0NIQVJTWyhoMiA+PiA4KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDIgPj4gNCkgJiAweDBGXSArIEhFWF9DSEFSU1toMiAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMjgpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDI0KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMjApICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDE2KSAmIDB4MEZdICtcclxuICAgICAgICAgICAgICAgIEhFWF9DSEFSU1soaDMgPj4gMTIpICYgMHgwRl0gKyBIRVhfQ0hBUlNbKGgzID4+IDgpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoMyA+PiA0KSAmIDB4MEZdICsgSEVYX0NIQVJTW2gzICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAyOCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gMjQpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAyMCkgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gMTYpICYgMHgwRl0gK1xyXG4gICAgICAgICAgICAgICAgSEVYX0NIQVJTWyhoNCA+PiAxMikgJiAweDBGXSArIEhFWF9DSEFSU1soaDQgPj4gOCkgJiAweDBGXSArXHJcbiAgICAgICAgICAgICAgICBIRVhfQ0hBUlNbKGg0ID4+IDQpICYgMHgwRl0gKyBIRVhfQ0hBUlNbaDQgJiAweDBGXTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS50b1N0cmluZyA9IFNoYTEucHJvdG90eXBlLmhleDtcclxuXHJcbiAgICAgICAgU2hhMS5wcm90b3R5cGUuZGlnZXN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICB0aGlzLmZpbmFsaXplKCk7XHJcblxyXG4gICAgICAgICAgICB2YXIgaDAgPSB0aGlzLmgwLCBoMSA9IHRoaXMuaDEsIGgyID0gdGhpcy5oMiwgaDMgPSB0aGlzLmgzLCBoNCA9IHRoaXMuaDQ7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gW1xyXG4gICAgICAgICAgICAgICAgKGgwID4+IDI0KSAmIDB4RkYsIChoMCA+PiAxNikgJiAweEZGLCAoaDAgPj4gOCkgJiAweEZGLCBoMCAmIDB4RkYsXHJcbiAgICAgICAgICAgICAgICAoaDEgPj4gMjQpICYgMHhGRiwgKGgxID4+IDE2KSAmIDB4RkYsIChoMSA+PiA4KSAmIDB4RkYsIGgxICYgMHhGRixcclxuICAgICAgICAgICAgICAgIChoMiA+PiAyNCkgJiAweEZGLCAoaDIgPj4gMTYpICYgMHhGRiwgKGgyID4+IDgpICYgMHhGRiwgaDIgJiAweEZGLFxyXG4gICAgICAgICAgICAgICAgKGgzID4+IDI0KSAmIDB4RkYsIChoMyA+PiAxNikgJiAweEZGLCAoaDMgPj4gOCkgJiAweEZGLCBoMyAmIDB4RkYsXHJcbiAgICAgICAgICAgICAgICAoaDQgPj4gMjQpICYgMHhGRiwgKGg0ID4+IDE2KSAmIDB4RkYsIChoNCA+PiA4KSAmIDB4RkYsIGg0ICYgMHhGRlxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIFNoYTEucHJvdG90eXBlLmFycmF5ID0gU2hhMS5wcm90b3R5cGUuZGlnZXN0O1xyXG5cclxuICAgICAgICBTaGExLnByb3RvdHlwZS5hcnJheUJ1ZmZlciA9IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgdGhpcy5maW5hbGl6ZSgpO1xyXG5cclxuICAgICAgICAgICAgdmFyIGJ1ZmZlciA9IG5ldyBBcnJheUJ1ZmZlcigyMCk7XHJcbiAgICAgICAgICAgIHZhciBkYXRhVmlldyA9IG5ldyBEYXRhVmlldyhidWZmZXIpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMCwgdGhpcy5oMCk7XHJcbiAgICAgICAgICAgIGRhdGFWaWV3LnNldFVpbnQzMig0LCB0aGlzLmgxKTtcclxuICAgICAgICAgICAgZGF0YVZpZXcuc2V0VWludDMyKDgsIHRoaXMuaDIpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMTIsIHRoaXMuaDMpO1xyXG4gICAgICAgICAgICBkYXRhVmlldy5zZXRVaW50MzIoMTYsIHRoaXMuaDQpO1xyXG4gICAgICAgICAgICByZXR1cm4gYnVmZmVyO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIHZhciBleHBvcnRzID0gY3JlYXRlTWV0aG9kKCk7XHJcblxyXG4gICAgICAgIGlmIChDT01NT05fSlMpIHtcclxuICAgICAgICAgICAgbW9kdWxlLmV4cG9ydHMgPSBleHBvcnRzO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHJvb3QuX3NoYTEgPSBleHBvcnRzO1xyXG4gICAgICAgICAgICBpZiAoQU1EKSB7XHJcbiAgICAgICAgICAgICAgICBkZWZpbmUoZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBleHBvcnRzO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBlbmNvZGVfdGV4dChidWZmZXI6IHN0cmluZykgOiBBcnJheUJ1ZmZlciB7XHJcbiAgICAgICAgaWYgKCh3aW5kb3cgYXMgYW55KS5UZXh0RW5jb2Rlcikge1xyXG4gICAgICAgICAgICByZXR1cm4gbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKGJ1ZmZlcikuYnVmZmVyO1xyXG4gICAgICAgIH1cclxuICAgICAgICBsZXQgdXRmOCA9IHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChidWZmZXIpKTtcclxuICAgICAgICBsZXQgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkodXRmOC5sZW5ndGgpO1xyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdXRmOC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICByZXN1bHRbaV0gPSB1dGY4LmNoYXJDb2RlQXQoaSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiByZXN1bHQuYnVmZmVyO1xyXG4gICAgfVxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHNoYTEobWVzc2FnZTogc3RyaW5nIHwgQXJyYXlCdWZmZXIpIDogUHJvbWlzZUxpa2U8QXJyYXlCdWZmZXI+IHtcclxuICAgICAgICBpZighKHR5cGVvZihtZXNzYWdlKSA9PT0gXCJzdHJpbmdcIiB8fCBtZXNzYWdlIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpKSB0aHJvdyBcIkludmFsaWQgdHlwZSFcIjtcclxuXHJcbiAgICAgICAgbGV0IGJ1ZmZlciA9IG1lc3NhZ2UgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlciA/IG1lc3NhZ2UgOiBlbmNvZGVfdGV4dChtZXNzYWdlIGFzIHN0cmluZyk7XHJcblxyXG4gICAgICAgIGlmKCFjcnlwdG8gfHwgIWNyeXB0by5zdWJ0bGUgfHwgIWNyeXB0by5zdWJ0bGUuZGlnZXN0IHx8IC9FZGdlLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpKVxyXG4gICAgICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8QXJyYXlCdWZmZXI+KHJlc29sdmUgPT4ge1xyXG4gICAgICAgICAgICAgICAgcmVzb2x2ZShfc2hhMS5hcnJheUJ1ZmZlcihidWZmZXIgYXMgQXJyYXlCdWZmZXIpKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICByZXR1cm4gY3J5cHRvLnN1YnRsZS5kaWdlc3QoXCJTSEEtMVwiLCBidWZmZXIpO1xyXG4gICAgfVxyXG5cclxufSIsImV4cG9ydCBmdW5jdGlvbiBndWlkKCkge1xyXG4gICAgZnVuY3Rpb24gczQoKSB7XHJcbiAgICAgICAgcmV0dXJuIE1hdGguZmxvb3IoKDEgKyBNYXRoLnJhbmRvbSgpKSAqIDB4MTAwMDApXHJcbiAgICAgICAgICAgIC50b1N0cmluZygxNilcclxuICAgICAgICAgICAgLnN1YnN0cmluZygxKTtcclxuICAgIH1cclxuICAgIHJldHVybiBzNCgpICsgczQoKSArICctJyArIHM0KCkgKyAnLScgKyBzNCgpICsgJy0nICsgczQoKSArICctJyArIHM0KCkgKyBzNCgpICsgczQoKTtcclxufSIsImltcG9ydCB7Z3VpZH0gZnJvbSBcIi4uL2NyeXB0by91aWRcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vbG9nXCI7XHJcbmltcG9ydCB7TWVzc2FnZUhlbHBlcn0gZnJvbSBcIi4uL3VpL2ZyYW1lcy9jaGF0XCI7XHJcbmltcG9ydCB7U3RhdGljU2V0dGluZ3N9IGZyb20gXCIuLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge2NyZWF0ZUVycm9yTW9kYWx9IGZyb20gXCIuLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5cclxuZXhwb3J0IG5hbWVzcGFjZSBpMThuIHtcclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25LZXkge1xyXG4gICAgICAgIG1lc3NhZ2U6IHN0cmluZztcclxuICAgICAgICBsaW5lPzogbnVtYmVyO1xyXG4gICAgICAgIGNoYXJhY3Rlcj86IG51bWJlcjtcclxuICAgICAgICBmaWxlbmFtZT86IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgaW50ZXJmYWNlIFRyYW5zbGF0aW9uIHtcclxuICAgICAgICBrZXk6IFRyYW5zbGF0aW9uS2V5O1xyXG4gICAgICAgIHRyYW5zbGF0ZWQ6IHN0cmluZztcclxuICAgICAgICBmbGFncz86IHN0cmluZ1tdO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgQ29udHJpYnV0b3Ige1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBlbWFpbDogc3RyaW5nO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25GaWxlIHtcclxuICAgICAgICBwYXRoOiBzdHJpbmc7XHJcbiAgICAgICAgZnVsbF91cmw6IHN0cmluZztcclxuXHJcbiAgICAgICAgdHJhbnNsYXRpb25zOiBUcmFuc2xhdGlvbltdO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgUmVwb3NpdG9yeVRyYW5zbGF0aW9uIHtcclxuICAgICAgICBrZXk6IHN0cmluZztcclxuICAgICAgICBwYXRoOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIGNvdW50cnlfY29kZTogc3RyaW5nO1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBjb250cmlidXRvcnM6IENvbnRyaWJ1dG9yW107XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25SZXBvc2l0b3J5IHtcclxuICAgICAgICB1bmlxdWVfaWQ6IHN0cmluZztcclxuICAgICAgICB1cmw6IHN0cmluZztcclxuICAgICAgICBuYW1lPzogc3RyaW5nO1xyXG4gICAgICAgIGNvbnRhY3Q/OiBzdHJpbmc7XHJcbiAgICAgICAgdHJhbnNsYXRpb25zPzogUmVwb3NpdG9yeVRyYW5zbGF0aW9uW107XHJcbiAgICAgICAgbG9hZF90aW1lc3RhbXA/OiBudW1iZXI7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGxldCB0cmFuc2xhdGlvbnM6IFRyYW5zbGF0aW9uW10gPSBbXTtcclxuICAgIGxldCBmYXN0X3RyYW5zbGF0ZTogeyBba2V5OnN0cmluZ106c3RyaW5nOyB9ID0ge307XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gdHIobWVzc2FnZTogc3RyaW5nLCBrZXk/OiBzdHJpbmcpIHtcclxuICAgICAgICBjb25zdCBzbG9wcHkgPSBmYXN0X3RyYW5zbGF0ZVttZXNzYWdlXTtcclxuICAgICAgICBpZihzbG9wcHkpIHJldHVybiBzbG9wcHk7XHJcblxyXG4gICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LkkxOE4sIFwiVHJhbnNsYXRpbmcgXFxcIiVzXFxcIi4gRGVmYXVsdDogXFxcIiVzXFxcIlwiLCBrZXksIG1lc3NhZ2UpO1xyXG5cclxuICAgICAgICBsZXQgdHJhbnNsYXRlZCA9IG1lc3NhZ2U7XHJcbiAgICAgICAgZm9yKGNvbnN0IHRyYW5zbGF0aW9uIG9mIHRyYW5zbGF0aW9ucykge1xyXG4gICAgICAgICAgICBpZih0cmFuc2xhdGlvbi5rZXkubWVzc2FnZSA9PSBtZXNzYWdlKSB7XHJcbiAgICAgICAgICAgICAgICB0cmFuc2xhdGVkID0gdHJhbnNsYXRpb24udHJhbnNsYXRlZDtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBmYXN0X3RyYW5zbGF0ZVttZXNzYWdlXSA9IHRyYW5zbGF0ZWQ7XHJcbiAgICAgICAgcmV0dXJuIHRyYW5zbGF0ZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHRyYShtZXNzYWdlOiBzdHJpbmcsIC4uLmFyZ3M6IGFueVtdKSB7XHJcbiAgICAgICAgbWVzc2FnZSA9IHRyKG1lc3NhZ2UpO1xyXG4gICAgICAgIHJldHVybiBNZXNzYWdlSGVscGVyLmZvcm1hdE1lc3NhZ2UobWVzc2FnZSwgLi4uYXJncyk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZnVuY3Rpb24gbG9hZF90cmFuc2xhdGlvbl9maWxlKHVybDogc3RyaW5nLCBwYXRoOiBzdHJpbmcpIDogUHJvbWlzZTxUcmFuc2xhdGlvbkZpbGU+IHtcclxuICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8VHJhbnNsYXRpb25GaWxlPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICQuYWpheCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IHVybCxcclxuICAgICAgICAgICAgICAgIGFzeW5jOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgc3VjY2VzczogcmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWxlID0gKHR5cGVvZihyZXN1bHQpID09PSBcInN0cmluZ1wiID8gSlNPTi5wYXJzZShyZXN1bHQpIDogcmVzdWx0KSBhcyBUcmFuc2xhdGlvbkZpbGU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFmaWxlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJJbnZhbGlkIGpzb25cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUuZnVsbF91cmwgPSB1cmw7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUucGF0aCA9IHBhdGg7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAvL1RPRE86IFZhbGlkYXRlIGZpbGVcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShmaWxlKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkkxOE4sIHRyKFwiRmFpbGVkIHRvIGxvYWQgdHJhbnNsYXRpb24gZmlsZSAlcy4gRmFpbGVkIHRvIHBhcnNlIG9yIHByb2Nlc3MganNvbjogJW9cIiksIHVybCwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJGYWlsZWQgdG8gcHJvY2VzcyBvciBwYXJzZSBqc29uIVwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBlcnJvcikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlamVjdCh0cihcIkZhaWxlZCB0byBsb2FkIGZpbGU6IFwiKSArIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSlcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9hZF9maWxlKHVybDogc3RyaW5nLCBwYXRoOiBzdHJpbmcpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgcmV0dXJuIGxvYWRfdHJhbnNsYXRpb25fZmlsZSh1cmwsIHBhdGgpLnRoZW4oYXN5bmMgcmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgLyogVE9ETzogSW1wcm92ZSB0aGlzIHRlc3Q/ISovXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0cihcIkR1bW15IHRyYW5zbGF0aW9uIHRlc3RcIik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IFwiZHVtbXkgdGVzdCBmYWlsZWRcIjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJTdWNjZXNzZnVsbHkgaW5pdGlhbGl6ZWQgdXAgdHJhbnNsYXRpb24gZmlsZSBmcm9tICVzXCIpLCB1cmwpO1xyXG4gICAgICAgICAgICB0cmFuc2xhdGlvbnMgPSByZXN1bHQudHJhbnNsYXRpb25zO1xyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJGYWlsZWQgdG8gbG9hZCB0cmFuc2xhdGlvbiBmaWxlIGZyb20gXFxcIiVzXFxcIi4gRXJyb3I6ICVvXCIpLCB1cmwsIGVycm9yKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBmdW5jdGlvbiBsb2FkX3JlcG9zaXRvcnkwKHJlcG86IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSwgcmVsb2FkOiBib29sZWFuKSB7XHJcbiAgICAgICAgaWYoIXJlcG8ubG9hZF90aW1lc3RhbXAgfHwgcmVwby5sb2FkX3RpbWVzdGFtcCA8IDEwMDAgfHwgcmVsb2FkKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGluZm9fanNvbiA9IGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgICAgICQuYWpheCh7XHJcbiAgICAgICAgICAgICAgICAgICAgdXJsOiByZXBvLnVybCArIFwiL2luZm8uanNvblwiLFxyXG4gICAgICAgICAgICAgICAgICAgIGFzeW5jOiB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgIGNhY2hlOiAhcmVsb2FkLFxyXG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGZpbGUgPSAodHlwZW9mKHJlc3VsdCkgPT09IFwic3RyaW5nXCIgPyBKU09OLnBhcnNlKHJlc3VsdCkgOiByZXN1bHQpIGFzIFRyYW5zbGF0aW9uRmlsZTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYoIWZpbGUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChcIkludmFsaWQganNvblwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZShmaWxlKTtcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBlcnJvcikgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJGYWlsZWQgdG8gbG9hZCBmaWxlOiBcIikgKyBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHJlcG8sIGluZm9fanNvbik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZighcmVwby51bmlxdWVfaWQpXHJcbiAgICAgICAgICAgIHJlcG8udW5pcXVlX2lkID0gZ3VpZCgpO1xyXG5cclxuICAgICAgICByZXBvLnRyYW5zbGF0aW9ucyA9IHJlcG8udHJhbnNsYXRpb25zIHx8IFtdO1xyXG4gICAgICAgIHJlcG8ubG9hZF90aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkX3JlcG9zaXRvcnkodXJsOiBzdHJpbmcpIDogUHJvbWlzZTxUcmFuc2xhdGlvblJlcG9zaXRvcnk+IHtcclxuICAgICAgICBjb25zdCByZXN1bHQgPSB7fSBhcyBUcmFuc2xhdGlvblJlcG9zaXRvcnk7XHJcbiAgICAgICAgcmVzdWx0LnVybCA9IHVybDtcclxuICAgICAgICBhd2FpdCBsb2FkX3JlcG9zaXRvcnkwKHJlc3VsdCwgZmFsc2UpO1xyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IG5hbWVzcGFjZSBjb25maWcge1xyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgVHJhbnNsYXRpb25Db25maWcge1xyXG4gICAgICAgICAgICBjdXJyZW50X3JlcG9zaXRvcnlfdXJsPzogc3RyaW5nO1xyXG4gICAgICAgICAgICBjdXJyZW50X2xhbmd1YWdlPzogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgY3VycmVudF90cmFuc2xhdGlvbl91cmw/OiBzdHJpbmc7XHJcbiAgICAgICAgICAgIGN1cnJlbnRfdHJhbnNsYXRpb25fcGF0aD86IHN0cmluZztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGV4cG9ydCBpbnRlcmZhY2UgUmVwb3NpdG9yeUNvbmZpZyB7XHJcbiAgICAgICAgICAgIHJlcG9zaXRvcmllcz86IHtcclxuICAgICAgICAgICAgICAgIHVybD86IHN0cmluZztcclxuICAgICAgICAgICAgICAgIHJlcG9zaXRvcnk/OiBUcmFuc2xhdGlvblJlcG9zaXRvcnk7XHJcbiAgICAgICAgICAgIH1bXTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IHJlcG9zaXRvcnlfY29uZmlnX2tleSA9IFwiaTE4bi5yZXBvc2l0b3J5XCI7XHJcbiAgICAgICAgbGV0IF9jYWNoZWRfcmVwb3NpdG9yeV9jb25maWc6IFJlcG9zaXRvcnlDb25maWc7XHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHJlcG9zaXRvcnlfY29uZmlnKCkge1xyXG4gICAgICAgICAgICBpZihfY2FjaGVkX3JlcG9zaXRvcnlfY29uZmlnKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIF9jYWNoZWRfcmVwb3NpdG9yeV9jb25maWc7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBjb25maWdfc3RyaW5nID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0ocmVwb3NpdG9yeV9jb25maWdfa2V5KTtcclxuICAgICAgICAgICAgbGV0IGNvbmZpZzogUmVwb3NpdG9yeUNvbmZpZztcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGNvbmZpZyA9IGNvbmZpZ19zdHJpbmcgPyBKU09OLnBhcnNlKGNvbmZpZ19zdHJpbmcpIDoge307XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JMThOLCB0cihcIkZhaWxlZCB0byBwYXJzZSByZXBvc2l0b3J5IGNvbmZpZzogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBjb25maWcucmVwb3NpdG9yaWVzID0gY29uZmlnLnJlcG9zaXRvcmllcyB8fCBbXTtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IHJlcG8gb2YgY29uZmlnLnJlcG9zaXRvcmllcylcclxuICAgICAgICAgICAgICAgIChyZXBvLnJlcG9zaXRvcnkgfHwge2xvYWRfdGltZXN0YW1wOiAwfSkubG9hZF90aW1lc3RhbXAgPSAwO1xyXG5cclxuICAgICAgICAgICAgaWYoY29uZmlnLnJlcG9zaXRvcmllcy5sZW5ndGggPT0gMCkge1xyXG4gICAgICAgICAgICAgICAgLy9BZGQgdGhlIGRlZmF1bHQgVGVhU3BlYWsgcmVwb3NpdG9yeVxyXG4gICAgICAgICAgICAgICAgbG9hZF9yZXBvc2l0b3J5KFN0YXRpY1NldHRpbmdzLmluc3RhbmNlLnN0YXRpYyhcImkxOG4uZGVmYXVsdF9yZXBvc2l0b3J5XCIsIFwiaHR0cHM6Ly93ZWIudGVhc3BlYWsuZGUvaTE4bi9cIikpLnRoZW4ocmVwbyA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSTE4TiwgdHIoXCJTdWNjZXNzZnVsbHkgYWRkZWQgZGVmYXVsdCByZXBvc2l0b3J5IGZyb20gXFxcIiVzXFxcIi5cIiksIHJlcG8udXJsKTtcclxuICAgICAgICAgICAgICAgICAgICByZWdpc3Rlcl9yZXBvc2l0b3J5KHJlcG8pO1xyXG4gICAgICAgICAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkkxOE4sIHRyKFwiRmFpbGVkIHRvIGFkZCBkZWZhdWx0IHJlcG9zaXRvcnkuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiBfY2FjaGVkX3JlcG9zaXRvcnlfY29uZmlnID0gY29uZmlnO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHNhdmVfcmVwb3NpdG9yeV9jb25maWcoKSB7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKHJlcG9zaXRvcnlfY29uZmlnX2tleSwgSlNPTi5zdHJpbmdpZnkoX2NhY2hlZF9yZXBvc2l0b3J5X2NvbmZpZykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc3QgdHJhbnNsYXRpb25fY29uZmlnX2tleSA9IFwiaTE4bi50cmFuc2xhdGlvblwiO1xyXG4gICAgICAgIGxldCBfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZzogVHJhbnNsYXRpb25Db25maWc7XHJcblxyXG4gICAgICAgIGV4cG9ydCBmdW5jdGlvbiB0cmFuc2xhdGlvbl9jb25maWcoKSA6IFRyYW5zbGF0aW9uQ29uZmlnIHtcclxuICAgICAgICAgICAgaWYoX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWcpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWc7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBjb25maWdfc3RyaW5nID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0odHJhbnNsYXRpb25fY29uZmlnX2tleSk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZyA9IGNvbmZpZ19zdHJpbmcgPyBKU09OLnBhcnNlKGNvbmZpZ19zdHJpbmcpIDoge307XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JMThOLCB0cihcIkZhaWxlZCB0byBpbml0aWFsaXplIHRyYW5zbGF0aW9uIGNvbmZpZy4gVXNpbmcgZGVmYXVsdCBvbmUuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgX2NhY2hlZF90cmFuc2xhdGlvbl9jb25maWcgPSB7fSBhcyBhbnk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIF9jYWNoZWRfdHJhbnNsYXRpb25fY29uZmlnO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZXhwb3J0IGZ1bmN0aW9uIHNhdmVfdHJhbnNsYXRpb25fY29uZmlnKCkge1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSh0cmFuc2xhdGlvbl9jb25maWdfa2V5LCBKU09OLnN0cmluZ2lmeShfY2FjaGVkX3RyYW5zbGF0aW9uX2NvbmZpZykpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gcmVnaXN0ZXJfcmVwb3NpdG9yeShyZXBvc2l0b3J5OiBUcmFuc2xhdGlvblJlcG9zaXRvcnkpIHtcclxuICAgICAgICBpZighcmVwb3NpdG9yeSkgcmV0dXJuO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgcmVwbyBvZiBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXMpXHJcbiAgICAgICAgICAgIGlmKHJlcG8udXJsID09IHJlcG9zaXRvcnkudXJsKSByZXR1cm47XHJcblxyXG4gICAgICAgIGNvbmZpZy5yZXBvc2l0b3J5X2NvbmZpZygpLnJlcG9zaXRvcmllcy5wdXNoKHJlcG9zaXRvcnkpO1xyXG4gICAgICAgIGNvbmZpZy5zYXZlX3JlcG9zaXRvcnlfY29uZmlnKCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGV4cG9ydCBmdW5jdGlvbiByZWdpc3RlcmVkX3JlcG9zaXRvcmllcygpIDogVHJhbnNsYXRpb25SZXBvc2l0b3J5W10ge1xyXG4gICAgICAgIHJldHVybiBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXMubWFwKGUgPT4gZS5yZXBvc2l0b3J5IHx8IHt1cmw6IGUudXJsLCBsb2FkX3RpbWVzdGFtcDogMH0gYXMgVHJhbnNsYXRpb25SZXBvc2l0b3J5KTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlbGV0ZV9yZXBvc2l0b3J5KHJlcG9zaXRvcnk6IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSkge1xyXG4gICAgICAgIGlmKCFyZXBvc2l0b3J5KSByZXR1cm47XHJcblxyXG4gICAgICAgIGZvcihjb25zdCByZXBvIG9mIFsuLi5jb25maWcucmVwb3NpdG9yeV9jb25maWcoKS5yZXBvc2l0b3JpZXNdKVxyXG4gICAgICAgICAgICBpZihyZXBvLnVybCA9PSByZXBvc2l0b3J5LnVybCkge1xyXG4gICAgICAgICAgICAgICAgY29uZmlnLnJlcG9zaXRvcnlfY29uZmlnKCkucmVwb3NpdG9yaWVzLnJlbW92ZShyZXBvKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIGNvbmZpZy5zYXZlX3JlcG9zaXRvcnlfY29uZmlnKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGl0ZXJhdGVfcmVwb3NpdG9yaWVzKGNhbGxiYWNrX2VudHJ5OiAocmVwb3NpdG9yeTogVHJhbnNsYXRpb25SZXBvc2l0b3J5KSA9PiBhbnkpIHtcclxuICAgICAgICBjb25zdCBwcm9taXNlcyA9IFtdO1xyXG5cclxuICAgICAgICBmb3IoY29uc3QgcmVwb3NpdG9yeSBvZiByZWdpc3RlcmVkX3JlcG9zaXRvcmllcygpKSB7XHJcbiAgICAgICAgICAgIHByb21pc2VzLnB1c2gobG9hZF9yZXBvc2l0b3J5MChyZXBvc2l0b3J5LCBmYWxzZSkudGhlbigoKSA9PiBjYWxsYmFja19lbnRyeShyZXBvc2l0b3J5KSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuSTE4TiwgXCJGYWlsZWQgdG8gZmV0Y2ggcmVwb3NpdG9yeSAlcy4gZXJyb3I6ICVvXCIsIHJlcG9zaXRvcnkudXJsLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH0pKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHByb21pc2VzKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc2VsZWN0X3RyYW5zbGF0aW9uKHJlcG9zaXRvcnk6IFRyYW5zbGF0aW9uUmVwb3NpdG9yeSwgZW50cnk6IFJlcG9zaXRvcnlUcmFuc2xhdGlvbikge1xyXG4gICAgICAgIGNvbnN0IGNmZyA9IGNvbmZpZy50cmFuc2xhdGlvbl9jb25maWcoKTtcclxuXHJcbiAgICAgICAgaWYoZW50cnkgJiYgcmVwb3NpdG9yeSkge1xyXG4gICAgICAgICAgICBjZmcuY3VycmVudF9sYW5ndWFnZSA9IGVudHJ5Lm5hbWU7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3JlcG9zaXRvcnlfdXJsID0gcmVwb3NpdG9yeS51cmw7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3RyYW5zbGF0aW9uX3VybCA9IHJlcG9zaXRvcnkudXJsICsgZW50cnkucGF0aDtcclxuICAgICAgICAgICAgY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fcGF0aCA9IGVudHJ5LnBhdGg7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgY2ZnLmN1cnJlbnRfbGFuZ3VhZ2UgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3JlcG9zaXRvcnlfdXJsID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICBjZmcuY3VycmVudF90cmFuc2xhdGlvbl91cmwgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgIGNmZy5jdXJyZW50X3RyYW5zbGF0aW9uX3BhdGggPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25maWcuc2F2ZV90cmFuc2xhdGlvbl9jb25maWcoKTtcclxuICAgIH1cclxuXHJcbiAgICAvKiBBVFRFTlRJT046IFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBiZWZvcmUgbW9zdCBvdGhlciBsaWJyYXJ5IGluaXppYWxpc2F0aW9ucyEgKi9cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIGNvbnN0IHJjZmcgPSBjb25maWcucmVwb3NpdG9yeV9jb25maWcoKTsgLyogaW5pdGlhbGl6ZSAqL1xyXG4gICAgICAgIGNvbnN0IGNmZyA9IGNvbmZpZy50cmFuc2xhdGlvbl9jb25maWcoKTtcclxuXHJcbiAgICAgICAgaWYoY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBhd2FpdCBsb2FkX2ZpbGUoY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsLCBjZmcuY3VycmVudF90cmFuc2xhdGlvbl9wYXRoKTtcclxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBzZWxlY3RlZCB0cmFuc2xhdGlvbjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIGNvbnN0IHNob3dfZXJyb3IgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY3JlYXRlRXJyb3JNb2RhbCh0cihcIlRyYW5zbGF0aW9uIFN5c3RlbVwiKSwgdHJhKFwiRmFpbGVkIHRvIGxvYWQgY3VycmVudCBzZWxlY3RlZCB0cmFuc2xhdGlvbiBmaWxlLns6YnI6fUZpbGU6IHswfXs6YnI6fUVycm9yOiB7MX17OmJyOn17OmJyOn1Vc2luZyBkZWZhdWx0IGZhbGxiYWNrIHRyYW5zbGF0aW9ucy5cIiwgY2ZnLmN1cnJlbnRfdHJhbnNsYXRpb25fdXJsLCBlcnJvcikpLm9wZW4oKVxyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgIGlmKGxvYWRlci5ydW5uaW5nKCkpXHJcbiAgICAgICAgICAgICAgICAgICAgbG9hZGVyLnJlZ2lzdGVyX3Rhc2sobG9hZGVyLlN0YWdlLkpBVkFTQ1JJUFRfSU5JVElBTElaSU5HLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yaXR5OiAxMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHNob3dfZXJyb3IoKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogXCJJMThOIGVycm9yIGRpc3BsYXlcIlxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICAgICAgICAgIHNob3dfZXJyb3IoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBhd2FpdCBsb2FkX2ZpbGUoXCJodHRwOi8vbG9jYWxob3N0L2hvbWUvVGVhU3BlYWsvVGVhU3BlYWsvV2ViLUNsaWVudC93ZWIvZW52aXJvbm1lbnQvZGV2ZWxvcG1lbnQvaTE4bi9kZV9ERS50cmFuc2xhdGlvblwiKTtcclxuICAgICAgICAvLyBhd2FpdCBsb2FkX2ZpbGUoXCJodHRwOi8vbG9jYWxob3N0L2hvbWUvVGVhU3BlYWsvVGVhU3BlYWsvV2ViLUNsaWVudC93ZWIvZW52aXJvbm1lbnQvZGV2ZWxvcG1lbnQvaTE4bi90ZXN0Lmpzb25cIik7XHJcbiAgICB9XHJcbn1cclxuXHJcbi8vIEB0cy1pZ25vcmVcclxuY29uc3QgdHI6IHR5cGVvZiBpMThuLnRyID0gaTE4bi50cjtcclxuY29uc3QgdHJhOiB0eXBlb2YgaTE4bi50cmEgPSBpMThuLnRyYTtcclxuXHJcbih3aW5kb3cgYXMgYW55KS50ciA9IGkxOG4udHI7XHJcbih3aW5kb3cgYXMgYW55KS50cmEgPSBpMThuLnRyYTsiLCJpbXBvcnQgc3Bhd25ZZXNObyA9IE1vZGFscy5zcGF3blllc05vO1xyXG5pbXBvcnQge0Nvbm5lY3Rpb25IYW5kbGVyfSBmcm9tIFwiLi9Db25uZWN0aW9uSGFuZGxlclwiO1xyXG5pbXBvcnQge2JpcGN9IGZyb20gXCIuL0Jyb3dzZXJJUENcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi9sb2dcIjtcclxuaW1wb3J0IHtwcm9maWxlc30gZnJvbSBcIi4vcHJvZmlsZXMvQ29ubmVjdGlvblByb2ZpbGVcIjtcclxuaW1wb3J0IHtNb2RhbHN9IGZyb20gXCIuL3VpL21vZGFsL01vZGFsQ29ubmVjdFwiO1xyXG5pbXBvcnQge3NldHRpbmdzLCBTZXR0aW5nc30gZnJvbSBcIi4vc2V0dGluZ3NcIjtcclxuaW1wb3J0IHtpMThufSBmcm9tIFwiLi9pMThuL2xvY2FsaXplXCI7XHJcbmltcG9ydCB7Y3JlYXRlSW5mb01vZGFsfSBmcm9tIFwiLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5pbXBvcnQge01lc3NhZ2VIZWxwZXJ9IGZyb20gXCIuL3VpL2ZyYW1lcy9jaGF0XCI7XHJcblxyXG5leHBvcnQgY29uc3QganNfcmVuZGVyID0gd2luZG93LmpzcmVuZGVyIHx8ICQ7XHJcbmV4cG9ydCBjb25zdCBuYXRpdmVfY2xpZW50ID0gd2luZG93LnJlcXVpcmUgIT09IHVuZGVmaW5lZDtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRVc2VyTWVkaWFGdW5jdGlvblByb21pc2UoKSA6IChjb25zdHJhaW50czogTWVkaWFTdHJlYW1Db25zdHJhaW50cykgPT4gUHJvbWlzZTxNZWRpYVN0cmVhbT4ge1xyXG4gICAgaWYoJ21lZGlhRGV2aWNlcycgaW4gbmF2aWdhdG9yICYmICdnZXRVc2VyTWVkaWEnIGluIG5hdmlnYXRvci5tZWRpYURldmljZXMpXHJcbiAgICAgICAgcmV0dXJuIGNvbnN0cmFpbnRzID0+IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzKTtcclxuXHJcbiAgICBjb25zdCBfY2FsbGJhY2tlZF9mdW5jdGlvbiA9IG5hdmlnYXRvci5nZXRVc2VyTWVkaWEgfHwgbmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYSB8fCBuYXZpZ2F0b3IubW96R2V0VXNlck1lZGlhO1xyXG4gICAgaWYoIV9jYWxsYmFja2VkX2Z1bmN0aW9uKVxyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcblxyXG4gICAgcmV0dXJuIGNvbnN0cmFpbnRzID0+IG5ldyBQcm9taXNlPE1lZGlhU3RyZWFtPigocmVzb2x2ZSwgcmVqZWN0KSA9PiBfY2FsbGJhY2tlZF9mdW5jdGlvbihjb25zdHJhaW50cywgcmVzb2x2ZSwgcmVqZWN0KSk7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgV2luZG93IHtcclxuICAgIG9wZW5fY29ubmVjdGVkX3F1ZXN0aW9uOiAoKSA9PiBQcm9taXNlPGJvb2xlYW4+O1xyXG59XHJcblxyXG5leHBvcnQgZGVjbGFyZSBjb25zdCBub2RlUmVxdWlyZTogdHlwZW9mIHJlcXVpcmU7XHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cF9jbG9zZSgpIHtcclxuICAgIHdpbmRvdy5vbmJlZm9yZXVubG9hZCA9IGV2ZW50ID0+IHtcclxuICAgICAgICBpZihwcm9maWxlcy5yZXF1aXJlc19zYXZlKCkpXHJcbiAgICAgICAgICAgIHByb2ZpbGVzLnNhdmUoKTtcclxuXHJcbiAgICAgICAgaWYoIXNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRElTQUJMRV9VTkxPQURfRElBTE9HLCBmYWxzZSkpIHtcclxuICAgICAgICAgICAgY29uc3QgYWN0aXZlX2Nvbm5lY3Rpb25zID0gc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKCkuZmlsdGVyKGUgPT4gZS5jb25uZWN0ZWQpO1xyXG4gICAgICAgICAgICBpZihhY3RpdmVfY29ubmVjdGlvbnMubGVuZ3RoID09IDApIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgIGlmKCFuYXRpdmVfY2xpZW50KSB7XHJcbiAgICAgICAgICAgICAgICBldmVudC5yZXR1cm5WYWx1ZSA9IFwiQXJlIHlvdSByZWFsbHkgc3VyZT88YnI+WW91J3JlIHN0aWxsIGNvbm5lY3RlZCFcIjtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IGRvX2V4aXQgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZHAgPSBzZXJ2ZXJfY29ubmVjdGlvbnMuc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcnMoKS5tYXAoZSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGUuc2VydmVyQ29ubmVjdGlvbi5jb25uZWN0ZWQoKSlcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBlLnNlcnZlckNvbm5lY3Rpb24uZGlzY29ubmVjdCh0cihcImNsaWVudCBjbG9zZWRcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkubWFwKGUgPT4gZS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUud2Fybih0cihcIkZhaWxlZCB0byBkaXNjb25uZWN0IGZyb20gc2VydmVyIG9uIGNsaWVudCBjbG9zZTogJW9cIiksIGUpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZXhpdCA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3Qge3JlbW90ZX0gPSBub2RlUmVxdWlyZSgnZWxlY3Ryb24nKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3RlLmdldEN1cnJlbnRXaW5kb3coKS5jbG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIFByb21pc2UuYWxsKGRwKS50aGVuKGV4aXQpO1xyXG4gICAgICAgICAgICAgICAgICAgIC8qIGZvcmNlIGV4aXQgYWZ0ZXIgMjUwMG1zICovXHJcbiAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dChleGl0LCAyNTAwKTtcclxuICAgICAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgICAgICBpZih3aW5kb3cub3Blbl9jb25uZWN0ZWRfcXVlc3Rpb24pIHtcclxuICAgICAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIGV2ZW50LnJldHVyblZhbHVlID0gXCJxdWVzdGlvblwiO1xyXG4gICAgICAgICAgICAgICAgICAgIHdpbmRvdy5vcGVuX2Nvbm5lY3RlZF9xdWVzdGlvbigpLnRoZW4ocmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYocmVzdWx0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvKiBwcmV2ZW50IHF1aXR0aW5nIGJlY2F1c2Ugd2UgdHJ5IHRvIGRpc2Nvbm5lY3QgKi9cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5vbmJlZm9yZXVubG9hZCA9IGUgPT4gZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8qIGFsbG93IGEgZm9yY2UgcXVpdCBhZnRlciA1IHNlY29uZHMgKi9cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gd2luZG93Lm9uYmVmb3JldW5sb2FkLCA1MDAwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2V4aXQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAvKiB3ZSdyZSBpbiBkZWJ1Z2dpbmcgbW9kZSAqL1xyXG4gICAgICAgICAgICAgICAgICAgIGRvX2V4aXQoKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWNsYXJlIGZ1bmN0aW9uIG1vbWVudCguLi5hcmd1bWVudHMpIDogYW55O1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBfanNyZW5kZXIoKSA6IGJvb2xlYW4ge1xyXG4gICAgaWYoIWpzX3JlbmRlcikge1xyXG4gICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcihcIk1pc3NpbmcganNyZW5kZXIgZXh0ZW5zaW9uIVwiKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgICBpZighanNfcmVuZGVyLnZpZXdzKSB7XHJcbiAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKFwiTWlzc2luZyBqc3JlbmRlciB2aWV3ZXIgZXh0ZW5zaW9uIVwiKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgICBqc19yZW5kZXIudmlld3Muc2V0dGluZ3MuYWxsb3dDb2RlKHRydWUpO1xyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJybmRcIiwgKGFyZ3VtZW50KSA9PiB7XHJcbiAgICAgICAgbGV0IG1pbiA9IHBhcnNlSW50KGFyZ3VtZW50LnN1YnN0cigwLCBhcmd1bWVudC5pbmRleE9mKCd+JykpKTtcclxuICAgICAgICBsZXQgbWF4ID0gcGFyc2VJbnQoYXJndW1lbnQuc3Vic3RyKGFyZ3VtZW50LmluZGV4T2YoJ34nKSArIDEpKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIChNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAobWluICsgbWF4ICsgMSkgLSBtaW4pKS50b1N0cmluZygpO1xyXG4gICAgfSk7XHJcblxyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJmbXRfZGF0ZVwiLCAoLi4uYXJncykgPT4ge1xyXG4gICAgICAgIHJldHVybiBtb21lbnQoYXJnc1swXSkuZm9ybWF0KGFyZ3NbMV0pO1xyXG4gICAgfSk7XHJcblxyXG4gICAganNfcmVuZGVyLnZpZXdzLnRhZ3MoXCJ0clwiLCAoLi4uYXJncykgPT4ge1xyXG4gICAgICAgIHJldHVybiB0cihhcmdzWzBdKTtcclxuICAgIH0pO1xyXG5cclxuICAgICQoXCIuanNyZW5kZXItdGVtcGxhdGVcIikuZWFjaCgoaWR4LCBfZW50cnkpID0+IHtcclxuICAgICAgICBpZighanNfcmVuZGVyLnRlbXBsYXRlcyhfZW50cnkuaWQsIF9lbnRyeS5pbm5lckhUTUwpKSB7XHJcbiAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5HRU5FUkFMLCB0cihcIkZhaWxlZCB0byBzZXR1cCBjYWNoZSBmb3IganMgcmVuZGVyZXIgdGVtcGxhdGUgJXMhXCIpLCBfZW50cnkuaWQpO1xyXG4gICAgICAgIH0gZWxzZVxyXG4gICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5HRU5FUkFMLCB0cihcIlN1Y2Nlc3NmdWxseSBsb2FkZWQganNyZW5kZXIgdGVtcGxhdGUgJXNcIiksIF9lbnRyeS5pZCk7XHJcbiAgICB9KTtcclxuICAgIHJldHVybiB0cnVlO1xyXG59XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZSgpIHtcclxuICAgIFNldHRpbmdzLmluaXRpYWxpemUoKTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICAgIGF3YWl0IGkxOG4uaW5pdGlhbGl6ZSgpO1xyXG4gICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZWQgdGhlIHRyYW5zbGF0aW9uIHN5c3RlbSFcXG5FcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICBsb2FkZXIuY3JpdGljYWxfZXJyb3IoXCJGYWlsZWQgdG8gc2V0dXAgdGhlIHRyYW5zbGF0aW9uIHN5c3RlbVwiKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgYmlwYy5zZXR1cCgpO1xyXG59XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZV9hcHAoKSB7XHJcbiAgICB0cnkgeyAvL0luaXRpYWxpemUgbWFpbiB0ZW1wbGF0ZVxyXG4gICAgICAgIGNvbnN0IG1haW4gPSAkKFwiI3RtcGxfbWFpblwiKS5yZW5kZXJUYWcoe1xyXG4gICAgICAgICAgICBtdWx0aV9zZXNzaW9uOiAgIXNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0RJU0FCTEVfTVVMVElfU0VTU0lPTiksXHJcbiAgICAgICAgICAgIGFwcF92ZXJzaW9uOiBhcHAudWlfdmVyc2lvbigpXHJcbiAgICAgICAgfSkuZGl2aWRlcmZ5KCk7XHJcblxyXG4gICAgICAgICQoXCJib2R5XCIpLmFwcGVuZChtYWluKTtcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgZXJyb3IpO1xyXG4gICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcih0cihcIkZhaWxlZCB0byBzZXR1cCBtYWluIHBhZ2UhXCIpKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgY29udHJvbF9iYXIgPSBuZXcgQ29udHJvbEJhcigkKFwiI2NvbnRyb2xfYmFyXCIpKTsgLyogc2V0dXAgdGhlIGNvbnRyb2wgYmFyICovXHJcblxyXG4gICAgaWYoIWF1ZGlvLnBsYXllci5pbml0aWFsaXplKCkpXHJcbiAgICAgICAgY29uc29sZS53YXJuKHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgYXVkaW8gY29udHJvbGxlciFcIikpO1xyXG5cclxuICAgIGF1ZGlvLnBsYXllci5vbl9yZWFkeSgoKSA9PiB7XHJcbiAgICAgICAgaWYoYXVkaW8ucGxheWVyLnNldF9tYXN0ZXJfdm9sdW1lKVxyXG4gICAgICAgICAgICBhdWRpby5wbGF5ZXIub25fcmVhZHkoKCkgPT4gYXVkaW8ucGxheWVyLnNldF9tYXN0ZXJfdm9sdW1lKHNldHRpbmdzLmdsb2JhbChTZXR0aW5ncy5LRVlfU09VTkRfTUFTVEVSKSAvIDEwMCkpO1xyXG4gICAgICAgIGVsc2VcclxuICAgICAgICAgICAgbG9nLndhcm4oTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJDbGllbnQgZG9lcyBub3Qgc3VwcG9ydCBhdWRpby5wbGF5ZXIuc2V0X21hc3Rlcl92b2x1bWUoKS4uLiBNYXkgY2xpZW50IGlzIHRvbyBvbGQ/XCIpKTtcclxuICAgICAgICBpZihhdWRpby5yZWNvcmRlci5kZXZpY2VfcmVmcmVzaF9hdmFpbGFibGUoKSlcclxuICAgICAgICAgICAgYXVkaW8ucmVjb3JkZXIucmVmcmVzaF9kZXZpY2VzKCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBkZWZhdWx0X3JlY29yZGVyID0gbmV3IFJlY29yZGVyUHJvZmlsZShcImRlZmF1bHRcIik7XHJcbiAgICBkZWZhdWx0X3JlY29yZGVyLmluaXRpYWxpemUoKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LkFVRElPLCB0cihcIkZhaWxlZCB0byBpbml0aWFsaXplIGRlZmF1bHQgcmVjb3JkZXI6ICVvXCIpLCBlcnJvcik7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzb3VuZC5pbml0aWFsaXplKCkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuQVVESU8sIHRyKFwiU291bmRzIGluaXRpYWxpemVkXCIpKTtcclxuICAgIH0pO1xyXG4gICAgc291bmQuc2V0X21hc3Rlcl92b2x1bWUoc2V0dGluZ3MuZ2xvYmFsKFNldHRpbmdzLktFWV9TT1VORF9NQVNURVJfU09VTkRTKSAvIDEwMCk7XHJcblxyXG4gICAgYXdhaXQgcHJvZmlsZXMubG9hZCgpO1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgICAgYXdhaXQgcHB0LmluaXRpYWxpemUoKTtcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBwcHQhXFxuRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgcHB0IVwiKSk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIHNldHVwX2Nsb3NlKCk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBzdHIyYWI4KHN0cikge1xyXG4gICAgY29uc3QgYnVmID0gbmV3IEFycmF5QnVmZmVyKHN0ci5sZW5ndGgpO1xyXG4gICAgY29uc3QgYnVmVmlldyA9IG5ldyBVaW50OEFycmF5KGJ1Zik7XHJcbiAgICBmb3IgKGxldCBpID0gMCwgc3RyTGVuID0gc3RyLmxlbmd0aDsgaSA8IHN0ckxlbjsgaSsrKSB7XHJcbiAgICAgICAgYnVmVmlld1tpXSA9IHN0ci5jaGFyQ29kZUF0KGkpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGJ1ZjtcclxufVxyXG5cclxuLyogRklYTUUgRG9udCB1c2UgYXRvYiwgYmVjYXVzZSBpdCBzdWNrcyBmb3Igbm9uIFVURi04IHRpbmdzICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhcnJheUJ1ZmZlckJhc2U2NChiYXNlNjQ6IHN0cmluZykge1xyXG4gICAgYmFzZTY0ID0gYXRvYihiYXNlNjQpO1xyXG4gICAgY29uc3QgYnVmID0gbmV3IEFycmF5QnVmZmVyKGJhc2U2NC5sZW5ndGgpO1xyXG4gICAgY29uc3QgYnVmVmlldyA9IG5ldyBVaW50OEFycmF5KGJ1Zik7XHJcbiAgICBmb3IgKGxldCBpID0gMCwgc3RyTGVuID0gYmFzZTY0Lmxlbmd0aDsgaSA8IHN0ckxlbjsgaSsrKSB7XHJcbiAgICAgICAgYnVmVmlld1tpXSA9IGJhc2U2NC5jaGFyQ29kZUF0KGkpO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGJ1ZjtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGJhc2U2NF9lbmNvZGVfYWIoc291cmNlOiBBcnJheUJ1ZmZlckxpa2UpIHtcclxuICAgIGNvbnN0IGVuY29kaW5ncyA9IFwiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrL1wiO1xyXG4gICAgbGV0IGJhc2U2NCAgICAgID0gXCJcIjtcclxuXHJcbiAgICBjb25zdCBieXRlcyAgICAgICAgICA9IG5ldyBVaW50OEFycmF5KHNvdXJjZSk7XHJcbiAgICBjb25zdCBieXRlX2xlbmd0aCAgICA9IGJ5dGVzLmJ5dGVMZW5ndGg7XHJcbiAgICBjb25zdCBieXRlX3JlbWluZGVyICA9IGJ5dGVfbGVuZ3RoICUgMztcclxuICAgIGNvbnN0IG1haW5fbGVuZ3RoICAgID0gYnl0ZV9sZW5ndGggLSBieXRlX3JlbWluZGVyO1xyXG5cclxuICAgIGxldCBhLCBiLCBjLCBkO1xyXG4gICAgbGV0IGNodW5rO1xyXG5cclxuICAgIC8vIE1haW4gbG9vcCBkZWFscyB3aXRoIGJ5dGVzIGluIGNodW5rcyBvZiAzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG1haW5fbGVuZ3RoOyBpID0gaSArIDMpIHtcclxuICAgICAgICAvLyBDb21iaW5lIHRoZSB0aHJlZSBieXRlcyBpbnRvIGEgc2luZ2xlIGludGVnZXJcclxuICAgICAgICBjaHVuayA9IChieXRlc1tpXSA8PCAxNikgfCAoYnl0ZXNbaSArIDFdIDw8IDgpIHwgYnl0ZXNbaSArIDJdO1xyXG5cclxuICAgICAgICAvLyBVc2UgYml0bWFza3MgdG8gZXh0cmFjdCA2LWJpdCBzZWdtZW50cyBmcm9tIHRoZSB0cmlwbGV0XHJcbiAgICAgICAgYSA9IChjaHVuayAmIDE2NTE1MDcyKSA+PiAxODsgLy8gMTY1MTUwNzIgPSAoMl42IC0gMSkgPDwgMThcclxuICAgICAgICBiID0gKGNodW5rICYgMjU4MDQ4KSAgID4+IDEyOyAvLyAyNTgwNDggICA9ICgyXjYgLSAxKSA8PCAxMlxyXG4gICAgICAgIGMgPSAoY2h1bmsgJiA0MDMyKSAgICAgPj4gIDY7IC8vIDQwMzIgICAgID0gKDJeNiAtIDEpIDw8ICA2XHJcbiAgICAgICAgZCA9IChjaHVuayAmIDYzKSAgICAgICA+PiAgMDsgLy8gNjMgICAgICAgPSAoMl42IC0gMSkgPDwgIDBcclxuXHJcbiAgICAgICAgLy8gQ29udmVydCB0aGUgcmF3IGJpbmFyeSBzZWdtZW50cyB0byB0aGUgYXBwcm9wcmlhdGUgQVNDSUkgZW5jb2RpbmdcclxuICAgICAgICBiYXNlNjQgKz0gZW5jb2RpbmdzW2FdICsgZW5jb2RpbmdzW2JdICsgZW5jb2RpbmdzW2NdICsgZW5jb2RpbmdzW2RdO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlYWwgd2l0aCB0aGUgcmVtYWluaW5nIGJ5dGVzIGFuZCBwYWRkaW5nXHJcbiAgICBpZiAoYnl0ZV9yZW1pbmRlciA9PSAxKSB7XHJcbiAgICAgICAgY2h1bmsgPSBieXRlc1ttYWluX2xlbmd0aF07XHJcblxyXG4gICAgICAgIGEgPSAoY2h1bmsgJiAyNTIpID4+IDI7IC8vIDI1MiA9ICgyXjYgLSAxKSA8PCAyXHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgNCBsZWFzdCBzaWduaWZpY2FudCBiaXRzIHRvIHplcm9cclxuICAgICAgICBiID0gKGNodW5rICYgMykgICA8PCA0OyAvLyAzICAgPSAyXjIgLSAxXHJcblxyXG4gICAgICAgIGJhc2U2NCArPSBlbmNvZGluZ3NbYV0gKyBlbmNvZGluZ3NbYl0gKyAnPT0nO1xyXG4gICAgfSBlbHNlIGlmIChieXRlX3JlbWluZGVyID09IDIpIHtcclxuICAgICAgICBjaHVuayA9IChieXRlc1ttYWluX2xlbmd0aF0gPDwgOCkgfCBieXRlc1ttYWluX2xlbmd0aCArIDFdO1xyXG5cclxuICAgICAgICBhID0gKGNodW5rICYgNjQ1MTIpID4+IDEwOyAvLyA2NDUxMiA9ICgyXjYgLSAxKSA8PCAxMFxyXG4gICAgICAgIGIgPSAoY2h1bmsgJiAxMDA4KSAgPj4gIDQ7IC8vIDEwMDggID0gKDJeNiAtIDEpIDw8ICA0XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgMiBsZWFzdCBzaWduaWZpY2FudCBiaXRzIHRvIHplcm9cclxuICAgICAgICBjID0gKGNodW5rICYgMTUpICAgIDw8ICAyOyAvLyAxNSAgICA9IDJeNCAtIDFcclxuXHJcbiAgICAgICAgYmFzZTY0ICs9IGVuY29kaW5nc1thXSArIGVuY29kaW5nc1tiXSArIGVuY29kaW5nc1tjXSArICc9JztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gYmFzZTY0XHJcbn1cclxuXHJcbi8qXHJcbmNsYXNzIFRlc3RQcm94eSBleHRlbmRzIGJpcGMuTWV0aG9kUHJveHkge1xyXG4gICAgY29uc3RydWN0b3IocGFyYW1zOiBiaXBjLk1ldGhvZFByb3h5Q29ubmVjdFBhcmFtZXRlcnMpIHtcclxuICAgICAgICBzdXBlcihiaXBjLmdldF9oYW5kbGVyKCksIHBhcmFtcy5jaGFubmVsX2lkICYmIHBhcmFtcy5jbGllbnRfaWQgPyBwYXJhbXMgOiB1bmRlZmluZWQpO1xyXG5cclxuICAgICAgICBpZighdGhpcy5pc19zbGF2ZSgpKSB7XHJcbiAgICAgICAgICAgIHRoaXMucmVnaXN0ZXJfbWV0aG9kKHRoaXMuYWRkX3NsYXZlKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYoIXRoaXMuaXNfbWFzdGVyKCkpIHtcclxuICAgICAgICAgICAgdGhpcy5yZWdpc3Rlcl9tZXRob2QodGhpcy5zYXlfaGVsbG8pO1xyXG4gICAgICAgICAgICB0aGlzLnJlZ2lzdGVyX21ldGhvZCh0aGlzLmFkZF9tYXN0ZXIpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzZXR1cCgpIHtcclxuICAgICAgICBzdXBlci5zZXR1cCgpO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBvbl9jb25uZWN0ZWQoKSB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSVBDLCBcIlRlc3QgcHJveHkgY29ubmVjdGVkXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBvbl9kaXNjb25uZWN0ZWQoKSB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSVBDLCBcIlRlc3QgcHJveHkgZGlzY29ubmVjdGVkXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgYXN5bmMgc2F5X2hlbGxvKCkgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIFwiSGVsbG8gV29ybGRcIik7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBhZGRfc2xhdmUoYTogbnVtYmVyLCBiOiBudW1iZXIpIDogUHJvbWlzZTxudW1iZXI+IHtcclxuICAgICAgICByZXR1cm4gYSArIGI7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBhZGRfbWFzdGVyKGE6IG51bWJlciwgYjogbnVtYmVyKSA6IFByb21pc2U8bnVtYmVyPiB7XHJcbiAgICAgICAgcmV0dXJuIGEgKiBiO1xyXG4gICAgfVxyXG59XHJcbmludGVyZmFjZSBXaW5kb3cge1xyXG4gICAgcHJveHlfaW5zdGFuY2U6IFRlc3RQcm94eSAmIHt1cmw6ICgpID0+IHN0cmluZ307XHJcbn1cclxuKi9cclxuXHJcbmZ1bmN0aW9uIGhhbmRsZV9jb25uZWN0X3JlcXVlc3QocHJvcGVydGllczogYmlwYy5jb25uZWN0LkNvbm5lY3RSZXF1ZXN0RGF0YSwgY29ubmVjdGlvbjogQ29ubmVjdGlvbkhhbmRsZXIpIHtcclxuICAgIGNvbnN0IHByb2ZpbGVfdXVpZCA9IHByb3BlcnRpZXMucHJvZmlsZSB8fCAocHJvZmlsZXMuZGVmYXVsdF9wcm9maWxlKCkgfHwge2lkOiAnZGVmYXVsdCd9KS5pZDtcclxuICAgIGNvbnN0IHByb2ZpbGUgPSBwcm9maWxlcy5maW5kX3Byb2ZpbGUocHJvZmlsZV91dWlkKSB8fCBwcm9maWxlcy5kZWZhdWx0X3Byb2ZpbGUoKTtcclxuICAgIGNvbnN0IHVzZXJuYW1lID0gcHJvcGVydGllcy51c2VybmFtZSB8fCBwcm9maWxlLmNvbm5lY3RfdXNlcm5hbWUoKTtcclxuXHJcbiAgICBjb25zdCBwYXNzd29yZCA9IHByb3BlcnRpZXMucGFzc3dvcmQgPyBwcm9wZXJ0aWVzLnBhc3N3b3JkLnZhbHVlIDogXCJcIjtcclxuICAgIGNvbnN0IHBhc3N3b3JkX2hhc2hlZCA9IHByb3BlcnRpZXMucGFzc3dvcmQgPyBwcm9wZXJ0aWVzLnBhc3N3b3JkLmhhc2hlZCA6IGZhbHNlO1xyXG5cclxuICAgIGlmKHByb2ZpbGUgJiYgcHJvZmlsZS52YWxpZCgpKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbi5zdGFydENvbm5lY3Rpb24ocHJvcGVydGllcy5hZGRyZXNzLCBwcm9maWxlLCB0cnVlLCB7XHJcbiAgICAgICAgICAgIG5pY2tuYW1lOiB1c2VybmFtZSxcclxuICAgICAgICAgICAgcGFzc3dvcmQ6IHBhc3N3b3JkLmxlbmd0aCA+IDAgPyB7XHJcbiAgICAgICAgICAgICAgICBwYXNzd29yZDogcGFzc3dvcmQsXHJcbiAgICAgICAgICAgICAgICBoYXNoZWQ6IHBhc3N3b3JkX2hhc2hlZFxyXG4gICAgICAgICAgICB9IDogdW5kZWZpbmVkXHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgc2VydmVyX2Nvbm5lY3Rpb25zLnNldF9hY3RpdmVfY29ubmVjdGlvbl9oYW5kbGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICBNb2RhbHMuc3Bhd25Db25uZWN0TW9kYWwoe30se1xyXG4gICAgICAgICAgICB1cmw6IHByb3BlcnRpZXMuYWRkcmVzcyxcclxuICAgICAgICAgICAgZW5mb3JjZTogdHJ1ZVxyXG4gICAgICAgIH0sIHtcclxuICAgICAgICAgICAgcHJvZmlsZTogcHJvZmlsZSxcclxuICAgICAgICAgICAgZW5mb3JjZTogdHJ1ZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBtYWluKCkge1xyXG4gICAgLypcclxuICAgIHdpbmRvdy5wcm94eV9pbnN0YW5jZSA9IG5ldyBUZXN0UHJveHkoe1xyXG4gICAgICAgIGNsaWVudF9pZDogc2V0dGluZ3Muc3RhdGljX2dsb2JhbDxzdHJpbmc+KFwicHJveHlfY2xpZW50X2lkXCIsIHVuZGVmaW5lZCksXHJcbiAgICAgICAgY2hhbm5lbF9pZDogc2V0dGluZ3Muc3RhdGljX2dsb2JhbDxzdHJpbmc+KFwicHJveHlfY2hhbm5lbF9pZFwiLCB1bmRlZmluZWQpXHJcbiAgICB9KSBhcyBhbnk7XHJcbiAgICBpZih3aW5kb3cucHJveHlfaW5zdGFuY2UuaXNfbWFzdGVyKCkpIHtcclxuICAgICAgICB3aW5kb3cucHJveHlfaW5zdGFuY2Uuc2V0dXAoKTtcclxuICAgICAgICB3aW5kb3cucHJveHlfaW5zdGFuY2UudXJsID0gKCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBkYXRhID0gd2luZG93LnByb3h5X2luc3RhbmNlLmdlbmVyYXRlX2Nvbm5lY3RfcGFyYW1ldGVycygpO1xyXG4gICAgICAgICAgICByZXR1cm4gXCJwcm94eV9jaGFubmVsX2lkPVwiICsgZGF0YS5jaGFubmVsX2lkICsgXCImcHJveHlfY2xpZW50X2lkPVwiICsgZGF0YS5jbGllbnRfaWQ7XHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuICAgICovXHJcbiAgICAvL2h0dHA6Ly9sb2NhbGhvc3Q6NjMzNDMvV2ViLUNsaWVudC9pbmRleC5waHA/X2lqdD1vbWNwbXQ4YjlobmpsZmd1aDhhamdyZ29sciZkZWZhdWx0X2Nvbm5lY3RfdXJsPXRydWUmZGVmYXVsdF9jb25uZWN0X3R5cGU9dGVhbXNwZWFrJmRlZmF1bHRfY29ubmVjdF91cmw9bG9jYWxob3N0JTNBOTk4NyZkaXNhYmxlVW5sb2FkRGlhbG9nPTEmbG9hZGVyX2lnbm9yZV9hZ2U9MVxyXG5cclxuICAgIC8qIGluaXRpYWxpemUgZm9udCAqL1xyXG4gICAge1xyXG4gICAgICAgIGNvbnN0IGZvbnQgPSBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9GT05UX1NJWkUsIDE0KTsgLy9wYXJzZUludChnZXRDb21wdXRlZFN0eWxlKGRvY3VtZW50LmJvZHkpLmZvbnRTaXplKVxyXG4gICAgICAgICQoZG9jdW1lbnQuYm9keSkuY3NzKFwiZm9udC1zaXplXCIsIGZvbnQgKyBcInB4XCIpO1xyXG4gICAgfVxyXG5cclxuICAgIC8qIGNvbnRleHQgbWVudSBwcmV2ZW50ICovXHJcbiAgICAkKGRvY3VtZW50KS5vbignY29udGV4dG1lbnUnLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQuaXNEZWZhdWx0UHJldmVudGVkKCkpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaWYoIXNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0RJU0FCTEVfR0xPQkFMX0NPTlRFWFRfTUVOVSkpXHJcbiAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICB0b3BfbWVudS5pbml0aWFsaXplKCk7XHJcblxyXG4gICAgc2VydmVyX2Nvbm5lY3Rpb25zID0gbmV3IFNlcnZlckNvbm5lY3Rpb25NYW5hZ2VyKCQoXCIjY29ubmVjdGlvbi1oYW5kbGVyc1wiKSk7XHJcbiAgICBjb250cm9sX2Jhci5pbml0aWFsaXNlKCk7IC8qIGJlZm9yZSBjb25uZWN0aW9uIGhhbmRsZXIgdG8gYWxsb3cgcHJvcGVydHkgYXBwbHkgKi9cclxuXHJcbiAgICBjb25zdCBpbml0aWFsX2hhbmRsZXIgPSBzZXJ2ZXJfY29ubmVjdGlvbnMuc3Bhd25fc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgaW5pdGlhbF9oYW5kbGVyLmFjcXVpcmVfcmVjb3JkZXIoZGVmYXVsdF9yZWNvcmRlciwgZmFsc2UpO1xyXG4gICAgY29udHJvbF9iYXIuc2V0X2Nvbm5lY3Rpb25faGFuZGxlcihpbml0aWFsX2hhbmRsZXIpO1xyXG4gICAgLyoqIFNldHVwIHRoZSBYRiBmb3J1bSBpZGVudGl0eSAqKi9cclxuICAgIHByb2ZpbGVzLmlkZW50aXRpZXMudXBkYXRlX2ZvcnVtKCk7XHJcblxyXG4gICAgbGV0IF9yZXNpemVfdGltZW91dDogTm9kZUpTLlRpbWVyO1xyXG4gICAgJCh3aW5kb3cpLm9uKCdyZXNpemUnLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQudGFyZ2V0ICE9PSB3aW5kb3cpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgaWYoX3Jlc2l6ZV90aW1lb3V0KVxyXG4gICAgICAgICAgICBjbGVhclRpbWVvdXQoX3Jlc2l6ZV90aW1lb3V0KTtcclxuICAgICAgICBfcmVzaXplX3RpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IGNvbm5lY3Rpb24gb2Ygc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKCkpXHJcbiAgICAgICAgICAgICAgICBjb25uZWN0aW9uLmludm9rZV9yZXNpemVkX29uX2FjdGl2YXRlID0gdHJ1ZTtcclxuICAgICAgICAgICAgY29uc3QgYWN0aXZlX2Nvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgICAgICBpZihhY3RpdmVfY29ubmVjdGlvbilcclxuICAgICAgICAgICAgICAgIGFjdGl2ZV9jb25uZWN0aW9uLnJlc2l6ZV9lbGVtZW50cygpO1xyXG4gICAgICAgICAgICAkKFwiLndpbmRvdy1yZXNpemUtbGlzdGVuZXJcIikudHJpZ2dlcigncmVzaXplJyk7XHJcbiAgICAgICAgfSwgMTAwMCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzdGF0cy5pbml0aWFsaXplKHtcclxuICAgICAgICB2ZXJib3NlOiB0cnVlLFxyXG4gICAgICAgIGFub255bWl6ZV9pcF9hZGRyZXNzZXM6IHRydWUsXHJcbiAgICAgICAgdm9sYXRpbGVfY29sbGVjdGlvbl9vbmx5OiBmYWxzZVxyXG4gICAgfSk7XHJcbiAgICBzdGF0cy5yZWdpc3Rlcl91c2VyX2NvdW50X2xpc3RlbmVyKHN0YXR1cyA9PiB7XHJcbiAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuU1RBVElTVElDUywgdHIoXCJSZWNlaXZlZCB1c2VyIGNvdW50IHVwZGF0ZTogJW9cIiksIHN0YXR1cyk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBzZXJ2ZXJfY29ubmVjdGlvbnMuc2V0X2FjdGl2ZV9jb25uZWN0aW9uX2hhbmRsZXIoc2VydmVyX2Nvbm5lY3Rpb25zLnNlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXJzKClbMF0pO1xyXG5cclxuXHJcbiAgICAoPGFueT53aW5kb3cpLnRlc3RfdXBsb2FkID0gKG1lc3NhZ2U/OiBzdHJpbmcpID0+IHtcclxuICAgICAgICBtZXNzYWdlID0gbWVzc2FnZSB8fCBcIkhlbGxvIFdvcmxkXCI7XHJcblxyXG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgIGNvbm5lY3Rpb24uZmlsZU1hbmFnZXIudXBsb2FkX2ZpbGUoe1xyXG4gICAgICAgICAgICBzaXplOiBtZXNzYWdlLmxlbmd0aCxcclxuICAgICAgICAgICAgb3ZlcndyaXRlOiB0cnVlLFxyXG4gICAgICAgICAgICBjaGFubmVsOiBjb25uZWN0aW9uLmdldENsaWVudCgpLmN1cnJlbnRDaGFubmVsKCksXHJcbiAgICAgICAgICAgIG5hbWU6ICcvSGVsbG9Xb3JsZC50eHQnLFxyXG4gICAgICAgICAgICBwYXRoOiAnJ1xyXG4gICAgICAgIH0pLnRoZW4oa2V5ID0+IHtcclxuICAgICAgICAgICAgY29uc3QgdXBsb2FkID0gbmV3IFJlcXVlc3RGaWxlVXBsb2FkKGtleSk7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheShtZXNzYWdlLmxlbmd0aCk7XHJcbiAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgIGZvcihsZXQgaW5kZXggPSAwOyBpbmRleCA8IG1lc3NhZ2UubGVuZ3RoOyBpbmRleCsrKVxyXG4gICAgICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleF0gPSBtZXNzYWdlLmNoYXJDb2RlQXQoaW5kZXgpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB1cGxvYWQucHV0X2RhdGEoYnVmZmVyKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSlcclxuICAgIH07XHJcblxyXG4gICAgLyogc2NoZWR1bGUgaXQgYSBiaXQgbGF0ZXIgdGhlbiB0aGUgbWFpbiBiZWNhdXNlIHRoZSBtYWluIGZ1bmN0aW9uIGlzIHN0aWxsIHdpdGhpbiB0aGUgbG9hZGVyICovXHJcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICBjb25zdCBjb25uZWN0aW9uID0gc2VydmVyX2Nvbm5lY3Rpb25zLmFjdGl2ZV9jb25uZWN0aW9uX2hhbmRsZXIoKTtcclxuICAgICAgICAvKlxyXG4gICAgICAgIE1vZGFscy5jcmVhdGVDaGFubmVsTW9kYWwoY29ubmVjdGlvbiwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIGNvbm5lY3Rpb24ucGVybWlzc2lvbnMsIChjYiwgcGVybXMpID0+IHtcclxuICAgICAgICAgICAgXHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgKi9cclxuICAgICAgIC8vIE1vZGFscy5vcGVuU2VydmVySW5mbyhjb25uZWN0aW9uLmNoYW5uZWxUcmVlLnNlcnZlcik7XHJcbiAgICAgICAgLy9Nb2RhbHMuY3JlYXRlU2VydmVyTW9kYWwoY29ubmVjdGlvbi5jaGFubmVsVHJlZS5zZXJ2ZXIsIHByb3BlcnRpZXMgPT4gUHJvbWlzZS5yZXNvbHZlKCkpO1xyXG5cclxuICAgICAgICAvL01vZGFscy5vcGVuQ2xpZW50SW5mbyhjb25uZWN0aW9uLmdldENsaWVudCgpKTtcclxuICAgICAgICAvL01vZGFscy5vcGVuU2VydmVySW5mb0JhbmR3aWR0aChjb25uZWN0aW9uLmNoYW5uZWxUcmVlLnNlcnZlcik7XHJcblxyXG4gICAgICAgIC8vTW9kYWxzLm9wZW5CYW5MaXN0KGNvbm5lY3Rpb24pO1xyXG4gICAgICAgIC8qXHJcbiAgICAgICAgTW9kYWxzLnNwYXduQmFuQ2xpZW50KGNvbm5lY3Rpb24sW1xyXG4gICAgICAgICAgICB7bmFtZTogXCJXb2x2ZXJpbkRFVlwiLCB1bmlxdWVfaWQ6IFwiWFhYWFwifSxcclxuICAgICAgICAgICAge25hbWU6IFwiV29sdmVyaW5ERVZcIiwgdW5pcXVlX2lkOiBcIlhYWFhcIn0sXHJcbiAgICAgICAgICAgIHtuYW1lOiBcIldvbHZlcmluREVWXCIsIHVuaXF1ZV9pZDogXCJYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFwifSxcclxuICAgICAgICAgICAge25hbWU6IFwiV29sdmVyaW5ERVZcIiwgdW5pcXVlX2lkOiBcIllZWVwifVxyXG4gICAgICAgIF0sICgpID0+IHt9KTtcclxuICAgICAgICAqL1xyXG4gICAgfSwgNDAwMCk7XHJcbiAgICAvL01vZGFscy5zcGF3blNldHRpbmdzTW9kYWwoXCJpZGVudGl0eS1wcm9maWxlc1wiKTtcclxuICAgIC8vTW9kYWxzLnNwYXduS2V5U2VsZWN0KGNvbnNvbGUubG9nKTtcclxuICAgIC8vTW9kYWxzLnNwYXduQm9va21hcmtNb2RhbCgpO1xyXG5cclxuICAgIC8qXHJcbiAgICB7XHJcbiAgICAgICAgY29uc3QgbW9kYWwgPSBjcmVhdGVNb2RhbCh7XHJcbiAgICAgICAgICAgIGhlYWRlcjogdHIoXCJUZXN0IE5ldCBHcmFwaFwiKSxcclxuICAgICAgICAgICAgYm9keTogKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgY2FudmFzID0gJC5zcGF3bihcImNhbnZhc1wiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5jc3MoXCJwb3NpdGlvblwiLCBcImFic29sdXRlXCIpXHJcbiAgICAgICAgICAgICAgICAgICAgLmNzcyh7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvcDogMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tOiAwLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICByaWdodDogMCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgbGVmdDogMFxyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiAkLnNwYXduKFwiZGl2XCIpXHJcbiAgICAgICAgICAgICAgICAgICAgLmNzcyhcImhlaWdodFwiLCBcIjVlbVwiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5jc3MoXCJ3aWR0aFwiLCBcIjMwZW1cIilcclxuICAgICAgICAgICAgICAgICAgICAuY3NzKFwicG9zaXRpb25cIiwgXCJyZWxhdGl2ZVwiKVxyXG4gICAgICAgICAgICAgICAgICAgIC5hcHBlbmQoY2FudmFzKTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZm9vdGVyOiBudWxsXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGdyYXBoID0gbmV3IG5ldC5ncmFwaC5HcmFwaChtb2RhbC5odG1sVGFnLmZpbmQoXCJjYW52YXNcIilbMF0gYXMgYW55KTtcclxuICAgICAgICBncmFwaC5pbml0aWFsaXplKCk7XHJcblxyXG4gICAgICAgIG1vZGFsLmNsb3NlX2xpc3RlbmVyLnB1c2goKCkgPT4gZ3JhcGgudGVybWluYXRlKCkpO1xyXG4gICAgICAgIG1vZGFsLm9wZW4oKTtcclxuICAgIH1cclxuICAgICAqL1xyXG5cclxuXHJcbiAgICAvKiBmb3IgdGVzdGluZyAqL1xyXG4gICAgaWYoc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfVVNFUl9JU19ORVcpKSB7XHJcbiAgICAgICAgY29uc3QgbW9kYWwgPSBNb2RhbHMub3Blbk1vZGFsTmV3Y29tZXIoKTtcclxuICAgICAgICBtb2RhbC5jbG9zZV9saXN0ZW5lci5wdXNoKCgpID0+IHNldHRpbmdzLmNoYW5nZUdsb2JhbChTZXR0aW5ncy5LRVlfVVNFUl9JU19ORVcsIGZhbHNlKSk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmNvbnN0IHRhc2tfdGVhd2ViX3N0YXJ0ZXI6IGxvYWRlci5UYXNrID0ge1xyXG4gICAgbmFtZTogXCJ2b2ljZSBhcHAgc3RhcnRlclwiLFxyXG4gICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplX2FwcCgpO1xyXG4gICAgICAgICAgICBtYWluKCk7XHJcbiAgICAgICAgICAgIGlmKCFhdWRpby5wbGF5ZXIuaW5pdGlhbGl6ZWQoKSkge1xyXG4gICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuVk9JQ0UsIHRyKFwiSW5pdGlhbGl6ZSBhdWRpbyBjb250cm9sbGVyIGxhdGVyIVwiKSk7XHJcbiAgICAgICAgICAgICAgICBpZighYXVkaW8ucGxheWVyLmluaXRpYWxpemVGcm9tR2VzdHVyZSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJNaXNzaW5nIGF1ZGlvLnBsYXllci5pbml0aWFsaXplRnJvbUdlc3R1cmVcIikpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgJChkb2N1bWVudCkub25lKCdjbGljaycsIGV2ZW50ID0+IGF1ZGlvLnBsYXllci5pbml0aWFsaXplRnJvbUdlc3R1cmUoKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChleCkge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGV4LnN0YWNrKTtcclxuICAgICAgICAgICAgaWYoZXggaW5zdGFuY2VvZiBSZWZlcmVuY2VFcnJvciB8fCBleCBpbnN0YW5jZW9mIFR5cGVFcnJvcilcclxuICAgICAgICAgICAgICAgIGV4ID0gZXgubmFtZSArIFwiOiBcIiArIGV4Lm1lc3NhZ2U7XHJcbiAgICAgICAgICAgIGxvYWRlci5jcml0aWNhbF9lcnJvcihcIkZhaWxlZCB0byBpbnZva2UgbWFpbiBmdW5jdGlvbjo8YnI+XCIgKyBleCk7XHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByaW9yaXR5OiAxMFxyXG59O1xyXG5cclxuY29uc3QgdGFza19jb25uZWN0X2hhbmRsZXI6IGxvYWRlci5UYXNrID0ge1xyXG4gICAgbmFtZTogXCJDb25uZWN0IGhhbmRsZXJcIixcclxuICAgIGZ1bmN0aW9uOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgICAgY29uc3QgYWRkcmVzcyA9IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfQ09OTkVDVF9BRERSRVNTLCBcIlwiKTtcclxuICAgICAgICBjb25zdCBjaGFuZGxlciA9IGJpcGMuZ2V0X2Nvbm5lY3RfaGFuZGxlcigpO1xyXG4gICAgICAgIGlmKHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRkxBR19DT05ORUNUX0RFRkFVTFQsIGZhbHNlKSAmJiBhZGRyZXNzKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGNvbm5lY3RfZGF0YSA9IHtcclxuICAgICAgICAgICAgICAgIGFkZHJlc3M6IGFkZHJlc3MsXHJcblxyXG4gICAgICAgICAgICAgICAgcHJvZmlsZTogc2V0dGluZ3Muc3RhdGljKFNldHRpbmdzLktFWV9DT05ORUNUX1BST0ZJTEUsIFwiXCIpLFxyXG4gICAgICAgICAgICAgICAgdXNlcm5hbWU6IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfQ09OTkVDVF9VU0VSTkFNRSwgXCJcIiksXHJcblxyXG4gICAgICAgICAgICAgICAgcGFzc3dvcmQ6IHtcclxuICAgICAgICAgICAgICAgICAgICB2YWx1ZTogc2V0dGluZ3Muc3RhdGljKFNldHRpbmdzLktFWV9DT05ORUNUX1BBU1NXT1JELCBcIlwiKSxcclxuICAgICAgICAgICAgICAgICAgICBoYXNoZWQ6IHNldHRpbmdzLnN0YXRpYyhTZXR0aW5ncy5LRVlfRkxBR19DT05ORUNUX1BBU1NXT1JELCBmYWxzZSlcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIGlmKGNoYW5kbGVyKSB7XHJcbiAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGNoYW5kbGVyLnBvc3RfY29ubmVjdF9yZXF1ZXN0KGNvbm5lY3RfZGF0YSwgKCkgPT4gbmV3IFByb21pc2U8Ym9vbGVhbj4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBzcGF3blllc05vKHRyKFwiQW5vdGhlciBUZWFXZWIgaW5zdGFuY2UgaXMgYWxyZWFkeSBydW5uaW5nXCIpLCB0cmEoXCJBbm90aGVyIFRlYVdlYiBpbnN0YW5jZSBpcyBhbHJlYWR5IHJ1bm5pbmcuezpicjp9V291bGQgeW91IGxpa2UgdG8gY29ubmVjdCB0aGVyZT9cIiksIHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9LCB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLm9wZW4oKTtcclxuICAgICAgICAgICAgICAgICAgICB9KSk7XHJcbiAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuQ0xJRU5ULCB0cihcIkV4ZWN1dGVkIGNvbm5lY3Qgc3VjY2Vzc2Z1bGx5IGluIGFub3RoZXIgYnJvd3NlciB3aW5kb3cuIENsb3NpbmcgdGhpcyB3aW5kb3dcIikpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJZb3UncmUgY29ubmVjdGluZyB0byB7MH0gd2l0aGluIHRoZSBvdGhlciBUZWFXZWIgaW5zdGFuY2Uuezpicjp9XCIgK1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBcIllvdSBjb3VsZCBub3cgY2xvc2UgdGhpcyBwYWdlLlwiO1xyXG4gICAgICAgICAgICAgICAgICAgIGNyZWF0ZUluZm9Nb2RhbChcclxuICAgICAgICAgICAgICAgICAgICAgICAgdHIoXCJDb25uZWN0aW5nIHN1Y2Nlc3NmdWxseSB3aXRoaW4gb3RoZXIgaW5zdGFuY2VcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIE1lc3NhZ2VIZWxwZXIuZm9ybWF0TWVzc2FnZSh0cihtZXNzYWdlKSwgY29ubmVjdF9kYXRhLmFkZHJlc3MpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9vdGVyOiB1bmRlZmluZWRcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICkub3BlbigpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5DTElFTlQsIHRyKFwiRmFpbGVkIHRvIGV4ZWN1dGUgY29ubmVjdCB3aXRoaW4gb3RoZXIgVGVhV2ViIGluc3RhbmNlLiBVc2luZyB0aGlzIG9uZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHtcclxuICAgICAgICAgICAgICAgIHByaW9yaXR5OiAwLFxyXG4gICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IGhhbmRsZV9jb25uZWN0X3JlcXVlc3QoY29ubmVjdF9kYXRhLCBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpIHx8IHNlcnZlcl9jb25uZWN0aW9ucy5zcGF3bl9zZXJ2ZXJfY29ubmVjdGlvbl9oYW5kbGVyKCkpLFxyXG4gICAgICAgICAgICAgICAgbmFtZTogdHIoXCJkZWZhdWx0IHVybCBjb25uZWN0XCIpXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZihjaGFuZGxlcikge1xyXG4gICAgICAgICAgICAvKiBubyBpbnN0YW5jZSBhdmFpbCwgc28gbGV0cyBtYWtlIHVzIGF2YWlsICovXHJcbiAgICAgICAgICAgIGNoYW5kbGVyLmNhbGxiYWNrX2F2YWlsYWJsZSA9IGRhdGEgPT4ge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuICFzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9ESVNBQkxFX01VTFRJX1NFU1NJT04pO1xyXG4gICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgY2hhbmRsZXIuY2FsbGJhY2tfZXhlY3V0ZSA9IGRhdGEgPT4ge1xyXG4gICAgICAgICAgICAgICAgaGFuZGxlX2Nvbm5lY3RfcmVxdWVzdChkYXRhLCBzZXJ2ZXJfY29ubmVjdGlvbnMuc3Bhd25fc2VydmVyX2Nvbm5lY3Rpb25faGFuZGxlcigpKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfdGVhd2ViX3N0YXJ0ZXIpO1xyXG4gICAgfSxcclxuICAgIHByaW9yaXR5OiAxMFxyXG59O1xyXG5cclxuY29uc3QgdGFza19jZXJ0aWZpY2F0ZV9jYWxsYmFjazogbG9hZGVyLlRhc2sgPSB7XHJcbiAgICBuYW1lOiBcImNlcnRpZmljYXRlIGFjY2VwdCB0ZXN0ZXJcIixcclxuICAgIGZ1bmN0aW9uOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgICAgY29uc3QgY2VydGlmaWNhdGVfYWNjZXB0ID0gc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfQ0VSVElGSUNBVEVfQ0FMTEJBQ0ssIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgaWYoY2VydGlmaWNhdGVfYWNjZXB0KSB7XHJcbiAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJVc2luZyB0aGlzIGluc3RhbmNlIGFzIGNlcnRpZmljYXRlIGNhbGxiYWNrLiBJRDogJXNcIiksIGNlcnRpZmljYXRlX2FjY2VwdCk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGJpcGMuZ2V0X2hhbmRsZXIoKS5wb3N0X2NlcnRpZmljYXRlX2FjY3BlY3RlZChjZXJ0aWZpY2F0ZV9hY2NlcHQpO1xyXG4gICAgICAgICAgICAgICAgfSBjYXRjaChlKSB7fSAvL0ZJWE1FIHJlbW92ZSFcclxuICAgICAgICAgICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklQQywgdHIoXCJPdGhlciBpbnN0YW5jZSBoYXMgYWNrbm93bGVkZ2VkIG91dCB3b3JrLiBDbG9zaW5nIHRoaXMgd2luZG93LlwiKSk7XHJcblxyXG4gICAgICAgICAgICAgICAgY29uc3Qgc2Vjb25kc190YWcgPSAkLnNwYXduKFwiYVwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICBsZXQgc2Vjb25kcyA9IDU7XHJcbiAgICAgICAgICAgICAgICBsZXQgaW50ZXJ2YWxfaWQ7XHJcbiAgICAgICAgICAgICAgICBpbnRlcnZhbF9pZCA9IHNldEludGVydmFsKCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBzZWNvbmRzLS07XHJcbiAgICAgICAgICAgICAgICAgICAgc2Vjb25kc190YWcudGV4dChzZWNvbmRzLnRvU3RyaW5nKCkpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZihzZWNvbmRzIDw9IDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KGludGVydmFsX2lkKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJDbG9zaW5nIHdpbmRvd1wiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHdpbmRvdy5jbG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSwgMTAwMCk7XHJcblxyXG4gICAgICAgICAgICAgICAgY29uc3QgbWVzc2FnZSA9XHJcbiAgICAgICAgICAgICAgICAgICAgXCJZb3UndmUgc3VjY2Vzc2Z1bGx5IGFjY2VwdGVkIHRoZSBjZXJ0aWZpY2F0ZS57OmJyOn1cIiArXHJcbiAgICAgICAgICAgICAgICAgICAgXCJUaGlzIHBhZ2Ugd2lsbCBjbG9zZSBpbiB7MH0gc2Vjb25kcy5cIjtcclxuICAgICAgICAgICAgICAgIGNyZWF0ZUluZm9Nb2RhbChcclxuICAgICAgICAgICAgICAgICAgICB0cihcIkNlcnRpZmljYXRlIGFjY2NlcHRlZCBzdWNjZXNzZnVsbHlcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgTWVzc2FnZUhlbHBlci5mb3JtYXRNZXNzYWdlKHRyKG1lc3NhZ2UpLCBzZWNvbmRzX3RhZyksXHJcbiAgICAgICAgICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjbG9zZWFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBmb290ZXI6IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICkub3BlbigpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JUEMsIHRyKFwiRmFpbGVkIHRvIHN1Y2Nlc3NmdWxseSBwb3N0IGNlcnRpZmljYXRlIGFjY2VwdCBzdGF0dXM6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBsb2cuaW5mbyhMb2dDYXRlZ29yeS5JUEMsIHRyKFwiV2UncmUgbm90IHVzZWQgdG8gYWNjZXB0IGNlcnRpZmljYXRlZC4gQm9vdGluZyBhcHAuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfY29ubmVjdF9oYW5kbGVyKTtcclxuICAgIH0sXHJcbiAgICBwcmlvcml0eTogMTBcclxufTtcclxuXHJcbmxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgbmFtZTogXCJqcmVuZGVyZSBpbml0aWFsaXplXCIsXHJcbiAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGlmKCFzZXR1cF9qc3JlbmRlcigpKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIGxvYWRcIjtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2FkZXIuY3JpdGljYWxfZXJyb3IodHIoXCJGYWlsZWQgdG8gc2V0dXAganNyZW5kZXJcIikpO1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIGxvYWQganNyZW5kZXIhICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG4gICAgcHJpb3JpdHk6IDEwMFxyXG59KTtcclxuXHJcbmxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgbmFtZTogXCJhcHAgc3RhcnRlclwiLFxyXG4gICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplKCk7XHJcblxyXG4gICAgICAgICAgICBpZihhcHAuaXNfd2ViKCkpIHtcclxuICAgICAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHRhc2tfY2VydGlmaWNhdGVfY2FsbGJhY2spO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgbG9hZGVyLnJlZ2lzdGVyX3Rhc2sobG9hZGVyLlN0YWdlLkxPQURFRCwgdGFza190ZWF3ZWJfc3RhcnRlcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChleCkge1xyXG4gICAgICAgICAgICBpZihleCBpbnN0YW5jZW9mIEVycm9yIHx8IHR5cGVvZihleC5zdGFjaykgIT09IFwidW5kZWZpbmVkXCIpXHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKCh0ciB8fCAobXNnID0+IG1zZykpKFwiQ3JpdGljYWwgZXJyb3Igc3RhY2sgdHJhY2U6ICVvXCIpLCBleC5zdGFjayk7XHJcblxyXG4gICAgICAgICAgICBpZihleCBpbnN0YW5jZW9mIFJlZmVyZW5jZUVycm9yIHx8IGV4IGluc3RhbmNlb2YgVHlwZUVycm9yKVxyXG4gICAgICAgICAgICAgICAgZXggPSBleC5uYW1lICsgXCI6IFwiICsgZXgubWVzc2FnZTtcclxuICAgICAgICAgICAgbG9hZGVyLmNyaXRpY2FsX2Vycm9yKFwiRmFpbGVkIHRvIGJvb3QgYXBwIGZ1bmN0aW9uOjxicj5cIiArIGV4KTtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG4gICAgcHJpb3JpdHk6IDEwMDBcclxufSk7XHJcblxyXG4iLCJpbXBvcnQge01lc3NhZ2VIZWxwZXJ9IGZyb20gXCIuLi91aS9mcmFtZXMvY2hhdFwiO1xyXG5pbXBvcnQge2NyZWF0ZUVycm9yTW9kYWx9IGZyb20gXCIuLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5pbXBvcnQge2d1aWR9IGZyb20gXCIuLi9jcnlwdG8vdWlkXCI7XHJcbmltcG9ydCB7ZGVjb2RlX2lkZW50aXR5LCBJZGVudGl0aWZ5VHlwZSwgSWRlbnRpdHl9IGZyb20gXCIuL0lkZW50aXR5XCI7XHJcbmltcG9ydCB7c3RhdGljX2ZvcnVtX2lkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL1RlYUZvcnVtSWRlbnRpdHlcIjtcclxuaW1wb3J0IHtUZWFTcGVha0lkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL1RlYW1TcGVha0lkZW50aXR5XCI7XHJcbmltcG9ydCB7QWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9ufSBmcm9tIFwiLi4vY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZVwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuXHJcbmltcG9ydCBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcjtcclxuXHJcbmV4cG9ydCBjbGFzcyBDb25uZWN0aW9uUHJvZmlsZSB7XHJcbiAgICBpZDogc3RyaW5nO1xyXG5cclxuICAgIHByb2ZpbGVfbmFtZTogc3RyaW5nO1xyXG4gICAgZGVmYXVsdF91c2VybmFtZTogc3RyaW5nO1xyXG4gICAgZGVmYXVsdF9wYXNzd29yZDogc3RyaW5nO1xyXG5cclxuICAgIHNlbGVjdGVkX2lkZW50aXR5X3R5cGU6IHN0cmluZyA9IFwidW5zZXRcIjtcclxuICAgIGlkZW50aXRpZXM6IHsgW2tleTogc3RyaW5nXTogSWRlbnRpdHkgfSA9IHt9O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGlkOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmlkID0gaWQ7XHJcbiAgICB9XHJcblxyXG4gICAgY29ubmVjdF91c2VybmFtZSgpOiBzdHJpbmcge1xyXG4gICAgICAgIGlmICh0aGlzLmRlZmF1bHRfdXNlcm5hbWUgJiYgdGhpcy5kZWZhdWx0X3VzZXJuYW1lICE9PSBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiKVxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5kZWZhdWx0X3VzZXJuYW1lO1xyXG5cclxuICAgICAgICBsZXQgc2VsZWN0ZWQgPSB0aGlzLnNlbGVjdGVkX2lkZW50aXR5KCk7XHJcbiAgICAgICAgbGV0IG5hbWUgPSBzZWxlY3RlZCA/IHNlbGVjdGVkLmZhbGxiYWNrX25hbWUoKSA6IHVuZGVmaW5lZDtcclxuICAgICAgICByZXR1cm4gbmFtZSB8fCBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiO1xyXG4gICAgfVxyXG5cclxuICAgIHNlbGVjdGVkX2lkZW50aXR5KGN1cnJlbnRfdHlwZT86IElkZW50aXRpZnlUeXBlKTogSWRlbnRpdHkge1xyXG4gICAgICAgIGlmICghY3VycmVudF90eXBlKVxyXG4gICAgICAgICAgICBjdXJyZW50X3R5cGUgPSB0aGlzLnNlbGVjdGVkX3R5cGUoKTtcclxuXHJcbiAgICAgICAgaWYgKGN1cnJlbnRfdHlwZSA9PT0gdW5kZWZpbmVkKVxyXG4gICAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG5cclxuICAgICAgICBpZiAoY3VycmVudF90eXBlID09IElkZW50aXRpZnlUeXBlLlRFQUZPUk8pIHtcclxuICAgICAgICAgICAgcmV0dXJuIHN0YXRpY19mb3J1bV9pZGVudGl0eSgpO1xyXG4gICAgICAgIH0gZWxzZSBpZiAoY3VycmVudF90eXBlID09IElkZW50aXRpZnlUeXBlLlRFQU1TUEVBSyB8fCBjdXJyZW50X3R5cGUgPT0gSWRlbnRpdGlmeVR5cGUuTklDS05BTUUpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuaWRlbnRpdGllc1tJZGVudGl0aWZ5VHlwZVtjdXJyZW50X3R5cGVdLnRvTG93ZXJDYXNlKCldO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZWxlY3RlZF90eXBlPygpOiBJZGVudGl0aWZ5VHlwZSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRfaWRlbnRpdHlfdHlwZSA/IElkZW50aXRpZnlUeXBlW3RoaXMuc2VsZWN0ZWRfaWRlbnRpdHlfdHlwZS50b1VwcGVyQ2FzZSgpXSA6IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZXRfaWRlbnRpdHkodHlwZTogSWRlbnRpdGlmeVR5cGUsIGlkZW50aXR5OiBJZGVudGl0eSkge1xyXG4gICAgICAgIHRoaXMuaWRlbnRpdGllc1tJZGVudGl0aWZ5VHlwZVt0eXBlXS50b0xvd2VyQ2FzZSgpXSA9IGlkZW50aXR5O1xyXG4gICAgfVxyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyPyhjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5zZWxlY3RlZF9pZGVudGl0eSgpO1xyXG4gICAgICAgIGlmICghaWRlbnRpdHkpXHJcbiAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICAgICAgcmV0dXJuIGlkZW50aXR5LnNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZT8oKTogc3RyaW5nIHtcclxuICAgICAgICBjb25zdCBpZGVudGl0eV9kYXRhID0ge307XHJcbiAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gdGhpcy5pZGVudGl0aWVzKVxyXG4gICAgICAgICAgICBpZiAodGhpcy5pZGVudGl0aWVzW2tleV0pXHJcbiAgICAgICAgICAgICAgICBpZGVudGl0eV9kYXRhW2tleV0gPSB0aGlzLmlkZW50aXRpZXNba2V5XS5lbmNvZGUoKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgICAgdmVyc2lvbjogMSxcclxuICAgICAgICAgICAgdXNlcm5hbWU6IHRoaXMuZGVmYXVsdF91c2VybmFtZSxcclxuICAgICAgICAgICAgcGFzc3dvcmQ6IHRoaXMuZGVmYXVsdF9wYXNzd29yZCxcclxuICAgICAgICAgICAgcHJvZmlsZV9uYW1lOiB0aGlzLnByb2ZpbGVfbmFtZSxcclxuICAgICAgICAgICAgaWRlbnRpdHlfdHlwZTogdGhpcy5zZWxlY3RlZF9pZGVudGl0eV90eXBlLFxyXG4gICAgICAgICAgICBpZGVudGl0eV9kYXRhOiBpZGVudGl0eV9kYXRhLFxyXG4gICAgICAgICAgICBpZDogdGhpcy5pZFxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHZhbGlkKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5zZWxlY3RlZF9pZGVudGl0eSgpO1xyXG4gICAgICAgIGlmICghaWRlbnRpdHkgfHwgIWlkZW50aXR5LnZhbGlkKCkpIHJldHVybiBmYWxzZTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbn1cclxuXHJcbmFzeW5jIGZ1bmN0aW9uIGRlY29kZV9wcm9maWxlKGRhdGEpOiBQcm9taXNlPENvbm5lY3Rpb25Qcm9maWxlIHwgc3RyaW5nPiB7XHJcbiAgICBkYXRhID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgIGlmIChkYXRhLnZlcnNpb24gIT09IDEpXHJcbiAgICAgICAgcmV0dXJuIFwiaW52YWxpZCB2ZXJzaW9uXCI7XHJcblxyXG4gICAgY29uc3QgcmVzdWx0OiBDb25uZWN0aW9uUHJvZmlsZSA9IG5ldyBDb25uZWN0aW9uUHJvZmlsZShkYXRhLmlkKTtcclxuICAgIHJlc3VsdC5kZWZhdWx0X3VzZXJuYW1lID0gZGF0YS51c2VybmFtZTtcclxuICAgIHJlc3VsdC5kZWZhdWx0X3Bhc3N3b3JkID0gZGF0YS5wYXNzd29yZDtcclxuICAgIHJlc3VsdC5wcm9maWxlX25hbWUgPSBkYXRhLnByb2ZpbGVfbmFtZTtcclxuICAgIHJlc3VsdC5zZWxlY3RlZF9pZGVudGl0eV90eXBlID0gKGRhdGEuaWRlbnRpdHlfdHlwZSB8fCBcIlwiKS50b0xvd2VyQ2FzZSgpO1xyXG5cclxuICAgIGlmIChkYXRhLmlkZW50aXR5X2RhdGEpIHtcclxuICAgICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhLmlkZW50aXR5X2RhdGEpIHtcclxuICAgICAgICAgICAgY29uc3QgdHlwZSA9IElkZW50aXRpZnlUeXBlW2tleS50b1VwcGVyQ2FzZSgpIGFzIHN0cmluZ107XHJcbiAgICAgICAgICAgIGNvbnN0IF9kYXRhID0gZGF0YS5pZGVudGl0eV9kYXRhW2tleV07XHJcbiAgICAgICAgICAgIGlmICh0eXBlID09IHVuZGVmaW5lZCkgY29udGludWU7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBpZGVudGl0eSA9IGF3YWl0IGRlY29kZV9pZGVudGl0eSh0eXBlLCBfZGF0YSk7XHJcbiAgICAgICAgICAgIGlmIChpZGVudGl0eSA9PSB1bmRlZmluZWQpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgcmVzdWx0LmlkZW50aXRpZXNba2V5LnRvTG93ZXJDYXNlKCldID0gaWRlbnRpdHk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiByZXN1bHQ7XHJcbn1cclxuXHJcbmludGVyZmFjZSBQcm9maWxlc0RhdGEge1xyXG4gICAgdmVyc2lvbjogbnVtYmVyO1xyXG4gICAgcHJvZmlsZXM6IHN0cmluZ1tdO1xyXG59XHJcblxyXG5sZXQgYXZhaWxhYmxlX3Byb2ZpbGVzOiBDb25uZWN0aW9uUHJvZmlsZVtdID0gW107XHJcblxyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZCgpIHtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcyA9IFtdO1xyXG5cclxuICAgIGNvbnN0IHByb2ZpbGVzX2pzb24gPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInByb2ZpbGVzXCIpO1xyXG4gICAgbGV0IHByb2ZpbGVzX2RhdGE6IFByb2ZpbGVzRGF0YSA9ICgoKSA9PiB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmV0dXJuIHByb2ZpbGVzX2pzb24gPyBKU09OLnBhcnNlKHByb2ZpbGVzX2pzb24pIDoge3ZlcnNpb246IDB9IGFzIGFueTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBkZWJ1Z2dlcjtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkludmFsaWQgcHJvZmlsZSBqc29uISBSZXNldHRpbmcgcHJvZmlsZXMgOiggKCVvKVwiKSwgcHJvZmlsZXNfanNvbik7XHJcbiAgICAgICAgICAgIGNyZWF0ZUVycm9yTW9kYWwodHIoXCJQcm9maWxlIGRhdGEgaW52YWxpZFwiKSwgTWVzc2FnZUhlbHBlci5mb3JtYXRNZXNzYWdlKHRyKFwiVGhlIHByb2ZpbGUgZGF0YSBpcyBpbnZhbGlkLns6YnI6fVRoaXMgbWlnaHQgY2F1c2UgZGF0YSBsb3NzLlwiKSkpLm9wZW4oKTtcclxuICAgICAgICAgICAgcmV0dXJuIHt2ZXJzaW9uOiAwfTtcclxuICAgICAgICB9XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIGlmIChwcm9maWxlc19kYXRhLnZlcnNpb24gPT09IDApIHtcclxuICAgICAgICBwcm9maWxlc19kYXRhID0ge1xyXG4gICAgICAgICAgICB2ZXJzaW9uOiAxLFxyXG4gICAgICAgICAgICBwcm9maWxlczogW11cclxuICAgICAgICB9O1xyXG4gICAgfVxyXG4gICAgaWYgKHByb2ZpbGVzX2RhdGEudmVyc2lvbiA9PSAxKSB7XHJcbiAgICAgICAgZm9yIChjb25zdCBwcm9maWxlX2RhdGEgb2YgcHJvZmlsZXNfZGF0YS5wcm9maWxlcykge1xyXG4gICAgICAgICAgICBjb25zdCBwcm9maWxlID0gYXdhaXQgZGVjb2RlX3Byb2ZpbGUocHJvZmlsZV9kYXRhKTtcclxuICAgICAgICAgICAgaWYgKHR5cGVvZiAocHJvZmlsZSkgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIGxvYWQgcHJvZmlsZS4gUmVhc29uOiAlcywgUHJvZmlsZSBkYXRhOiAlc1wiKSwgcHJvZmlsZSwgcHJvZmlsZXNfZGF0YSk7XHJcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBhdmFpbGFibGVfcHJvZmlsZXMucHVzaChwcm9maWxlKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFmaW5kX3Byb2ZpbGUoXCJkZWZhdWx0XCIpKSB7IC8vQ3JlYXRlIGEgZGVmYXVsdCBwcm9maWxlIGFuZCB0ZWFmb3JvIHByb2ZpbGVcclxuICAgICAgICB7XHJcbiAgICAgICAgICAgIGNvbnN0IHByb2ZpbGUgPSBjcmVhdGVfbmV3X3Byb2ZpbGUoXCJkZWZhdWx0XCIsIFwiZGVmYXVsdFwiKTtcclxuICAgICAgICAgICAgcHJvZmlsZS5kZWZhdWx0X3Bhc3N3b3JkID0gXCJcIjtcclxuICAgICAgICAgICAgcHJvZmlsZS5kZWZhdWx0X3VzZXJuYW1lID0gXCJcIjtcclxuICAgICAgICAgICAgcHJvZmlsZS5wcm9maWxlX25hbWUgPSBcIkRlZmF1bHQgUHJvZmlsZVwiO1xyXG5cclxuICAgICAgICAgICAgLyogZ2VuZXJhdGUgZGVmYXVsdCBpZGVudGl0eSAqL1xyXG4gICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgY29uc3QgaWRlbnRpdHkgPSBhd2FpdCBUZWFTcGVha0lkZW50aXR5LmdlbmVyYXRlX25ldygpO1xyXG4gICAgICAgICAgICAgICAgbGV0IGFjdGl2ZSA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBhY3RpdmUgPSBmYWxzZTtcclxuICAgICAgICAgICAgICAgIH0sIDEwMDApO1xyXG4gICAgICAgICAgICAgICAgYXdhaXQgaWRlbnRpdHkuaW1wcm92ZV9sZXZlbCg4LCAxLCAoKSA9PiBhY3RpdmUpO1xyXG4gICAgICAgICAgICAgICAgcHJvZmlsZS5zZXRfaWRlbnRpdHkoSWRlbnRpdGlmeVR5cGUuVEVBTVNQRUFLLCBpZGVudGl0eSk7XHJcbiAgICAgICAgICAgICAgICBwcm9maWxlLnNlbGVjdGVkX2lkZW50aXR5X3R5cGUgPSBJZGVudGl0aWZ5VHlwZVtJZGVudGl0aWZ5VHlwZS5URUFNU1BFQUtdO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgY3JlYXRlRXJyb3JNb2RhbCh0cihcIkZhaWxlZCB0byBnZW5lcmF0ZSBkZWZhdWx0IGlkZW50aXR5XCIpLCB0cihcIkZhaWxlZCB0byBnZW5lcmF0ZSBkZWZhdWx0IGlkZW50aXR5ITxicj5QbGVhc2UgbWFudWFsbHkgZ2VuZXJhdGUgdGhlIGlkZW50aXR5IHdpdGhpbiB5b3VyIHNldHRpbmdzID0+IHByb2ZpbGVzXCIpKS5vcGVuKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHsgLyogZm9ydW0gaWRlbnRpdHkgKHdvcmtzIG9ubHkgd2hlbiBjb25uZWN0ZWQgdG8gdGhlIGZvcnVtKSAqL1xyXG4gICAgICAgICAgICBjb25zdCBwcm9maWxlID0gY3JlYXRlX25ld19wcm9maWxlKFwiVGVhU3BlYWsgRm9ydW1cIiwgXCJ0ZWFmb3JvXCIpO1xyXG4gICAgICAgICAgICBwcm9maWxlLmRlZmF1bHRfcGFzc3dvcmQgPSBcIlwiO1xyXG4gICAgICAgICAgICBwcm9maWxlLmRlZmF1bHRfdXNlcm5hbWUgPSBcIlwiO1xyXG4gICAgICAgICAgICBwcm9maWxlLnByb2ZpbGVfbmFtZSA9IFwiVGVhU3BlYWsgRm9ydW0gcHJvZmlsZVwiO1xyXG5cclxuICAgICAgICAgICAgcHJvZmlsZS5zZXRfaWRlbnRpdHkoSWRlbnRpdGlmeVR5cGUuVEVBRk9STywgc3RhdGljX2ZvcnVtX2lkZW50aXR5KCkpO1xyXG4gICAgICAgICAgICBwcm9maWxlLnNlbGVjdGVkX2lkZW50aXR5X3R5cGUgPSBJZGVudGl0aWZ5VHlwZVtJZGVudGl0aWZ5VHlwZS5URUFGT1JPXTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHNhdmUoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZV9uZXdfcHJvZmlsZShuYW1lOiBzdHJpbmcsIGlkPzogc3RyaW5nKTogQ29ubmVjdGlvblByb2ZpbGUge1xyXG4gICAgY29uc3QgcHJvZmlsZSA9IG5ldyBDb25uZWN0aW9uUHJvZmlsZShpZCB8fCBndWlkKCkpO1xyXG4gICAgcHJvZmlsZS5wcm9maWxlX25hbWUgPSBuYW1lO1xyXG4gICAgcHJvZmlsZS5kZWZhdWx0X3VzZXJuYW1lID0gXCJcIjtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcy5wdXNoKHByb2ZpbGUpO1xyXG4gICAgcmV0dXJuIHByb2ZpbGU7XHJcbn1cclxuXHJcbmxldCBfcmVxdWlyZXNfc2F2ZSA9IGZhbHNlO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHNhdmUoKSB7XHJcbiAgICBjb25zdCBwcm9maWxlczogc3RyaW5nW10gPSBbXTtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBhdmFpbGFibGVfcHJvZmlsZXMpXHJcbiAgICAgICAgcHJvZmlsZXMucHVzaChwcm9maWxlLmVuY29kZSgpKTtcclxuXHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgIHZlcnNpb246IDEsXHJcbiAgICAgICAgcHJvZmlsZXM6IHByb2ZpbGVzXHJcbiAgICB9KTtcclxuICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwicHJvZmlsZXNcIiwgZGF0YSk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBtYXJrX25lZWRfc2F2ZSgpIHtcclxuICAgIF9yZXF1aXJlc19zYXZlID0gdHJ1ZTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmVzX3NhdmUoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gX3JlcXVpcmVzX3NhdmU7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBwcm9maWxlcygpOiBDb25uZWN0aW9uUHJvZmlsZVtdIHtcclxuICAgIHJldHVybiBhdmFpbGFibGVfcHJvZmlsZXM7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBmaW5kX3Byb2ZpbGUoaWQ6IHN0cmluZyk6IENvbm5lY3Rpb25Qcm9maWxlIHwgdW5kZWZpbmVkIHtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBwcm9maWxlcygpKVxyXG4gICAgICAgIGlmIChwcm9maWxlLmlkID09IGlkKVxyXG4gICAgICAgICAgICByZXR1cm4gcHJvZmlsZTtcclxuXHJcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZmluZF9wcm9maWxlX2J5X25hbWUobmFtZTogc3RyaW5nKTogQ29ubmVjdGlvblByb2ZpbGUgfCB1bmRlZmluZWQge1xyXG4gICAgbmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKTtcclxuICAgIGZvciAoY29uc3QgcHJvZmlsZSBvZiBwcm9maWxlcygpKVxyXG4gICAgICAgIGlmICgocHJvZmlsZS5wcm9maWxlX25hbWUgfHwgXCJcIikudG9Mb3dlckNhc2UoKSA9PSBuYW1lKVxyXG4gICAgICAgICAgICByZXR1cm4gcHJvZmlsZTtcclxuXHJcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG59XHJcblxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRfcHJvZmlsZSgpOiBDb25uZWN0aW9uUHJvZmlsZSB7XHJcbiAgICByZXR1cm4gZmluZF9wcm9maWxlKFwiZGVmYXVsdFwiKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHNldF9kZWZhdWx0X3Byb2ZpbGUocHJvZmlsZTogQ29ubmVjdGlvblByb2ZpbGUpIHtcclxuICAgIGNvbnN0IG9sZF9kZWZhdWx0ID0gZGVmYXVsdF9wcm9maWxlKCk7XHJcbiAgICBpZiAob2xkX2RlZmF1bHQgJiYgb2xkX2RlZmF1bHQgIT0gcHJvZmlsZSkge1xyXG4gICAgICAgIG9sZF9kZWZhdWx0LmlkID0gZ3VpZCgpO1xyXG4gICAgfVxyXG4gICAgcHJvZmlsZS5pZCA9IFwiZGVmYXVsdFwiO1xyXG4gICAgcmV0dXJuIG9sZF9kZWZhdWx0O1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZGVsZXRlX3Byb2ZpbGUocHJvZmlsZTogQ29ubmVjdGlvblByb2ZpbGUpIHtcclxuICAgIGF2YWlsYWJsZV9wcm9maWxlcy5yZW1vdmUocHJvZmlsZSk7XHJcbn0iLCJpbXBvcnQge0Fic3RyYWN0Q29tbWFuZEhhbmRsZXIsIEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgU2VydmVyQ29tbWFuZH0gZnJvbSBcIi4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtjb25uZWN0aW9ufSBmcm9tIFwiLi4vY29ubmVjdGlvbi9IYW5kc2hha2VIYW5kbGVyXCI7XHJcblxyXG5pbXBvcnQgSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyID0gY29ubmVjdGlvbi5IYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbmltcG9ydCB7TmFtZUlkZW50aXR5fSBmcm9tIFwiLi9pZGVudGl0aWVzL05hbWVJZGVudGl0eVwiO1xyXG5pbXBvcnQge1RlYUZvcnVtSWRlbnRpdHl9IGZyb20gXCIuL2lkZW50aXRpZXMvVGVhRm9ydW1JZGVudGl0eVwiO1xyXG5pbXBvcnQge1RlYVNwZWFrSWRlbnRpdHl9IGZyb20gXCIuL2lkZW50aXRpZXMvVGVhbVNwZWFrSWRlbnRpdHlcIjtcclxuXHJcbmV4cG9ydCBlbnVtIElkZW50aXRpZnlUeXBlIHtcclxuICAgIFRFQUZPUk8sXHJcbiAgICBURUFNU1BFQUssXHJcbiAgICBOSUNLTkFNRVxyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIElkZW50aXR5IHtcclxuICAgIGZhbGxiYWNrX25hbWUoKTogc3RyaW5nIHwgdW5kZWZpbmVkIDtcclxuICAgIHVpZCgpIDogc3RyaW5nO1xyXG4gICAgdHlwZSgpIDogSWRlbnRpdGlmeVR5cGU7XHJcblxyXG4gICAgdmFsaWQoKSA6IGJvb2xlYW47XHJcblxyXG4gICAgZW5jb2RlPygpIDogc3RyaW5nO1xyXG4gICAgZGVjb2RlKGRhdGE6IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+O1xyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikgOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZWNvZGVfaWRlbnRpdHkodHlwZTogSWRlbnRpdGlmeVR5cGUsIGRhdGE6IHN0cmluZykgOiBQcm9taXNlPElkZW50aXR5PiB7XHJcbiAgICBsZXQgaWRlbnRpdHk6IElkZW50aXR5O1xyXG4gICAgc3dpdGNoICh0eXBlKSB7XHJcbiAgICAgICAgY2FzZSBJZGVudGl0aWZ5VHlwZS5OSUNLTkFNRTpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgTmFtZUlkZW50aXR5KCk7XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgIGNhc2UgSWRlbnRpdGlmeVR5cGUuVEVBRk9STzpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgVGVhRm9ydW1JZGVudGl0eSh1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlIElkZW50aXRpZnlUeXBlLlRFQU1TUEVBSzpcclxuICAgICAgICAgICAgaWRlbnRpdHkgPSBuZXcgVGVhU3BlYWtJZGVudGl0eSh1bmRlZmluZWQsIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgfVxyXG4gICAgaWYoIWlkZW50aXR5KVxyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgICBhd2FpdCBpZGVudGl0eS5kZWNvZGUoZGF0YSlcclxuICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAvKiB0b2RvIGJldHRlciBlcnJvciBoYW5kbGluZyEgKi9cclxuICAgICAgICBjb25zb2xlLmVycm9yKGVycm9yKTtcclxuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBpZGVudGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZV9pZGVudGl0eSh0eXBlOiBJZGVudGl0aWZ5VHlwZSkge1xyXG4gICAgbGV0IGlkZW50aXR5OiBJZGVudGl0eTtcclxuICAgIHN3aXRjaCAodHlwZSkge1xyXG4gICAgICAgIGNhc2UgSWRlbnRpdGlmeVR5cGUuTklDS05BTUU6XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IE5hbWVJZGVudGl0eSgpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlIElkZW50aXRpZnlUeXBlLlRFQUZPUk86XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IFRlYUZvcnVtSWRlbnRpdHkodW5kZWZpbmVkKTtcclxuICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSBJZGVudGl0aWZ5VHlwZS5URUFNU1BFQUs6XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gbmV3IFRlYVNwZWFrSWRlbnRpdHkodW5kZWZpbmVkLCB1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICBicmVhaztcclxuICAgIH1cclxuICAgIHJldHVybiBpZGVudGl0eTtcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyPFQgZXh0ZW5kcyBBYnN0cmFjdEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcj4gZXh0ZW5kcyBBYnN0cmFjdENvbW1hbmRIYW5kbGVyIHtcclxuICAgIHJlYWRvbmx5IGhhbmRsZTogVDtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24sIGhhbmRsZTogVCkge1xyXG4gICAgICAgIHN1cGVyKGNvbm5lY3Rpb24pO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlID0gaGFuZGxlO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICBoYW5kbGVfY29tbWFuZChjb21tYW5kOiBTZXJ2ZXJDb21tYW5kKTogYm9vbGVhbiB7XHJcbiAgICAgICAgaWYoJC5pc0Z1bmN0aW9uKHRoaXNbY29tbWFuZC5jb21tYW5kXSkpXHJcbiAgICAgICAgICAgIHRoaXNbY29tbWFuZC5jb21tYW5kXShjb21tYW5kLmFyZ3VtZW50cyk7XHJcbiAgICAgICAgZWxzZSBpZihjb21tYW5kLmNvbW1hbmQgPT0gXCJlcnJvclwiKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJSZWNlaXZlZCB1bmtub3duIGNvbW1hbmQgd2hpbGUgaGFuZHNoYWtpbmcgKCVvKVwiKSwgY29tbWFuZCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgaW1wbGVtZW50cyBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciB7XHJcbiAgICBjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb247XHJcblxyXG4gICAgcHJvdGVjdGVkIGNhbGxiYWNrczogKChzdWNjZXNzOiBib29sZWFuLCBtZXNzYWdlPzogc3RyaW5nKSA9PiBhbnkpW10gPSBbXTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uID0gY29ubmVjdGlvbjtcclxuICAgIH1cclxuXHJcbiAgICByZWdpc3Rlcl9jYWxsYmFjayhjYWxsYmFjazogKHN1Y2Nlc3M6IGJvb2xlYW4sIG1lc3NhZ2U/OiBzdHJpbmcpID0+IGFueSkge1xyXG4gICAgICAgIHRoaXMuY2FsbGJhY2tzLnB1c2goY2FsbGJhY2spO1xyXG4gICAgfVxyXG5cclxuICAgIGFic3RyYWN0IHN0YXJ0X2hhbmRzaGFrZSgpO1xyXG5cclxuICAgIHByb3RlY3RlZCB0cmlnZ2VyX3N1Y2Nlc3MoKSB7XHJcbiAgICAgICAgZm9yKGNvbnN0IGNhbGxiYWNrIG9mIHRoaXMuY2FsbGJhY2tzKVxyXG4gICAgICAgICAgICBjYWxsYmFjayh0cnVlKTtcclxuICAgIH1cclxuXHJcbiAgICBwcm90ZWN0ZWQgdHJpZ2dlcl9mYWlsKG1lc3NhZ2U6IHN0cmluZykge1xyXG4gICAgICAgIGZvcihjb25zdCBjYWxsYmFjayBvZiB0aGlzLmNhbGxiYWNrcylcclxuICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UsIG1lc3NhZ2UpO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtDb21tYW5kUmVzdWx0fSBmcm9tIFwiLi4vLi4vY29ubmVjdGlvbi9TZXJ2ZXJDb25uZWN0aW9uRGVjbGFyYXRpb25cIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7QWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9ufSBmcm9tIFwiLi4vLi4vY29ubmVjdGlvbi9Db25uZWN0aW9uQmFzZVwiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi8uLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuXHJcbmltcG9ydCBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIgPSBjb25uZWN0aW9uLkhhbmRzaGFrZUlkZW50aXR5SGFuZGxlcjtcclxuaW1wb3J0IHtBYnN0cmFjdEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciwgSGFuZHNoYWtlQ29tbWFuZEhhbmRsZXIsIElkZW50aXRpZnlUeXBlLCBJZGVudGl0eX0gZnJvbSBcIi4uL0lkZW50aXR5XCI7XHJcblxyXG5jbGFzcyBOYW1lSGFuZHNoYWtlSGFuZGxlciBleHRlbmRzIEFic3RyYWN0SGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyIHtcclxuICAgIHJlYWRvbmx5IGlkZW50aXR5OiBOYW1lSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxOYW1lSGFuZHNoYWtlSGFuZGxlcj47XHJcblxyXG4gICAgY29uc3RydWN0b3IoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uLCBpZGVudGl0eTogTmFtZUlkZW50aXR5KSB7XHJcbiAgICAgICAgc3VwZXIoY29ubmVjdGlvbik7XHJcbiAgICAgICAgdGhpcy5pZGVudGl0eSA9IGlkZW50aXR5O1xyXG5cclxuICAgICAgICB0aGlzLmhhbmRsZXIgPSBuZXcgSGFuZHNoYWtlQ29tbWFuZEhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyW1wiaGFuZHNoYWtlaWRlbnRpdHlwcm9vZlwiXSA9ICgpID0+IHRoaXMudHJpZ2dlcl9mYWlsKFwic2VydmVyIHJlcXVlc3RlZCB1bmV4cGVjdGVkIHByb29mXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBjbGllbnRfbmlja25hbWU6IHRoaXMuaWRlbnRpdHkubmFtZSgpXHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBuYW1lIGJhc2VkIGhhbmRzaGFrZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIGJlZ2luIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIE5hbWVJZGVudGl0eSBpbXBsZW1lbnRzIElkZW50aXR5IHtcclxuICAgIHByaXZhdGUgX25hbWU6IHN0cmluZztcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihuYW1lPzogc3RyaW5nKSB7XHJcbiAgICAgICAgdGhpcy5fbmFtZSA9IG5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0X25hbWUobmFtZTogc3RyaW5nKSB7IHRoaXMuX25hbWUgPSBuYW1lOyB9XHJcblxyXG4gICAgbmFtZSgpIDogc3RyaW5nIHsgcmV0dXJuIHRoaXMuX25hbWU7IH1cclxuXHJcbiAgICBmYWxsYmFja19uYW1lKCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgdWlkKCk6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIGJ0b2EodGhpcy5fbmFtZSk7IC8vRklYTUUgaGFzaCFcclxuICAgIH1cclxuXHJcbiAgICB0eXBlKCk6IElkZW50aXRpZnlUeXBlIHtcclxuICAgICAgICByZXR1cm4gSWRlbnRpdGlmeVR5cGUuTklDS05BTUU7XHJcbiAgICB9XHJcblxyXG4gICAgdmFsaWQoKTogYm9vbGVhbiB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWUgIT0gdW5kZWZpbmVkICYmIHRoaXMuX25hbWUubGVuZ3RoID49IDU7XHJcbiAgICB9XHJcblxyXG4gICAgZGVjb2RlKGRhdGEpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgZGF0YSA9IEpTT04ucGFyc2UoZGF0YSk7XHJcbiAgICAgICAgaWYoZGF0YS52ZXJzaW9uICE9PSAxKVxyXG4gICAgICAgICAgICB0aHJvdyBcImludmFsaWQgdmVyc2lvblwiO1xyXG5cclxuICAgICAgICB0aGlzLl9uYW1lID0gZGF0YVtcIm5hbWVcIl07XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZT8oKSA6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgICAgdmVyc2lvbjogMSxcclxuICAgICAgICAgICAgbmFtZTogdGhpcy5fbmFtZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHNwYXduX2lkZW50aXR5X2hhbmRzaGFrZV9oYW5kbGVyKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbikgOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIHJldHVybiBuZXcgTmFtZUhhbmRzaGFrZUhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICB9XHJcbn0iLCJpbXBvcnQge0Fic3RyYWN0U2VydmVyQ29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7Zm9ydW19IGZyb20gXCIuL3RlYXNwZWFrLWZvcnVtXCI7XHJcbmltcG9ydCB7Y29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vSGFuZHNoYWtlSGFuZGxlclwiO1xyXG5pbXBvcnQgSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyID0gY29ubmVjdGlvbi5IYW5kc2hha2VJZGVudGl0eUhhbmRsZXI7XHJcbmltcG9ydCB7QWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIsIEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyLCBJZGVudGl0aWZ5VHlwZSwgSWRlbnRpdHl9IGZyb20gXCIuLi9JZGVudGl0eVwiO1xyXG5cclxuY2xhc3MgVGVhRm9ydW1IYW5kc2hha2VIYW5kbGVyIGV4dGVuZHMgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgcmVhZG9ubHkgaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxUZWFGb3J1bUhhbmRzaGFrZUhhbmRsZXI+O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHkpIHtcclxuICAgICAgICBzdXBlcihjb25uZWN0aW9uKTtcclxuICAgICAgICB0aGlzLmlkZW50aXR5ID0gaWRlbnRpdHk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyID0gbmV3IEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlcltcImhhbmRzaGFrZWlkZW50aXR5cHJvb2ZcIl0gPSB0aGlzLmhhbmRsZV9wcm9vZi5iaW5kKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBkYXRhOiB0aGlzLmlkZW50aXR5LmRhdGEoKS5kYXRhX2pzb24oKVxyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgVGVhRm9ydW0gYmFzZWQgaGFuZHNoYWtlLiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIGJlZ2luIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9wcm9vZihqc29uKSB7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWluZGVudGl0eXByb29mXCIsIHtcclxuICAgICAgICAgICAgcHJvb2Y6IHRoaXMuaWRlbnRpdHkuZGF0YSgpLmRhdGFfc2lnbigpXHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gcHJvb2YgdGhlIGlkZW50aXR5LiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgIGVycm9yID0gZXJyb3IuZXh0cmFfbWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlO1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBleGVjdXRlIHByb29mIChcIiArIGVycm9yICsgXCIpXCIpO1xyXG4gICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFRlYUZvcnVtSWRlbnRpdHkgaW1wbGVtZW50cyBJZGVudGl0eSB7XHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGlkZW50aXR5X2RhdGE6IGZvcnVtLkRhdGE7XHJcblxyXG4gICAgdmFsaWQoKSA6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiAhIXRoaXMuaWRlbnRpdHlfZGF0YSAmJiAhdGhpcy5pZGVudGl0eV9kYXRhLmlzX2V4cGlyZWQoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdHJ1Y3RvcihkYXRhOiBmb3J1bS5EYXRhKSB7XHJcbiAgICAgICAgdGhpcy5pZGVudGl0eV9kYXRhID0gZGF0YTtcclxuICAgIH1cclxuXHJcbiAgICBkYXRhKCkgOiBmb3J1bS5EYXRhIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5pZGVudGl0eV9kYXRhO1xyXG4gICAgfVxyXG5cclxuICAgIGRlY29kZShkYXRhKSA6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgICAgIGRhdGEgPSBKU09OLnBhcnNlKGRhdGEpO1xyXG4gICAgICAgIGlmKGRhdGEudmVyc2lvbiAhPT0gMSlcclxuICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIHZlcnNpb25cIjtcclxuXHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGVuY29kZSgpIDogc3RyaW5nIHtcclxuICAgICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICB2ZXJzaW9uOiAxXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3Bhd25faWRlbnRpdHlfaGFuZHNoYWtlX2hhbmRsZXIoY29ubmVjdGlvbjogQWJzdHJhY3RTZXJ2ZXJDb25uZWN0aW9uKSA6IEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBUZWFGb3J1bUhhbmRzaGFrZUhhbmRsZXIoY29ubmVjdGlvbiwgdGhpcyk7XHJcbiAgICB9XHJcblxyXG4gICAgZmFsbGJhY2tfbmFtZSgpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xyXG4gICAgICAgIHJldHVybiB0aGlzLmlkZW50aXR5X2RhdGEgPyB0aGlzLmlkZW50aXR5X2RhdGEubmFtZSgpIDogdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIHR5cGUoKTogSWRlbnRpdGlmeVR5cGUge1xyXG4gICAgICAgIHJldHVybiBJZGVudGl0aWZ5VHlwZS5URUFGT1JPO1xyXG4gICAgfVxyXG5cclxuICAgIHVpZCgpOiBzdHJpbmcge1xyXG4gICAgICAgIC8vRklYTUU6IFJlYWwgVUlEIVxyXG4gICAgICAgIHJldHVybiBcIlRlYUZvcm8jXCIgKyAoKHRoaXMuaWRlbnRpdHlfZGF0YSA/IHRoaXMuaWRlbnRpdHlfZGF0YS5uYW1lKCkgOiBcIkFub3RoZXIgVGVhU3BlYWsgdXNlclwiKSk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmxldCBzdGF0aWNfaWRlbnRpdHk6IFRlYUZvcnVtSWRlbnRpdHk7XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gc2V0X3N0YXRpY19pZGVudGl0eShpZGVudGl0eTogVGVhRm9ydW1JZGVudGl0eSkge1xyXG4gICAgc3RhdGljX2lkZW50aXR5ID0gaWRlbnRpdHk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVfZm9ydW0oKSB7XHJcbiAgICBpZihmb3J1bS5sb2dnZWRfaW4oKSAmJiAoIXN0YXRpY19pZGVudGl0eSB8fCBzdGF0aWNfaWRlbnRpdHkuZGF0YSgpICE9PSBmb3J1bS5kYXRhKCkpKSB7XHJcbiAgICAgICAgc3RhdGljX2lkZW50aXR5ID0gbmV3IFRlYUZvcnVtSWRlbnRpdHkoZm9ydW0uZGF0YSgpKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgc3RhdGljX2lkZW50aXR5ID0gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRfc3RhdGljX2ZvcnVtX2lkZW50aXR5KCkgOiBib29sZWFuIHtcclxuICAgIHJldHVybiBzdGF0aWNfaWRlbnRpdHkgJiYgc3RhdGljX2lkZW50aXR5LnZhbGlkKCk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBzdGF0aWNfZm9ydW1faWRlbnRpdHkoKSA6IFRlYUZvcnVtSWRlbnRpdHkgfCB1bmRlZmluZWQge1xyXG4gICAgcmV0dXJuIHN0YXRpY19pZGVudGl0eTtcclxufSIsImltcG9ydCB7YXJyYXlCdWZmZXJCYXNlNjQsIGJhc2U2NF9lbmNvZGVfYWIsIHN0cjJhYjh9IGZyb20gXCIuLi8uLi9tYWluXCI7XHJcbmltcG9ydCB7c2hhfSBmcm9tIFwiLi4vLi4vY3J5cHRvL3NoYVwiO1xyXG5pbXBvcnQge2FzbjF9IGZyb20gXCIuLi8uLi9jcnlwdG8vYXNuMVwiO1xyXG5pbXBvcnQge0Fic3RyYWN0U2VydmVyQ29ubmVjdGlvbn0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vQ29ubmVjdGlvbkJhc2VcIjtcclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi4vLi4vbG9nXCI7XHJcbmltcG9ydCB7Q29tbWFuZFJlc3VsdH0gZnJvbSBcIi4uLy4uL2Nvbm5lY3Rpb24vU2VydmVyQ29ubmVjdGlvbkRlY2xhcmF0aW9uXCI7XHJcbmltcG9ydCB7c2V0dGluZ3N9IGZyb20gXCIuLi8uLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge2Nvbm5lY3Rpb259IGZyb20gXCIuLi8uLi9jb25uZWN0aW9uL0hhbmRzaGFrZUhhbmRsZXJcIjtcclxuaW1wb3J0IEhhbmRzaGFrZUlkZW50aXR5SGFuZGxlciA9IGNvbm5lY3Rpb24uSGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyO1xyXG5pbXBvcnQge0Fic3RyYWN0SGFuZHNoYWtlSWRlbnRpdHlIYW5kbGVyLCBIYW5kc2hha2VDb21tYW5kSGFuZGxlciwgSWRlbnRpdGlmeVR5cGUsIElkZW50aXR5fSBmcm9tIFwiLi4vSWRlbnRpdHlcIjtcclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgQ3J5cHRvSGVscGVyIHtcclxuICAgIGV4cG9ydCBmdW5jdGlvbiBiYXNlNjRfdXJsX2VuY29kZShzdHIpe1xyXG4gICAgICAgIHJldHVybiBzdHIucmVwbGFjZSgvXFwrL2csICctJykucmVwbGFjZSgvXFwvL2csICdfJykucmVwbGFjZSgvXFw9KyQvLCAnJyk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGJhc2U2NF91cmxfZGVjb2RlKHN0cjogc3RyaW5nLCBwYWQ/OiBib29sZWFuKXtcclxuICAgICAgICBpZih0eXBlb2YocGFkKSA9PT0gJ3VuZGVmaW5lZCcgfHwgcGFkKVxyXG4gICAgICAgICAgICBzdHIgPSAoc3RyICsgJz09PScpLnNsaWNlKDAsIHN0ci5sZW5ndGggKyAoc3RyLmxlbmd0aCAlIDQpKTtcclxuICAgICAgICByZXR1cm4gc3RyLnJlcGxhY2UoLy0vZywgJysnKS5yZXBsYWNlKC9fL2csICcvJyk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGFycmF5YnVmZmVyX3RvX3N0cmluZyhidWYpIHtcclxuICAgICAgICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBuZXcgVWludDE2QXJyYXkoYnVmKSk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4cG9ydF9lY2Nfa2V5KGNyeXB0b19rZXk6IENyeXB0b0tleSwgcHVibGljX2tleTogYm9vbGVhbikge1xyXG4gICAgICAgIC8qXHJcbiAgICAgICAgICAgIFRvbWNyeXB0IHB1YmxpYyBrZXkgZXhwb3J0OlxyXG4gICAgICAgICAgICBpZiAodHlwZSA9PSBQS19QUklWQVRFKSB7XHJcbiAgICAgICAgICAgICAgIGZsYWdzWzBdID0gMTtcclxuICAgICAgICAgICAgICAgZXJyID0gZGVyX2VuY29kZV9zZXF1ZW5jZV9tdWx0aShvdXQsIG91dGxlbixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9CSVRfU1RSSU5HLCAgICAgIDFVTCwgZmxhZ3MsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTFRDX0FTTjFfU0hPUlRfSU5URUdFUiwgICAxVUwsICZrZXlfc2l6ZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5wdWJrZXkueCxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5wdWJrZXkueSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9JTlRFR0VSLCAgICAgICAgIDFVTCwga2V5LT5rLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0VPTCwgICAgICAgICAgICAgMFVMLCBOVUxMKTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgZmxhZ3NbMF0gPSAwO1xyXG4gICAgICAgICAgICAgICBlcnIgPSBkZXJfZW5jb2RlX3NlcXVlbmNlX211bHRpKG91dCwgb3V0bGVuLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0JJVF9TVFJJTkcsICAgICAgMVVMLCBmbGFncyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMVENfQVNOMV9TSE9SVF9JTlRFR0VSLCAgIDFVTCwgJmtleV9zaXplLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0lOVEVHRVIsICAgICAgICAgMVVMLCBrZXktPnB1YmtleS54LFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0lOVEVHRVIsICAgICAgICAgMVVMLCBrZXktPnB1YmtleS55LFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExUQ19BU04xX0VPTCwgICAgICAgICAgICAgMFVMLCBOVUxMKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgKi9cclxuXHJcbiAgICAgICAgY29uc3Qga2V5X2RhdGEgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmV4cG9ydEtleShcImp3a1wiLCBjcnlwdG9fa2V5KTtcclxuXHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuICAgICAgICBjb25zdCBsZW5ndGggPSBwdWJsaWNfa2V5ID8gNzkgOiAxMTQ7IC8qIG1heCBsZW5ndGhzISBEZXBlbmRzIG9uIHRoZSBwYWRkaW5nIGNvdWxkIGJlIGxlc3MgKi9cclxuICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheShsZW5ndGgpOyAvKiBmaXhlZCBBU04xIGxlbmd0aCAqL1xyXG4gICAgICAgIHsgLyogdGhlIGluaXRpYWwgc2VxdWVuY2UgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgzMDsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAwOyAvKiB3ZSB3aWxsIHNldCB0aGUgc2VxdWVuY2UgbGVuZ3RoIGxhdGVyICovXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHsgLyogdGhlIGZsYWdzIGJpdCBzdHJpbmcgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMzsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAyOyAvKiBsZW5ndGggKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwNzsgLyogZGF0YSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSBwdWJsaWNfa2V5ID8gMHgwMCA6IDB4ODA7IC8qIGZsYWcgMSBvciAwICgxID0gcHJpdmF0ZSBrZXkpKi9cclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBrZXkgc2l6ZSAoY29uc3QgMzIgZm9yIFAtMjU2KSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDAyOyAvKiB0eXBlICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDE7IC8qIGxlbmd0aCAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IFggKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMjsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwOyAvKiBsZW5ndGggKi9cclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEueCwgZmFsc2UpKTtcclxuICAgICAgICAgICAgaWYocmF3LmNoYXJDb2RlQXQoMCkgPiAweDdGKSB7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXggLSAxXSArPSAxO1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IDMyOyBpKyspXHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgRE9NRXhjZXB0aW9uKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeCBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IFkgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgwMjsgLyogdHlwZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDIwOyAvKiBsZW5ndGggKi9cclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEueSwgZmFsc2UpKTtcclxuICAgICAgICAgICAgaWYocmF3LmNoYXJDb2RlQXQoMCkgPiAweDdGKSB7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXggLSAxXSArPSAxO1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IDMyOyBpKyspXHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgRE9NRXhjZXB0aW9uKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeSBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZighcHVibGljX2tleSkge1xyXG4gICAgICAgICAgICB0cnkgeyAvKiBQdWJsaWMga2V4IEsgKi9cclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MjA7IC8qIGxlbmd0aCAqL1xyXG5cclxuICAgICAgICAgICAgICAgIGNvbnN0IHJhdyA9IGF0b2IoYmFzZTY0X3VybF9kZWNvZGUoa2V5X2RhdGEuZCwgZmFsc2UpKTtcclxuICAgICAgICAgICAgICAgIGlmKHJhdy5jaGFyQ29kZUF0KDApID4gMHg3Rikge1xyXG4gICAgICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCAtIDFdICs9IDE7XHJcbiAgICAgICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMDtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgMzI7IGkrKylcclxuICAgICAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSByYXcuY2hhckNvZGVBdChpKTtcclxuICAgICAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pXHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gcGFyc2UgeSBjb29yZGluYXRlIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBidWZmZXJbMV0gPSBpbmRleCAtIDI7IC8qIHNldCB0aGUgZmluYWwgc2VxdWVuY2UgbGVuZ3RoICovXHJcblxyXG4gICAgICAgIHJldHVybiBiYXNlNjRfZW5jb2RlX2FiKGJ1ZmZlci5idWZmZXIuc2xpY2UoMCwgaW5kZXgpKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBjcnlwdF9rZXkgPSBcImI5ZGZhYTdiZWU2YWM1N2FjN2I2NWYxMDk0YTFjMTU1ZTc0NzMyN2JjMmZlNWQ1MWM1MTIwMjNmZTU0YTI4MDIwMTAwNGU5MGFkMWRhYWFlMTA3NWQ1M2I3ZDU3MWMzMGUwNjNiNWE2MmE0YTAxN2JiMzk0ODMzYWEwOTgzZTZlXCI7XHJcbiAgICBmdW5jdGlvbiBjX3N0cmxlbihidWZmZXI6IFVpbnQ4QXJyYXksIG9mZnNldDogbnVtYmVyKSA6IG51bWJlciB7XHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuICAgICAgICB3aGlsZShpbmRleCArIG9mZnNldCA8IGJ1ZmZlci5sZW5ndGggJiYgYnVmZmVyW2luZGV4ICsgb2Zmc2V0XSAhPSAwKVxyXG4gICAgICAgICAgICBpbmRleCsrO1xyXG4gICAgICAgIHJldHVybiBpbmRleDtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVjcnlwdF90c19pZGVudGl0eShidWZmZXI6IFVpbnQ4QXJyYXkpIDogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgICAgICAvKiBidWZmZXIgY291bGQgY29udGFpbnMgYSB6ZXJvISAqL1xyXG4gICAgICAgIGNvbnN0IGhhc2ggPSBuZXcgVWludDhBcnJheShhd2FpdCBzaGEuc2hhMShidWZmZXIuYnVmZmVyLnNsaWNlKDIwLCAyMCArIGNfc3RybGVuKGJ1ZmZlciwgMjApKSkpO1xyXG4gICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAyMDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gaGFzaFtpXTtcclxuXHJcbiAgICAgICAgY29uc3QgbGVuZ3RoID0gTWF0aC5taW4oYnVmZmVyLmxlbmd0aCwgMTAwKTtcclxuICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpXSBePSBjcnlwdF9rZXkuY2hhckNvZGVBdChpKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGFycmF5YnVmZmVyX3RvX3N0cmluZyhidWZmZXIpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbmNyeXB0X3RzX2lkZW50aXR5KGJ1ZmZlcjogVWludDhBcnJheSkgOiBQcm9taXNlPHN0cmluZz4ge1xyXG4gICAgICAgIGNvbnN0IGxlbmd0aCA9IE1hdGgubWluKGJ1ZmZlci5sZW5ndGgsIDEwMCk7XHJcbiAgICAgICAgZm9yKGxldCBpID0gMDsgaSA8IGxlbmd0aDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gY3J5cHRfa2V5LmNoYXJDb2RlQXQoaSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGhhc2ggPSBuZXcgVWludDhBcnJheShhd2FpdCBzaGEuc2hhMShidWZmZXIuYnVmZmVyLnNsaWNlKDIwLCAyMCArIGNfc3RybGVuKGJ1ZmZlciwgMjApKSkpO1xyXG4gICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAyMDsgaSsrKVxyXG4gICAgICAgICAgICBidWZmZXJbaV0gXj0gaGFzaFtpXTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGJhc2U2NF9lbmNvZGVfYWIoYnVmZmVyKTtcclxuICAgIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEBwYXJhbSBidWZmZXIgYmFzZTY0IGVuY29kZWQgQVNOLjEgc3RyaW5nXHJcbiAgICAgKi9cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBkZWNvZGVfdG9tY3J5cHRfa2V5KGJ1ZmZlcjogc3RyaW5nKSB7XHJcbiAgICAgICAgbGV0IGRlY29kZWQ7XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGRlY29kZWQgPSBhc24xLmRlY29kZShhdG9iKGJ1ZmZlcikpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgaWYoZXJyb3IgaW5zdGFuY2VvZiBET01FeGNlcHRpb24pXHJcbiAgICAgICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBwYXJzZSBrZXkgYnVmZmVyIChpbnZhbGlkIGJhc2U2NClcIjtcclxuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBsZXQge3gsIHksIGt9ID0ge1xyXG4gICAgICAgICAgICB4OiBkZWNvZGVkLmNoaWxkcmVuWzJdLmNvbnRlbnQoSW5maW5pdHksIGFzbjEuVGFnVHlwZS5WaXNpYmxlU3RyaW5nKSxcclxuICAgICAgICAgICAgeTogZGVjb2RlZC5jaGlsZHJlblszXS5jb250ZW50KEluZmluaXR5LCBhc24xLlRhZ1R5cGUuVmlzaWJsZVN0cmluZyksXHJcbiAgICAgICAgICAgIGs6IGRlY29kZWQuY2hpbGRyZW5bNF0uY29udGVudChJbmZpbml0eSwgYXNuMS5UYWdUeXBlLlZpc2libGVTdHJpbmcpXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaWYoeC5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZih4LmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBYIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgeCA9IHguc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoeS5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZih5LmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBZIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgeSA9IHkuc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoay5sZW5ndGggPiAzMikge1xyXG4gICAgICAgICAgICBpZihrLmNoYXJDb2RlQXQoMCkgIT0gMClcclxuICAgICAgICAgICAgICAgIHRocm93IFwiSW52YWxpZCBwcml2YXRlIGNvb3JkaW5hdGUhIChUb28gbG9uZylcIjtcclxuICAgICAgICAgICAgayA9IGsuc3Vic3RyKDEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLypcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSB4OiAlcyAoJWQpXCIsIGJ0b2EoeCksIHgubGVuZ3RoKTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSB5OiAlcyAoJWQpXCIsIGJ0b2EoeSksIHkubGVuZ3RoKTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIktleSBrOiAlcyAoJWQpXCIsIGJ0b2EoayksIGsubGVuZ3RoKTtcclxuICAgICAgICAqL1xyXG4gICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNydjogXCJQLTI1NlwiLFxyXG4gICAgICAgICAgICBkOiBiYXNlNjRfdXJsX2VuY29kZShidG9hKGspKSxcclxuICAgICAgICAgICAgeDogYmFzZTY0X3VybF9lbmNvZGUoYnRvYSh4KSksXHJcbiAgICAgICAgICAgIHk6IGJhc2U2NF91cmxfZW5jb2RlKGJ0b2EoeSkpLFxyXG5cclxuICAgICAgICAgICAgZXh0OiB0cnVlLFxyXG4gICAgICAgICAgICBrZXlfb3BzOltcImRlcml2ZUtleVwiLCBcInNpZ25cIl0sXHJcbiAgICAgICAgICAgIGt0eTpcIkVDXCIsXHJcbiAgICAgICAgfTtcclxuICAgIH1cclxufVxyXG5cclxuY2xhc3MgVGVhU3BlYWtIYW5kc2hha2VIYW5kbGVyIGV4dGVuZHMgQWJzdHJhY3RIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgaWRlbnRpdHk6IFRlYVNwZWFrSWRlbnRpdHk7XHJcbiAgICBoYW5kbGVyOiBIYW5kc2hha2VDb21tYW5kSGFuZGxlcjxUZWFTcGVha0hhbmRzaGFrZUhhbmRsZXI+O1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGNvbm5lY3Rpb246IEFic3RyYWN0U2VydmVyQ29ubmVjdGlvbiwgaWRlbnRpdHk6IFRlYVNwZWFrSWRlbnRpdHkpIHtcclxuICAgICAgICBzdXBlcihjb25uZWN0aW9uKTtcclxuICAgICAgICB0aGlzLmlkZW50aXR5ID0gaWRlbnRpdHk7XHJcbiAgICAgICAgdGhpcy5oYW5kbGVyID0gbmV3IEhhbmRzaGFrZUNvbW1hbmRIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgICAgIHRoaXMuaGFuZGxlcltcImhhbmRzaGFrZWlkZW50aXR5cHJvb2ZcIl0gPSB0aGlzLmhhbmRsZV9wcm9vZi5iaW5kKHRoaXMpO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXJ0X2hhbmRzaGFrZSgpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS5yZWdpc3Rlcl9oYW5kbGVyKHRoaXMuaGFuZGxlcik7XHJcbiAgICAgICAgdGhpcy5jb25uZWN0aW9uLnNlbmRfY29tbWFuZChcImhhbmRzaGFrZWJlZ2luXCIsIHtcclxuICAgICAgICAgICAgaW50ZW50aW9uOiAwLFxyXG4gICAgICAgICAgICBhdXRoZW50aWNhdGlvbl9tZXRob2Q6IHRoaXMuaWRlbnRpdHkudHlwZSgpLFxyXG4gICAgICAgICAgICBwdWJsaWNLZXk6IHRoaXMuaWRlbnRpdHkucHVibGljX2tleVxyXG4gICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiRmFpbGVkIHRvIGluaXRpYWxpemUgVGVhbVNwZWFrIGJhc2VkIGhhbmRzaGFrZS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcblxyXG4gICAgICAgICAgICBpZihlcnJvciBpbnN0YW5jZW9mIENvbW1hbmRSZXN1bHQpXHJcbiAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9yLmV4dHJhX21lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuICAgICAgICAgICAgdGhpcy50cmlnZ2VyX2ZhaWwoXCJmYWlsZWQgdG8gZXhlY3V0ZSBiZWdpbiAoXCIgKyBlcnJvciArIFwiKVwiKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9wcm9vZihqc29uKSB7XHJcbiAgICAgICAgaWYoIWpzb25bMF1bXCJkaWdlc3RcIl0pIHtcclxuICAgICAgICAgICAgdGhpcy50cmlnZ2VyX2ZhaWwoXCJzZXJ2ZXIgdG9vIG9sZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5pZGVudGl0eS5zaWduX21lc3NhZ2UoanNvblswXVtcIm1lc3NhZ2VcIl0sIGpzb25bMF1bXCJkaWdlc3RcIl0pLnRoZW4ocHJvb2YgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZF9jb21tYW5kKFwiaGFuZHNoYWtlaW5kZW50aXR5cHJvb2ZcIiwge3Byb29mOiBwcm9vZn0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCB0cihcIkZhaWxlZCB0byBwcm9vZiB0aGUgaWRlbnRpdHkuIEVycm9yOiAlb1wiKSwgZXJyb3IpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVycm9yIGluc3RhbmNlb2YgQ29tbWFuZFJlc3VsdClcclxuICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGVycm9yLmV4dHJhX21lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuICAgICAgICAgICAgICAgIHRoaXMudHJpZ2dlcl9mYWlsKFwiZmFpbGVkIHRvIGV4ZWN1dGUgcHJvb2YgKFwiICsgZXJyb3IgKyBcIilcIik7XHJcbiAgICAgICAgICAgIH0pLnRoZW4oKCkgPT4gdGhpcy50cmlnZ2VyX3N1Y2Nlc3MoKSk7XHJcbiAgICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXJfZmFpbChcImZhaWxlZCB0byBzaWduIG1lc3NhZ2VcIik7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfZmFpbChtZXNzYWdlOiBzdHJpbmcpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX2ZhaWwobWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHRyaWdnZXJfc3VjY2VzcygpIHtcclxuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uY29tbWFuZF9oYW5kbGVyX2Jvc3MoKS51bnJlZ2lzdGVyX2hhbmRsZXIodGhpcy5oYW5kbGVyKTtcclxuICAgICAgICBzdXBlci50cmlnZ2VyX3N1Y2Nlc3MoKTtcclxuICAgIH1cclxufVxyXG5cclxuY2xhc3MgSWRlbnRpdHlQT1dXb3JrZXIge1xyXG4gICAgcHJpdmF0ZSBfd29ya2VyOiBXb3JrZXI7XHJcbiAgICBwcml2YXRlIF9jdXJyZW50X2hhc2g6IHN0cmluZztcclxuICAgIHByaXZhdGUgX2Jlc3RfbGV2ZWw6IG51bWJlcjtcclxuXHJcbiAgICBhc3luYyBpbml0aWFsaXplKGtleTogc3RyaW5nKSB7XHJcbiAgICAgICAgdGhpcy5fd29ya2VyID0gbmV3IFdvcmtlcihzZXR0aW5ncy5zdGF0aWMoXCJ3b3JrZXJfZGlyZWN0b3J5XCIsIFwianMvd29ya2Vycy9cIikgKyBcIldvcmtlclBPVy5qc1wiKTtcclxuXHJcbiAgICAgICAgLyogaW5pdGlhbGl6ZSAqL1xyXG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgY29uc3QgdGltZW91dF9pZCA9IHNldFRpbWVvdXQoKCkgPT4gcmVqZWN0KFwidGltZW91dFwiKSwgMTAwMCk7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKCFldmVudC5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZighZXZlbnQuZGF0YS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW5pdGlhbGl6ZSBmYWlsZWQgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHRoaXMuaGFuZGxlX21lc3NhZ2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbmVycm9yID0gZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiUE9XIFdvcmtlciBlcnJvciAlb1wiKSwgZXZlbnQpO1xyXG4gICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG4gICAgICAgICAgICAgICAgcmVqZWN0KFwiRmFpbGVkIHRvIGxvYWQgd29ya2VyIChcIiArIGV2ZW50Lm1lc3NhZ2UgKyBcIilcIik7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8qIHNldCBkYXRhICovXHJcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLl93b3JrZXIucG9zdE1lc3NhZ2Uoe1xyXG4gICAgICAgICAgICAgICAgdHlwZTogXCJzZXRfZGF0YVwiLFxyXG4gICAgICAgICAgICAgICAgcHJpdmF0ZV9rZXk6IGtleSxcclxuICAgICAgICAgICAgICAgIGNvZGU6IFwic2V0X2RhdGFcIlxyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHRpbWVvdXRfaWQgPSBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChcInRpbWVvdXQgKGRhdGEpXCIpLCAxMDAwKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbm1lc3NhZ2UgPSBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKCFldmVudC5kYXRhKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEuc3VjY2Vzcykge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChcImluaXRpYWxpemUgb2YgZGF0YSBmYWlsZWQgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHRoaXMuaGFuZGxlX21lc3NhZ2UoZXZlbnQuZGF0YSk7XHJcbiAgICAgICAgICAgICAgICByZXNvbHZlKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgbWluZShoYXNoOiBzdHJpbmcsIGl0ZXJhdGlvbnM6IG51bWJlciwgdGFyZ2V0OiBudW1iZXIsIHRpbWVvdXQ/OiBudW1iZXIpIDogUHJvbWlzZTxCb29sZWFuPiB7XHJcbiAgICAgICAgdGhpcy5fY3VycmVudF9oYXNoID0gaGFzaDtcclxuICAgICAgICBpZih0YXJnZXQgPCB0aGlzLl9iZXN0X2xldmVsKVxyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlPEJvb2xlYW4+KChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5fd29ya2VyLnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgIHR5cGU6IFwibWluZVwiLFxyXG4gICAgICAgICAgICAgICAgaGFzaDogdGhpcy5fY3VycmVudF9oYXNoLFxyXG4gICAgICAgICAgICAgICAgaXRlcmF0aW9uczogaXRlcmF0aW9ucyxcclxuICAgICAgICAgICAgICAgIHRhcmdldDogdGFyZ2V0LFxyXG4gICAgICAgICAgICAgICAgY29kZTogXCJtaW5lXCJcclxuICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0IChtaW5lKVwiKSwgdGltZW91dCB8fCA1MDAwKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuX3dvcmtlci5vbm1lc3NhZ2UgPSBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4gdGhpcy5oYW5kbGVfbWVzc2FnZShldmVudC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dF9pZCk7XHJcbiAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJpbnZhbGlkIGRhdGFcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmICghZXZlbnQuZGF0YS5zdWNjZXNzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwibWluaW5nIGZhaWxlZCAoXCIgKyBldmVudC5kYXRhLnN1Y2Nlc3MgKyBcIiB8IFwiICsgKGV2ZW50LmRhdGEubWVzc2FnZSB8fCBcInVua25vd24gZXJvcm9yXCIpICsgXCIpXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZihldmVudC5kYXRhLnJlc3VsdCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2Jlc3RfbGV2ZWwgPSBldmVudC5kYXRhLmxldmVsO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2N1cnJlbnRfaGFzaCA9IGV2ZW50LmRhdGEuaGFzaDtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHRydWUpO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKGZhbHNlKTsgLyogbm8gcmVzdWx0ICovXHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgY3VycmVudF9oYXNoKCkgOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9jdXJyZW50X2hhc2g7XHJcbiAgICB9XHJcblxyXG4gICAgY3VycmVudF9sZXZlbCgpIDogbnVtYmVyIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fYmVzdF9sZXZlbDtcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBmaW5hbGl6ZSh0aW1lb3V0PzogbnVtYmVyKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLnBvc3RNZXNzYWdlKHtcclxuICAgICAgICAgICAgICAgICAgICB0eXBlOiBcImZpbmFsaXplXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgY29kZTogXCJmaW5hbGl6ZVwiXHJcbiAgICAgICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0X2lkID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0XCIpLCB0aW1lb3V0IHx8IDI1MCk7XHJcblxyXG4gICAgICAgICAgICAgICAgdGhpcy5fd29ya2VyLm9ubWVzc2FnZSA9IGV2ZW50ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl93b3JrZXIub25tZXNzYWdlID0gZXZlbnQgPT4gdGhpcy5oYW5kbGVfbWVzc2FnZShldmVudC5kYXRhKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRfaWQpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KFwiaW52YWxpZCBkYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWV2ZW50LmRhdGEuc3VjY2Vzcykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QoXCJmYWlsZWQgdG8gZmluYWxpemUgKFwiICsgZXZlbnQuZGF0YS5zdWNjZXNzICsgXCIgfCBcIiArIChldmVudC5kYXRhLm1lc3NhZ2UgfHwgXCJ1bmtub3duIGVyb3JvclwiKSArIFwiKVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xyXG4gICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gZmluYWxpemUgUE9XIHdvcmtlciEgKCVvKVwiKSwgZXJyb3IpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fd29ya2VyLnRlcm1pbmF0ZSgpO1xyXG4gICAgICAgIHRoaXMuX3dvcmtlciA9IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBwcml2YXRlIGhhbmRsZV9tZXNzYWdlKG1lc3NhZ2U6IGFueSkge1xyXG4gICAgICAgIGxvZy5pbmZvKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIHRyKFwiUmVjZWl2ZWQgbWVzc2FnZTogJW9cIiksIG1lc3NhZ2UpO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgY2xhc3MgVGVhU3BlYWtJZGVudGl0eSBpbXBsZW1lbnRzIElkZW50aXR5IHtcclxuICAgIHN0YXRpYyBhc3luYyBnZW5lcmF0ZV9uZXcoKSA6IFByb21pc2U8VGVhU3BlYWtJZGVudGl0eT4ge1xyXG4gICAgICAgIGxldCBrZXk6IENyeXB0b0tleVBhaXI7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAga2V5ID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5nZW5lcmF0ZUtleSh7bmFtZTonRUNESCcsIG5hbWVkQ3VydmU6ICdQLTI1Nid9LCB0cnVlLCBbXCJkZXJpdmVLZXlcIl0pO1xyXG4gICAgICAgIH0gY2F0Y2goZSkge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJDb3VsZCBub3QgZ2VuZXJhdGUgYSBuZXcga2V5OiAlb1wiKSwgZSk7XHJcbiAgICAgICAgICAgIHRocm93IFwiRmFpbGVkIHRvIGdlbmVyYXRlIGtleXBhaXJcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgY29uc3QgcHJpdmF0ZV9rZXkgPSBhd2FpdCBDcnlwdG9IZWxwZXIuZXhwb3J0X2VjY19rZXkoa2V5LnByaXZhdGVLZXksIGZhbHNlKTtcclxuXHJcbiAgICAgICAgY29uc3QgaWRlbnRpdHkgPSBuZXcgVGVhU3BlYWtJZGVudGl0eShwcml2YXRlX2tleSwgXCIwXCIsIHVuZGVmaW5lZCwgZmFsc2UpO1xyXG4gICAgICAgIGF3YWl0IGlkZW50aXR5LmluaXRpYWxpemUoKTtcclxuICAgICAgICByZXR1cm4gaWRlbnRpdHk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljIGFzeW5jIGltcG9ydF90cyh0c19zdHJpbmc6IHN0cmluZywgaW5pPzogYm9vbGVhbikgOiBQcm9taXNlPFRlYVNwZWFrSWRlbnRpdHk+IHtcclxuICAgICAgICBjb25zdCBwYXJzZV9zdHJpbmcgPSBzdHJpbmcgPT4ge1xyXG4gICAgICAgICAgICAvKiBwYXJzaW5nIHdpdGhvdXQgSU5JIHN0cnVjdHVyZSAqL1xyXG4gICAgICAgICAgICBjb25zdCBWX2luZGV4ID0gc3RyaW5nLmluZGV4T2YoJ1YnKTtcclxuICAgICAgICAgICAgaWYoVl9pbmRleCA9PSAtMSkgdGhyb3cgXCJpbnZhbGlkIGlucHV0IChtaXNzaW5nIFYpXCI7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgaGFzaDogc3RyaW5nLnN1YnN0cigwLCBWX2luZGV4KSxcclxuICAgICAgICAgICAgICAgIGRhdGE6IHN0cmluZy5zdWJzdHIoVl9pbmRleCArIDEpLFxyXG4gICAgICAgICAgICAgICAgbmFtZTogXCJUZWFTcGVhayB1c2VyXCJcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIGNvbnN0IHtoYXNoLCBkYXRhLCBuYW1lfSA9ICghaW5pID8gKCkgPT4gcGFyc2Vfc3RyaW5nKHRzX3N0cmluZykgOiAoKSA9PiB7XHJcbiAgICAgICAgICAgIC8qIHBhcnNpbmcgd2l0aCBJTkkgc3RydWN0dXJlICovXHJcbiAgICAgICAgICAgIGxldCBpZGVudGl0eTogc3RyaW5nLCBuYW1lOiBzdHJpbmc7XHJcblxyXG4gICAgICAgICAgICBmb3IoY29uc3QgbGluZSBvZiB0c19zdHJpbmcuc3BsaXQoXCJcXG5cIikpIHtcclxuICAgICAgICAgICAgICAgIGlmKGxpbmUuc3RhcnRzV2l0aChcImlkZW50aXR5PVwiKSlcclxuICAgICAgICAgICAgICAgICAgICBpZGVudGl0eSA9IGxpbmUuc3Vic3RyKDkpO1xyXG4gICAgICAgICAgICAgICAgZWxzZSBpZihsaW5lLnN0YXJ0c1dpdGgoXCJuaWNrbmFtZT1cIikpXHJcbiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGxpbmUuc3Vic3RyKDkpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZighaWRlbnRpdHkpIHRocm93IFwibWlzc2luZyBpZGVudGl0eSBrZXl3b3JkXCI7XHJcbiAgICAgICAgICAgIGlkZW50aXR5ID0gaWRlbnRpdHkubWF0Y2goL15cIj8oWzAtOV0rVlswLTlhLXpBLVorXFwvXStbPV0rKVwiPyQvKVsxXTtcclxuICAgICAgICAgICAgaWYoIWlkZW50aXR5KSB0aHJvdyBcImludmFsaWQgaWRlbnRpdHkga2V5IHZhbHVlXCI7XHJcblxyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBwYXJzZV9zdHJpbmcoaWRlbnRpdHkpO1xyXG4gICAgICAgICAgICByZXN1bHQubmFtZSA9IG5hbWUgfHwgcmVzdWx0Lm5hbWU7XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfSkoKTtcclxuXHJcbiAgICAgICAgaWYoIXRzX3N0cmluZy5tYXRjaCgvWzAtOV0rL2cpKSB0aHJvdyBcImludmFsaWQgaGFzaCFcIjtcclxuXHJcbiAgICAgICAgbGV0IGJ1ZmZlcjtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBidWZmZXIgPSBuZXcgVWludDhBcnJheShhcnJheUJ1ZmZlckJhc2U2NChkYXRhKSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgdHIoXCJGYWlsZWQgdG8gZGVjb2RlIGdpdmVuIGJhc2U2NCBkYXRhICglcylcIiksIGRhdGEpO1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBiYXNlIGRhdGEgKGJhc2U2NCBkZWNvZGUgZmFpbGVkKVwiO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjb25zdCBrZXk2NCA9IGF3YWl0IENyeXB0b0hlbHBlci5kZWNyeXB0X3RzX2lkZW50aXR5KG5ldyBVaW50OEFycmF5KGFycmF5QnVmZmVyQmFzZTY0KGRhdGEpKSk7XHJcblxyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gbmV3IFRlYVNwZWFrSWRlbnRpdHkoa2V5NjQsIGhhc2gsIG5hbWUsIGZhbHNlKTtcclxuICAgICAgICBhd2FpdCBpZGVudGl0eS5pbml0aWFsaXplKCk7XHJcbiAgICAgICAgcmV0dXJuIGlkZW50aXR5O1xyXG4gICAgfVxyXG5cclxuICAgIGhhc2hfbnVtYmVyOiBzdHJpbmc7IC8qIGhhc2ggc3VmZml4IGZvciB0aGUgcHJpdmF0ZSBrZXkgKi9cclxuICAgIHByaXZhdGVfa2V5OiBzdHJpbmc7IC8qIGJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgcHJpdmF0ZSBrZXkgKi9cclxuICAgIF9uYW1lOiBzdHJpbmc7XHJcblxyXG4gICAgcHVibGljX2tleTogc3RyaW5nOyAvKiBvbmx5IHNldCB3aGVuIGluaXRpYWxpemVkICovXHJcblxyXG4gICAgcHJpdmF0ZSBfaW5pdGlhbGl6ZWQ6IGJvb2xlYW47XHJcbiAgICBwcml2YXRlIF9jcnlwdG9fa2V5OiBDcnlwdG9LZXk7XHJcbiAgICBwcml2YXRlIF9jcnlwdG9fa2V5X3NpZ246IENyeXB0b0tleTtcclxuXHJcbiAgICBwcml2YXRlIF91bmlxdWVfaWQ6IHN0cmluZztcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcihwcml2YXRlX2tleT86IHN0cmluZywgaGFzaD86IHN0cmluZywgbmFtZT86IHN0cmluZywgaW5pdGlhbGl6ZT86IGJvb2xlYW4pIHtcclxuICAgICAgICB0aGlzLnByaXZhdGVfa2V5ID0gcHJpdmF0ZV9rZXk7XHJcbiAgICAgICAgdGhpcy5oYXNoX251bWJlciA9IGhhc2ggfHwgXCIwXCI7XHJcbiAgICAgICAgdGhpcy5fbmFtZSA9IG5hbWU7XHJcblxyXG4gICAgICAgIGlmKHRoaXMucHJpdmF0ZV9rZXkgJiYgKHR5cGVvZihpbml0aWFsaXplKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCBpbml0aWFsaXplKSkge1xyXG4gICAgICAgICAgICB0aGlzLmluaXRpYWxpemUoKS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBUZWFTcGVha0lkZW50aXR5ICglcylcIiwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5faW5pdGlhbGl6ZWQgPSBmYWxzZTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZhbGxiYWNrX25hbWUoKTogc3RyaW5nIHwgdW5kZWZpbmVkICB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX25hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgdWlkKCk6IHN0cmluZyB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3VuaXF1ZV9pZDtcclxuICAgIH1cclxuXHJcbiAgICB0eXBlKCk6IElkZW50aXRpZnlUeXBlIHtcclxuICAgICAgICByZXR1cm4gSWRlbnRpdGlmeVR5cGUuVEVBTVNQRUFLO1xyXG4gICAgfVxyXG5cclxuICAgIHZhbGlkKCk6IGJvb2xlYW4ge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9pbml0aWFsaXplZCAmJiAhIXRoaXMuX2NyeXB0b19rZXkgJiYgISF0aGlzLl9jcnlwdG9fa2V5X3NpZ247XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZGVjb2RlKGRhdGE6IHN0cmluZykgOiBQcm9taXNlPHZvaWQ+IHtcclxuICAgICAgICBjb25zdCBqc29uID0gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgICAgICBpZighanNvbikgdGhyb3cgXCJpbnZhbGlkIGpzb25cIjtcclxuXHJcbiAgICAgICAgaWYoanNvbi52ZXJzaW9uID09IDIpIHtcclxuICAgICAgICAgICAgdGhpcy5wcml2YXRlX2tleSA9IGpzb24ua2V5O1xyXG4gICAgICAgICAgICB0aGlzLmhhc2hfbnVtYmVyID0ganNvbi5oYXNoO1xyXG4gICAgICAgICAgICB0aGlzLl9uYW1lID0ganNvbi5uYW1lO1xyXG4gICAgICAgIH0gZWxzZSBpZihqc29uLnZlcnNpb24gPT0gMSkge1xyXG4gICAgICAgICAgICBjb25zdCBrZXkgPSBqc29uLmtleTtcclxuICAgICAgICAgICAgdGhpcy5fbmFtZSA9IGpzb24ubmFtZTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IGNsb25lID0gYXdhaXQgVGVhU3BlYWtJZGVudGl0eS5pbXBvcnRfdHMoa2V5LCBmYWxzZSk7XHJcbiAgICAgICAgICAgIHRoaXMucHJpdmF0ZV9rZXkgPSBjbG9uZS5wcml2YXRlX2tleTtcclxuICAgICAgICAgICAgdGhpcy5oYXNoX251bWJlciA9IGNsb25lLmhhc2hfbnVtYmVyO1xyXG4gICAgICAgIH0gZWxzZVxyXG4gICAgICAgICAgICB0aHJvdyBcImludmFsaWQgdmVyc2lvblwiO1xyXG5cclxuICAgICAgICBhd2FpdCB0aGlzLmluaXRpYWxpemUoKTtcclxuICAgIH1cclxuXHJcbiAgICBlbmNvZGU/KCkgOiBzdHJpbmcge1xyXG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh7XHJcbiAgICAgICAgICAgIGtleTogdGhpcy5wcml2YXRlX2tleSxcclxuICAgICAgICAgICAgaGFzaDogdGhpcy5oYXNoX251bWJlcixcclxuICAgICAgICAgICAgbmFtZTogdGhpcy5fbmFtZSxcclxuICAgICAgICAgICAgdmVyc2lvbjogMlxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIGxldmVsKCkgOiBQcm9taXNlPG51bWJlcj4ge1xyXG4gICAgICAgIGlmKCF0aGlzLl9pbml0aWFsaXplZCB8fCAhdGhpcy5wdWJsaWNfa2V5KVxyXG4gICAgICAgICAgICB0aHJvdyBcIm5vdCBpbml0aWFsaXplZFwiO1xyXG5cclxuICAgICAgICBjb25zdCBoYXNoID0gbmV3IFVpbnQ4QXJyYXkoYXdhaXQgc2hhLnNoYTEodGhpcy5wdWJsaWNfa2V5ICsgdGhpcy5oYXNoX251bWJlcikpO1xyXG5cclxuICAgICAgICBsZXQgbGV2ZWwgPSAwO1xyXG4gICAgICAgIHdoaWxlKGxldmVsIDwgaGFzaC5ieXRlTGVuZ3RoICYmIGhhc2hbbGV2ZWxdID09IDApXHJcbiAgICAgICAgICAgIGxldmVsKys7XHJcblxyXG4gICAgICAgIGlmKGxldmVsID49IGhhc2guYnl0ZUxlbmd0aCkge1xyXG4gICAgICAgICAgICBsZXZlbCA9IDI1NjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBsZXQgYnl0ZSA9IGhhc2hbbGV2ZWxdO1xyXG4gICAgICAgICAgICBsZXZlbCA8PD0gMztcclxuICAgICAgICAgICAgd2hpbGUoKGJ5dGUgJiAweDEpID09IDApIHtcclxuICAgICAgICAgICAgICAgIGxldmVsKys7XHJcbiAgICAgICAgICAgICAgICBieXRlID4+PSAxO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gbGV2ZWw7XHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gYVxyXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGJcclxuICAgICAqIEBkZXNjcmlwdGlvbiBiIG11c3QgYmUgc21hbGxlciAoaW4gYnl0ZXMpIHRoZW4gYVxyXG4gICAgICovXHJcbiAgICBwcml2YXRlIHN0cmluZ19hZGQoYTogc3RyaW5nLCBiOiBzdHJpbmcpIHtcclxuICAgICAgICBjb25zdCBjaGFyX3Jlc3VsdDogbnVtYmVyW10gPSBbXTtcclxuICAgICAgICBjb25zdCBjaGFyX2EgPSBbLi4uYV0ucmV2ZXJzZSgpLm1hcChlID0+IGUuY2hhckNvZGVBdCgwKSk7XHJcbiAgICAgICAgY29uc3QgY2hhcl9iID0gWy4uLmJdLnJldmVyc2UoKS5tYXAoZSA9PiBlLmNoYXJDb2RlQXQoMCkpO1xyXG5cclxuICAgICAgICBsZXQgY2FycnkgPSBmYWxzZTtcclxuICAgICAgICB3aGlsZShjaGFyX2IubGVuZ3RoID4gMCkge1xyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gY2hhcl9iLnBvcF9mcm9udCgpICsgY2hhcl9hLnBvcF9mcm9udCgpICsgKGNhcnJ5ID8gMSA6IDApIC0gNDg7XHJcbiAgICAgICAgICAgIGlmKChjYXJyeSA9IHJlc3VsdCA+IDU3KSlcclxuICAgICAgICAgICAgICAgIHJlc3VsdCAtPSAxMDtcclxuICAgICAgICAgICAgY2hhcl9yZXN1bHQucHVzaChyZXN1bHQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgd2hpbGUoY2hhcl9hLmxlbmd0aCA+IDApIHtcclxuICAgICAgICAgICAgbGV0IHJlc3VsdCA9IGNoYXJfYS5wb3BfZnJvbnQoKSArIChjYXJyeSA/IDEgOiAwKTtcclxuICAgICAgICAgICAgaWYoKGNhcnJ5ID0gcmVzdWx0ID4gNTcpKVxyXG4gICAgICAgICAgICAgICAgcmVzdWx0IC09IDEwO1xyXG4gICAgICAgICAgICBjaGFyX3Jlc3VsdC5wdXNoKHJlc3VsdCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihjYXJyeSlcclxuICAgICAgICAgICAgY2hhcl9yZXN1bHQucHVzaCg0OSk7XHJcblxyXG4gICAgICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNoYXJfcmVzdWx0LnNsaWNlKCkucmV2ZXJzZSgpKTtcclxuICAgIH1cclxuXHJcblxyXG4gICAgYXN5bmMgaW1wcm92ZV9sZXZlbF9mb3IodGltZTogbnVtYmVyLCB0aHJlYWRzOiBudW1iZXIpIDogUHJvbWlzZTxCb29sZWFuPiB7XHJcbiAgICAgICAgbGV0IGFjdGl2ZSA9IHRydWU7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiBhY3RpdmUgPSBmYWxzZSwgdGltZSk7XHJcblxyXG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmltcHJvdmVfbGV2ZWwoLTEsIHRocmVhZHMsICgpID0+IGFjdGl2ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgaW1wcm92ZV9sZXZlbCh0YXJnZXQ6IG51bWJlciwgdGhyZWFkczogbnVtYmVyLCBhY3RpdmVfY2FsbGJhY2s6ICgpID0+IGJvb2xlYW4sIGNhbGxiYWNrX2xldmVsPzogKGN1cnJlbnQ6IG51bWJlcikgPT4gYW55LCBjYWxsYmFja19zdGF0dXM/OiAoaGFzaF9yYXRlOiBudW1iZXIpID0+IGFueSkgOiBQcm9taXNlPEJvb2xlYW4+IHtcclxuICAgICAgICBpZighdGhpcy5faW5pdGlhbGl6ZWQgfHwgIXRoaXMucHVibGljX2tleSlcclxuICAgICAgICAgICAgdGhyb3cgXCJub3QgaW5pdGlhbGl6ZWRcIjtcclxuICAgICAgICBpZih0YXJnZXQgPT0gLTEpIC8qIGdldCB0aGUgaGlnaGVzdCBsZXZlbCBwb3NzaWJsZSAqL1xyXG4gICAgICAgICAgICB0YXJnZXQgPSAwO1xyXG4gICAgICAgIGVsc2UgaWYodGFyZ2V0IDw9IGF3YWl0IHRoaXMubGV2ZWwoKSlcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcblxyXG4gICAgICAgIGNvbnN0IHdvcmtlcnM6IElkZW50aXR5UE9XV29ya2VyW10gPSBbXTtcclxuXHJcbiAgICAgICAgY29uc3QgaXRlcmF0aW9ucyA9IDEwMDAwMDtcclxuICAgICAgICBsZXQgY3VycmVudF9oYXNoO1xyXG4gICAgICAgIGNvbnN0IG5leHRfaGFzaCA9ICgpID0+IHtcclxuICAgICAgICAgICAgaWYoIWN1cnJlbnRfaGFzaClcclxuICAgICAgICAgICAgICAgIHJldHVybiAoY3VycmVudF9oYXNoID0gdGhpcy5oYXNoX251bWJlcik7XHJcblxyXG4gICAgICAgICAgICBpZihjdXJyZW50X2hhc2gubGVuZ3RoIDwgaXRlcmF0aW9ucy50b1N0cmluZygpLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgY3VycmVudF9oYXNoID0gdGhpcy5zdHJpbmdfYWRkKGl0ZXJhdGlvbnMudG9TdHJpbmcoKSwgY3VycmVudF9oYXNoKTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIGN1cnJlbnRfaGFzaCA9IHRoaXMuc3RyaW5nX2FkZChjdXJyZW50X2hhc2gsIGl0ZXJhdGlvbnMudG9TdHJpbmcoKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIGN1cnJlbnRfaGFzaDtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB7IC8qIGluaXQgKi9cclxuICAgICAgICAgICAgY29uc3QgaW5pdGlhbGl6ZV9wcm9taXNlOiBQcm9taXNlPHZvaWQ+W10gPSBbXTtcclxuICAgICAgICAgICAgZm9yKGxldCBpbmRleCA9IDA7IGluZGV4ICA8IHRocmVhZHM7IGluZGV4KyspIHtcclxuICAgICAgICAgICAgICAgIGNvbnN0IHdvcmtlciA9IG5ldyBJZGVudGl0eVBPV1dvcmtlcigpO1xyXG4gICAgICAgICAgICAgICAgd29ya2Vycy5wdXNoKHdvcmtlcik7XHJcbiAgICAgICAgICAgICAgICBpbml0aWFsaXplX3Byb21pc2UucHVzaCh3b3JrZXIuaW5pdGlhbGl6ZSh0aGlzLnB1YmxpY19rZXkpKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGluaXRpYWxpemVfcHJvbWlzZSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBpbml0aWFsaXplXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCByZXN1bHQgPSBmYWxzZTtcclxuICAgICAgICBsZXQgYmVzdF9sZXZlbCA9IDA7XHJcbiAgICAgICAgbGV0IHRhcmdldF9sZXZlbCA9IHRhcmdldCA+IDAgPyB0YXJnZXQgOiBhd2FpdCB0aGlzLmxldmVsKCkgKyAxO1xyXG5cclxuICAgICAgICBjb25zdCB3b3JrZXJfcHJvbWlzZTogUHJvbWlzZTx2b2lkPltdID0gW107XHJcblxyXG4gICAgICAgIGNvbnN0IGhhc2hfdGltZXN0YW1wczogbnVtYmVyW10gPSBbXTtcclxuICAgICAgICBsZXQgbGFzdF9oYXNocmF0ZV91cGRhdGU6IG51bWJlciA9IDA7XHJcblxyXG4gICAgICAgIGNvbnN0IHVwZGF0ZV9oYXNocmF0ZSA9ICgpID0+IHtcclxuICAgICAgICAgICAgaWYoIWNhbGxiYWNrX3N0YXR1cykgcmV0dXJuO1xyXG4gICAgICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgICAgICBoYXNoX3RpbWVzdGFtcHMucHVzaChub3cpO1xyXG5cclxuICAgICAgICAgICAgaWYobGFzdF9oYXNocmF0ZV91cGRhdGUgKyAxMDAwIDwgbm93KSB7XHJcbiAgICAgICAgICAgICAgICBsYXN0X2hhc2hyYXRlX3VwZGF0ZSA9IG5vdztcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCB0aW1lb3V0ID0gbm93IC0gMTAgKiAxMDAwOyAvKiAxMHMgKi9cclxuICAgICAgICAgICAgICAgIGNvbnN0IHJvdW5kcyA9IGhhc2hfdGltZXN0YW1wcy5maWx0ZXIoZSA9PiBlID4gdGltZW91dCk7XHJcbiAgICAgICAgICAgICAgICBjYWxsYmFja19zdGF0dXMoTWF0aC5jZWlsKChyb3VuZHMubGVuZ3RoICogaXRlcmF0aW9ucykgLyBNYXRoLmNlaWwoKG5vdyAtIHJvdW5kc1swXSkgLyAxMDAwKSkpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXN1bHQgPSBhd2FpdCBuZXcgUHJvbWlzZTxib29sZWFuPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICBsZXQgYWN0aXZlID0gdHJ1ZTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBleGl0ID0gKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHJlc29sdmUodHJ1ZSksIDEwMDApO1xyXG4gICAgICAgICAgICAgICAgICAgIFByb21pc2UuYWxsKHdvcmtlcl9wcm9taXNlKS50aGVuKHJlc3VsdCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSh0cnVlKTtcclxuICAgICAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiByZXNvbHZlKHRydWUpKTtcclxuICAgICAgICAgICAgICAgICAgICBhY3RpdmUgPSBmYWxzZTtcclxuICAgICAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHdvcmtlciBvZiB3b3JrZXJzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgd29ya2VyX21pbmUgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFhY3RpdmUpIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHByb21pc2UgPSB3b3JrZXIubWluZShuZXh0X2hhc2goKSwgaXRlcmF0aW9ucywgdGFyZ2V0X2xldmVsKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcCA9IHByb21pc2UudGhlbihyZXN1bHQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBkYXRlX2hhc2hyYXRlKCk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgd29ya2VyX3Byb21pc2UucmVtb3ZlKHApO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKHJlc3VsdC52YWx1ZU9mKCkpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZih3b3JrZXIuY3VycmVudF9sZXZlbCgpID4gYmVzdF9sZXZlbCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmhhc2hfbnVtYmVyID0gd29ya2VyLmN1cnJlbnRfaGFzaCgpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmluZm8oTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgXCJGb3VuZCBuZXcgYmVzdCBhdCAlcyAoJWQpLiBPbGQgd2FzICVkXCIsIHRoaXMuaGFzaF9udW1iZXIsIHdvcmtlci5jdXJyZW50X2xldmVsKCksIGJlc3RfbGV2ZWwpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZXN0X2xldmVsID0gd29ya2VyLmN1cnJlbnRfbGV2ZWwoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoY2FsbGJhY2tfbGV2ZWwpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWxsYmFja19sZXZlbChiZXN0X2xldmVsKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKGFjdGl2ZSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZih0YXJnZXQgPiAwKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhpdCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRfbGV2ZWwgPSBiZXN0X2xldmVsICsgMTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoYWN0aXZlICYmIChhY3RpdmUgPSBhY3RpdmVfY2FsbGJhY2soKSkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB3b3JrZXJfbWluZSgpLCAwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4aXQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmtlcl9wcm9taXNlLnJlbW92ZShwKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2cud2FybihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBcIlBPVyB3b3JrZXIgZXJyb3IgJW9cIiwgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgd29ya2VyX3Byb21pc2UucHVzaChwKTtcclxuICAgICAgICAgICAgICAgICAgICB9O1xyXG5cclxuICAgICAgICAgICAgICAgICAgICB3b3JrZXJfbWluZSgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIC8vZXJyb3IgYWxyZWFkeSBwcmludGVkIGJlZm9yZSByZWplY3QgaGFkIGJlZW4gY2FsbGVkXHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB7IC8qIHNodXRkb3duICovXHJcbiAgICAgICAgICAgIGNvbnN0IGZpbmFsaXplX3Byb21pc2U6IFByb21pc2U8dm9pZD5bXSA9IFtdO1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qgd29ya2VyIG9mIHdvcmtlcnMpXHJcbiAgICAgICAgICAgICAgICBmaW5hbGl6ZV9wcm9taXNlLnB1c2god29ya2VyLmZpbmFsaXplKDI1MCkpO1xyXG5cclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKGZpbmFsaXplX3Byb21pc2UpO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gZmluYWxpemVcIjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIGlmKCF0aGlzLnByaXZhdGVfa2V5KVxyXG4gICAgICAgICAgICB0aHJvdyBcIkludmFsaWQgcHJpdmF0ZSBrZXlcIjtcclxuXHJcbiAgICAgICAgbGV0IGp3azogYW55O1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGp3ayA9IGF3YWl0IENyeXB0b0hlbHBlci5kZWNvZGVfdG9tY3J5cHRfa2V5KHRoaXMucHJpdmF0ZV9rZXkpO1xyXG4gICAgICAgICAgICBpZighandrKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgXCJyZXN1bHQgdW5kZWZpbmVkXCI7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBwYXJzZSBrZXkgKFwiICsgZXJyb3IgKyBcIilcIjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NyeXB0b19rZXlfc2lnbiA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuaW1wb3J0S2V5KFwiandrXCIsIGp3aywge25hbWU6J0VDRFNBJywgbmFtZWRDdXJ2ZTogJ1AtMjU2J30sIGZhbHNlLCBbXCJzaWduXCJdKTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGxvZy5lcnJvcihMb2dDYXRlZ29yeS5JREVOVElUSUVTLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHRocm93IFwiZmFpbGVkIHRvIGNyZWF0ZSBjcnlwdG8gc2lnbiBrZXlcIjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NyeXB0b19rZXkgPSBhd2FpdCBjcnlwdG8uc3VidGxlLmltcG9ydEtleShcImp3a1wiLCBqd2ssIHtuYW1lOidFQ0RIJywgbmFtZWRDdXJ2ZTogJ1AtMjU2J30sIHRydWUsIFtcImRlcml2ZUtleVwiXSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuSURFTlRJVElFUywgZXJyb3IpO1xyXG4gICAgICAgICAgICB0aHJvdyBcImZhaWxlZCB0byBjcmVhdGUgY3J5cHRvIGtleVwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgdGhpcy5wdWJsaWNfa2V5ID0gYXdhaXQgQ3J5cHRvSGVscGVyLmV4cG9ydF9lY2Nfa2V5KHRoaXMuX2NyeXB0b19rZXksIHRydWUpO1xyXG4gICAgICAgICAgICB0aGlzLl91bmlxdWVfaWQgPSBiYXNlNjRfZW5jb2RlX2FiKGF3YWl0IHNoYS5zaGExKHRoaXMucHVibGljX2tleSkpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nLmVycm9yKExvZ0NhdGVnb3J5LklERU5USVRJRVMsIGVycm9yKTtcclxuICAgICAgICAgICAgdGhyb3cgXCJmYWlsZWQgdG8gY2FsY3VsYXRlIHVuaXF1ZSBpZFwiO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5faW5pdGlhbGl6ZWQgPSB0cnVlO1xyXG4gICAgICAgIC8vY29uc3QgcHVibGljX2tleSA9IGF3YWl0IHByb2ZpbGVzLmlkZW50aXRpZXMuQ3J5cHRvSGVscGVyLmV4cG9ydF9lY2Nfa2V5KGtleSwgdHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgYXN5bmMgZXhwb3J0X3RzKGluaT86IGJvb2xlYW4pIDogUHJvbWlzZTxzdHJpbmc+IHtcclxuICAgICAgICBpZighdGhpcy5wcml2YXRlX2tleSlcclxuICAgICAgICAgICAgdGhyb3cgXCJJbnZhbGlkIHByaXZhdGUga2V5XCI7XHJcblxyXG4gICAgICAgIGNvbnN0IGlkZW50aXR5ID0gdGhpcy5oYXNoX251bWJlciArIFwiVlwiICsgYXdhaXQgQ3J5cHRvSGVscGVyLmVuY3J5cHRfdHNfaWRlbnRpdHkobmV3IFVpbnQ4QXJyYXkoc3RyMmFiOCh0aGlzLnByaXZhdGVfa2V5KSkpO1xyXG4gICAgICAgIGlmKCFpbmkpIHJldHVybiBpZGVudGl0eTtcclxuXHJcbiAgICAgICAgcmV0dXJuIFwiW0lkZW50aXR5XVxcblwiICtcclxuICAgICAgICAgICAgICAgIFwiaWQ9VGVhV2ViLUV4cG9ydGVkXFxuXCIgK1xyXG4gICAgICAgICAgICAgICAgXCJpZGVudGl0eT1cXFwiXCIgKyBpZGVudGl0eSArIFwiXFxcIlxcblwiICtcclxuICAgICAgICAgICAgICAgIFwibmlja25hbWU9XFxcIlwiICsgdGhpcy5mYWxsYmFja19uYW1lKCkgKyBcIlxcXCJcXG5cIiArXHJcbiAgICAgICAgICAgICAgICBcInBob25ldGljX25pY2tuYW1lPVwiO1xyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIHNpZ25fbWVzc2FnZShtZXNzYWdlOiBzdHJpbmcsIGhhc2g6IHN0cmluZyA9IFwiU0hBLTI1NlwiKSA6IFByb21pc2U8c3RyaW5nPiB7XHJcbiAgICAgICAgLyogYnJpbmcgdGhpcyB0byBsaWJ0b21jcnlwdCBmb3JtYXQgKi9cclxuICAgICAgICBjb25zdCBzaWduX2J1ZmZlciA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuc2lnbih7XHJcbiAgICAgICAgICAgIG5hbWU6IFwiRUNEU0FcIixcclxuICAgICAgICAgICAgaGFzaDogaGFzaFxyXG4gICAgICAgIH0sIHRoaXMuX2NyeXB0b19rZXlfc2lnbiwgc3RyMmFiOChtZXNzYWdlKSk7XHJcbiAgICAgICAgY29uc3Qgc2lnbiA9IG5ldyBVaW50OEFycmF5KHNpZ25fYnVmZmVyKTtcclxuICAgICAgICAvKiBmaXJzdCAzMiByIGJpdHMgfCBsYXN0IDMyIHMgYml0cyAqL1xyXG5cclxuICAgICAgICBjb25zdCBidWZmZXIgPSBuZXcgVWludDhBcnJheSg3Mik7XHJcbiAgICAgICAgbGV0IGluZGV4ID0gMDtcclxuXHJcbiAgICAgICAgeyAvKiB0aGUgaW5pdGlhbCBzZXF1ZW5jZSAqL1xyXG4gICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAweDMwOyAvKiB0eXBlICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDA7IC8qIHdlIHdpbGwgc2V0IHRoZSBzZXF1ZW5jZSBsZW5ndGggbGF0ZXIgKi9cclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBpbnRlZ2VyIHIgICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgyMDsgLyogbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICBpZihzaWduWzBdID4gMHg3Rikge1xyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4IC0gMV0gKz0gMTtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDA7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvcihsZXQgaSA9IDA7IGkgPCAzMjsgaSsrKVxyXG4gICAgICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gc2lnbltpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgeyAvKiBpbnRlZ2VyIHMgICovXHJcbiAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IDB4MDI7IC8qIHR5cGUgKi9cclxuICAgICAgICAgICAgYnVmZmVyW2luZGV4KytdID0gMHgyMDsgLyogbGVuZ3RoICovXHJcblxyXG4gICAgICAgICAgICBpZihzaWduWzMyXSA+IDB4N0YpIHtcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCAtIDFdICs9IDE7XHJcbiAgICAgICAgICAgICAgICBidWZmZXJbaW5kZXgrK10gPSAwO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBmb3IobGV0IGkgPSAwOyBpIDwgMzI7IGkrKylcclxuICAgICAgICAgICAgICAgIGJ1ZmZlcltpbmRleCsrXSA9IHNpZ25bMzIgKyBpXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgYnVmZmVyWzFdID0gaW5kZXggLSAyO1xyXG5cclxuICAgICAgICByZXR1cm4gYmFzZTY0X2VuY29kZV9hYihidWZmZXIuc3ViYXJyYXkoMCwgaW5kZXgpKTtcclxuICAgIH1cclxuXHJcbiAgICBzcGF3bl9pZGVudGl0eV9oYW5kc2hha2VfaGFuZGxlcihjb25uZWN0aW9uOiBBYnN0cmFjdFNlcnZlckNvbm5lY3Rpb24pOiBIYW5kc2hha2VJZGVudGl0eUhhbmRsZXIge1xyXG4gICAgICAgIHJldHVybiBuZXcgVGVhU3BlYWtIYW5kc2hha2VIYW5kbGVyKGNvbm5lY3Rpb24sIHRoaXMpO1xyXG4gICAgfVxyXG59IiwiaW1wb3J0IHtTZXR0aW5ncywgc2V0dGluZ3N9IGZyb20gXCIuLi8uLi9zZXR0aW5nc1wiO1xyXG5pbXBvcnQge3VwZGF0ZV9mb3J1bX0gZnJvbSBcIi4vVGVhRm9ydW1JZGVudGl0eVwiO1xyXG5cclxuZGVjbGFyZSBpbnRlcmZhY2UgV2luZG93IHtcclxuICAgIGdyZWNhcHRjaGE6IEdSZUNhcHRjaGE7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgR1JlQ2FwdGNoYSB7XHJcbiAgICByZW5kZXIoY29udGFpbmVyOiBzdHJpbmcgfCBIVE1MRWxlbWVudCwgcGFyYW1ldGVyczoge1xyXG4gICAgICAgIHNpdGVrZXk6IHN0cmluZztcclxuICAgICAgICB0aGVtZT86IFwiZGFya1wiIHwgXCJsaWdodFwiO1xyXG4gICAgICAgIHNpemU/OiBcImNvbXBhY3RcIiB8IFwibm9ybWFsXCI7XHJcblxyXG4gICAgICAgIHRhYmluZGV4PzogbnVtYmVyO1xyXG5cclxuICAgICAgICBjYWxsYmFjaz86ICh0b2tlbjogc3RyaW5nKSA9PiBhbnk7XHJcbiAgICAgICAgXCJleHBpcmVkLWNhbGxiYWNrXCI/OiAoKSA9PiBhbnk7XHJcbiAgICAgICAgXCJlcnJvci1jYWxsYmFja1wiPzogKGVycm9yOiBhbnkpID0+IGFueTtcclxuICAgIH0pIDogc3RyaW5nOyAvKiB3aWRnZXRfaWQgKi9cclxuXHJcbiAgICByZXNldCh3aWRnZXRfaWQ/OiBzdHJpbmcpO1xyXG59XHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIGZvcnVtIHtcclxuICAgIGV4cG9ydCBuYW1lc3BhY2UgZ2NhcHRjaGEge1xyXG4gICAgICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplKCkge1xyXG4gICAgICAgICAgICBpZih0eXBlb2YoKHdpbmRvdyBhcyBhbnkpLmdyZWNhcHRjaGEpID09PSBcInVuZGVmaW5lZFwiKSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgc2NyaXB0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKTtcclxuICAgICAgICAgICAgICAgIHNjcmlwdC5hc3luYyA9IHRydWU7XHJcblxyXG4gICAgICAgICAgICAgICAgbGV0IHRpbWVvdXQ7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBjYWxsYmFja19uYW1lID0gXCJjYXB0Y2hhX2NhbGxiYWNrX1wiICsgTWF0aC5yYW5kb20oKS50b1N0cmluZygpLnJlcGxhY2UoXCIuXCIsIFwiXCIpO1xyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcmlwdC5vbmVycm9yID0gcmVqZWN0O1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB3aW5kb3dbY2FsbGJhY2tfbmFtZV0gPSByZXNvbHZlO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBzY3JpcHQuc3JjID0gXCJodHRwczovL3d3dy5nb29nbGUuY29tL3JlY2FwdGNoYS9hcGkuanM/b25sb2FkPVwiICsgZW5jb2RlVVJJQ29tcG9uZW50KGNhbGxiYWNrX25hbWUpICsgXCImcmVuZGVyPWV4cGxpY2l0XCI7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZChzY3JpcHQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJ0aW1lb3V0XCIpLCAxNTAwMCk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgc2NyaXB0LnJlbW92ZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHNjcmlwdCA9IHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkZhaWxlZCB0byBmZXRjaCByZWNhcHRjaGEgamF2YXNjcmlwdCBzb3VyY2U6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gZG93bmxvYWQgc291cmNlXCIpO1xyXG4gICAgICAgICAgICAgICAgfSBmaW5hbGx5IHtcclxuICAgICAgICAgICAgICAgICAgICBpZihzY3JpcHQpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNjcmlwdC5vbmVycm9yID0gdW5kZWZpbmVkO1xyXG4gICAgICAgICAgICAgICAgICAgIGRlbGV0ZSB3aW5kb3dbY2FsbGJhY2tfbmFtZV07XHJcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZih0eXBlb2YoKHdpbmRvdyBhcyBhbnkpLmdyZWNhcHRjaGEpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gbG9hZCByZWNhcHRjaGFcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gc3Bhd24oY29udGFpbmVyOiBKUXVlcnksIGtleTogc3RyaW5nLCBjYWxsYmFja19kYXRhOiAodG9rZW46IHN0cmluZykgPT4gYW55KSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBhd2FpdCBpbml0aWFsaXplKCk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBHLVJlY2FwdGNoYS4gRXJyb3I6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyB0cihcImluaXRpYWxpc2F0aW9uIGZhaWxlZFwiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihjb250YWluZXIuYXR0cihcImNhcHRjaGEtdXVpZFwiKSlcclxuICAgICAgICAgICAgICAgICh3aW5kb3cgYXMgYW55KS5ncmVjYXB0Y2hhLnJlc2V0KGNvbnRhaW5lci5hdHRyKFwiY2FwdGNoYS11dWlkXCIpKTtcclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBjb250YWluZXIuYXR0cihcImNhcHRjaGEtdXVpZFwiLCAod2luZG93IGFzIGFueSkuZ3JlY2FwdGNoYS5yZW5kZXIoY29udGFpbmVyWzBdLCB7XHJcbiAgICAgICAgICAgICAgICAgICAgXCJzaXRla2V5XCI6IGtleSxcclxuICAgICAgICAgICAgICAgICAgICBjYWxsYmFjazogY2FsbGJhY2tfZGF0YVxyXG4gICAgICAgICAgICAgICAgfSkpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZ1bmN0aW9uIGFwaV91cmwoKSB7XHJcbiAgICAgICAgcmV0dXJuIHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX1RFQUZPUk9fVVJMKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY2xhc3MgRGF0YSB7XHJcbiAgICAgICAgcmVhZG9ubHkgYXV0aF9rZXk6IHN0cmluZztcclxuICAgICAgICByZWFkb25seSByYXc6IHN0cmluZztcclxuICAgICAgICByZWFkb25seSBzaWduOiBzdHJpbmc7XHJcblxyXG4gICAgICAgIHBhcnNlZDoge1xyXG4gICAgICAgICAgICB1c2VyX2lkOiBudW1iZXI7XHJcbiAgICAgICAgICAgIHVzZXJfbmFtZTogc3RyaW5nO1xyXG5cclxuICAgICAgICAgICAgZGF0YV9hZ2U6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgIHVzZXJfZ3JvdXBfaWQ6IG51bWJlcjtcclxuXHJcbiAgICAgICAgICAgIGlzX3N0YWZmOiBib29sZWFuO1xyXG4gICAgICAgICAgICB1c2VyX2dyb3VwczogbnVtYmVyW107XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgY29uc3RydWN0b3IoYXV0aDogc3RyaW5nLCByYXc6IHN0cmluZywgc2lnbjogc3RyaW5nKSB7XHJcbiAgICAgICAgICAgIHRoaXMuYXV0aF9rZXkgPSBhdXRoO1xyXG4gICAgICAgICAgICB0aGlzLnJhdyA9IHJhdztcclxuICAgICAgICAgICAgdGhpcy5zaWduID0gc2lnbjtcclxuXHJcbiAgICAgICAgICAgIHRoaXMucGFyc2VkID0gSlNPTi5wYXJzZShyYXcpO1xyXG4gICAgICAgIH1cclxuXHJcblxyXG4gICAgICAgIGRhdGFfanNvbigpIDogc3RyaW5nIHsgcmV0dXJuIHRoaXMucmF3OyB9XHJcbiAgICAgICAgZGF0YV9zaWduKCkgOiBzdHJpbmcgeyByZXR1cm4gdGhpcy5zaWduOyB9XHJcblxyXG4gICAgICAgIG5hbWUoKSA6IHN0cmluZyB7IHJldHVybiB0aGlzLnBhcnNlZC51c2VyX25hbWU7IH1cclxuXHJcbiAgICAgICAgdXNlcl9pZCgpIHsgcmV0dXJuIHRoaXMucGFyc2VkLnVzZXJfaWQ7IH1cclxuICAgICAgICB1c2VyX2dyb3VwKCkgeyByZXR1cm4gdGhpcy5wYXJzZWQudXNlcl9ncm91cF9pZDsgfVxyXG5cclxuICAgICAgICBpc19zdHVmZigpIDogYm9vbGVhbiB7IHJldHVybiB0aGlzLnBhcnNlZC5pc19zdGFmZjsgfVxyXG4gICAgICAgIGlzX3ByZW1pdW0oKSA6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5wYXJzZWQudXNlcl9ncm91cHMuaW5kZXhPZig1KSAhPSAtMTsgfVxyXG5cclxuICAgICAgICBkYXRhX2FnZSgpIDogRGF0ZSB7IHJldHVybiBuZXcgRGF0ZSh0aGlzLnBhcnNlZC5kYXRhX2FnZSk7IH1cclxuXHJcbiAgICAgICAgaXNfZXhwaXJlZCgpIDogYm9vbGVhbiB7IHJldHVybiB0aGlzLnBhcnNlZC5kYXRhX2FnZSArIDQ4ICogNjAgKiA2MCAqIDEwMDAgPCBEYXRlLm5vdygpOyB9XHJcbiAgICAgICAgc2hvdWxkX3JlbmV3KCkgOiBib29sZWFuIHsgcmV0dXJuIHRoaXMucGFyc2VkLmRhdGFfYWdlICsgMjQgKiA2MCAqIDYwICogMTAwMCA8IERhdGUubm93KCk7IH0gLyogcmVuZXcgZGF0YSBhbGwgMjRocnMgKi9cclxuICAgIH1cclxuICAgIGxldCBfZGF0YTogRGF0YSB8IHVuZGVmaW5lZDtcclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9nZ2VkX2luKCkgOiBib29sZWFuIHtcclxuICAgICAgICByZXR1cm4gISFfZGF0YSAmJiAhX2RhdGEuaXNfZXhwaXJlZCgpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBkYXRhKCkgOiBEYXRhIHsgcmV0dXJuIF9kYXRhOyB9XHJcblxyXG4gICAgZXhwb3J0IGludGVyZmFjZSBMb2dpblJlc3VsdCB7XHJcbiAgICAgICAgc3RhdHVzOiBcInN1Y2Nlc3NcIiB8IFwiY2FwdGNoYVwiIHwgXCJlcnJvclwiO1xyXG5cclxuICAgICAgICBlcnJvcl9tZXNzYWdlPzogc3RyaW5nO1xyXG4gICAgICAgIGNhcHRjaGE/OiB7XHJcbiAgICAgICAgICAgIHR5cGU6IFwiZ3JlLWNhcHRjaGFcIiB8IFwidW5rbm93blwiO1xyXG4gICAgICAgICAgICBkYXRhOiBhbnk7IC8qIGluIGNhc2Ugb2YgZ3JlLWNhcHRjaGEgaXQgd291bGQgYmUgdGhlIHNpZGUga2V5ICovXHJcbiAgICAgICAgfTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9naW4odXNlcm5hbWU6IHN0cmluZywgcGFzc3dvcmQ6IHN0cmluZywgY2FwdGNoYT86IGFueSkgOiBQcm9taXNlPExvZ2luUmVzdWx0PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlO1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAkLmFqYXgoe1xyXG4gICAgICAgICAgICAgICAgICAgIHVybDogYXBpX3VybCgpICsgXCI/d2ViLWFwaS92MS9sb2dpblwiLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGU6IFwiUE9TVFwiLFxyXG4gICAgICAgICAgICAgICAgICAgIGNhY2hlOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lOiB1c2VybmFtZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6IHBhc3N3b3JkLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW1lbWJlcjogdHJ1ZSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgXCJnLXJlY2FwdGNoYS1yZXNwb25zZVwiOiBjYXB0Y2hhXHJcbiAgICAgICAgICAgICAgICAgICAgfSxcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgY3Jvc3NEb21haW46IHRydWUsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHJlc29sdmUsXHJcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6ICh4aHIsIHN0YXR1cywgZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2codHIoXCJMb2dpbiByZXF1ZXN0IGZhaWxlZCAlbzogJW9cIiksIHN0YXR1cywgZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZWplY3QodHIoXCJyZXF1ZXN0IGZhaWxlZFwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiB0cihcImZhaWxlZCB0byBzZW5kIGxvZ2luIHJlcXVlc3RcIilcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKHJlc3BvbnNlW1wic3RhdHVzXCJdICE9PSBcIm9rXCIpIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIlJlc3BvbnNlIHN0YXR1cyBub3Qgb2tleS4gRXJyb3IgaGFwcGVuZDogJW9cIiksIHJlc3BvbnNlKTtcclxuICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgIHN0YXR1czogXCJlcnJvclwiLFxyXG4gICAgICAgICAgICAgICAgZXJyb3JfbWVzc2FnZTogKHJlc3BvbnNlW1wiZXJyb3JzXCJdIHx8IFtdKVswXSB8fCB0cihcIlVua25vd24gZXJyb3JcIilcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcInN1Y2Nlc3NcIl0pIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkxvZ2luIGZhaWxlZC4gUmVzcG9uc2UgJW9cIiksIHJlc3BvbnNlKTtcclxuXHJcbiAgICAgICAgICAgIGxldCBtZXNzYWdlID0gdHIoXCJmYWlsZWQgdG8gbG9naW5cIik7XHJcbiAgICAgICAgICAgIGxldCBjYXB0Y2hhO1xyXG4gICAgICAgICAgICAvKiB1c2VyL3Bhc3N3b3JkIHdyb25nIHwgYW5kIG1heWJlIGNhcHRjaGEgcmVxdWlyZWQgKi9cclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDEgfHwgcmVzcG9uc2VbXCJjb2RlXCJdID09IDMpXHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlID0gdHIoXCJJbnZhbGlkIHVzZXJuYW1lIG9yIHBhc3N3b3JkXCIpO1xyXG4gICAgICAgICAgICBpZihyZXNwb25zZVtcImNvZGVcIl0gPT0gMiB8fCByZXNwb25zZVtcImNvZGVcIl0gPT0gMykge1xyXG4gICAgICAgICAgICAgICAgY2FwdGNoYSA9IHtcclxuICAgICAgICAgICAgICAgICAgICB0eXBlOiByZXNwb25zZVtcImNhcHRjaGFcIl1bXCJ0eXBlXCJdLFxyXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHJlc3BvbnNlW1wiY2FwdGNoYVwiXVtcInNpdGVLZXlcIl0gLy9UT0RPOiBXaHkgc28gc3RhdGljIGhlcmU/XHJcbiAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDIpXHJcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IHRyKFwiY2FwdGNoYSByZXF1aXJlZFwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgIHN0YXR1czogdHlwZW9mKGNhcHRjaGEpICE9PSBcInVuZGVmaW5lZFwiID8gXCJjYXB0Y2hhXCIgOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiBtZXNzYWdlLFxyXG4gICAgICAgICAgICAgICAgY2FwdGNoYTogY2FwdGNoYVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgIH1cclxuICAgICAgICAvL2RvY3VtZW50LmNvb2tpZSA9IFwidXNlcl9kYXRhPVwiICsgcmVzcG9uc2VbXCJkYXRhXCJdICsgXCI7cGF0aD0vXCI7XHJcbiAgICAgICAgLy9kb2N1bWVudC5jb29raWUgPSBcInVzZXJfc2lnbj1cIiArIHJlc3BvbnNlW1wic2lnblwiXSArIFwiO3BhdGg9L1wiO1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKHJlc3BvbnNlW1wiYXV0aC1rZXlcIl0sIHJlc3BvbnNlW1wiZGF0YVwiXSwgcmVzcG9uc2VbXCJzaWduXCJdKTtcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1kYXRhXCIsIHJlc3BvbnNlW1wiZGF0YVwiXSk7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tc2lnblwiLCByZXNwb25zZVtcInNpZ25cIl0pO1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLWF1dGhcIiwgcmVzcG9uc2VbXCJhdXRoLWtleVwiXSk7XHJcbiAgICAgICAgICAgIHVwZGF0ZV9mb3J1bSgpO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgY29uc29sZS5lcnJvcih0cihcIkZhaWxlZCB0byBwYXJzZSBmb3J1bSBnaXZlbiBkYXRhOiAlb1wiKSwgZXJyb3IpO1xyXG4gICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzOiBcImVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICBlcnJvcl9tZXNzYWdlOiB0cihcIkZhaWxlZCB0byBwYXJzZSByZXNwb25zZSBkYXRhXCIpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIHN0YXR1czogXCJzdWNjZXNzXCJcclxuICAgICAgICB9O1xyXG4gICAgfVxyXG5cclxuICAgIGV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5ld19kYXRhKCkgOiBQcm9taXNlPFwic3VjY2Vzc1wiIHwgXCJsb2dpbi1yZXF1aXJlZFwiPiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlO1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgbmV3IFByb21pc2U8YW55PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAgICAgICAgICAgICAkLmFqYXgoe1xyXG4gICAgICAgICAgICAgICAgICAgIHVybDogYXBpX3VybCgpICsgXCI/d2ViLWFwaS92MS9yZW5ldy1kYXRhXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogXCJHRVRcIixcclxuICAgICAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGNyb3NzRG9tYWluOiB0cnVlLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiYXV0aC1rZXlcIjogX2RhdGEuYXV0aF9rZXlcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiByZXNvbHZlLFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBzdGF0dXMsIGVycm9yKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiUmVuZXcgcmVxdWVzdCBmYWlsZWQgJW86ICVvXCIpLCBzdGF0dXMsIGVycm9yKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KHRyKFwicmVxdWVzdCBmYWlsZWRcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgdGhyb3cgdHIoXCJmYWlsZWQgdG8gc2VuZCByZW5ldyByZXF1ZXN0XCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYocmVzcG9uc2VbXCJzdGF0dXNcIl0gIT09IFwib2tcIikge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiUmVzcG9uc2Ugc3RhdHVzIG5vdCBva2V5LiBFcnJvciBoYXBwZW5kOiAlb1wiKSwgcmVzcG9uc2UpO1xyXG4gICAgICAgICAgICB0aHJvdyAocmVzcG9uc2VbXCJlcnJvcnNcIl0gfHwgW10pWzBdIHx8IHRyKFwiVW5rbm93biBlcnJvclwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcInN1Y2Nlc3NcIl0pIHtcclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdID09IDEpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBcImxvZ2luLXJlcXVpcmVkXCI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgdGhyb3cgXCJpbnZhbGlkIGVycm9yIGNvZGUgKFwiICsgcmVzcG9uc2VbXCJjb2RlXCJdICsgXCIpXCI7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKCFyZXNwb25zZVtcImRhdGFcIl0gfHwgIXJlc3BvbnNlW1wic2lnblwiXSlcclxuICAgICAgICAgICAgdGhyb3cgdHIoXCJyZXNwb25zZSBtaXNzaW5nIGRhdGFcIik7XHJcblxyXG4gICAgICAgIGNvbnNvbGUuZGVidWcodHIoXCJSZW5ldyBzdWNjZWVkZWQuIFBhcnNpbmcgZGF0YS5cIikpO1xyXG5cclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKF9kYXRhLmF1dGhfa2V5LCByZXNwb25zZVtcImRhdGFcIl0sIHJlc3BvbnNlW1wic2lnblwiXSk7XHJcbiAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tZGF0YVwiLCByZXNwb25zZVtcImRhdGFcIl0pO1xyXG4gICAgICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIiwgcmVzcG9uc2VbXCJzaWduXCJdKTtcclxuICAgICAgICAgICAgdXBkYXRlX2ZvcnVtKCk7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiRmFpbGVkIHRvIHBhcnNlIGZvcnVtIGdpdmVuIGRhdGE6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIHRocm93IHRyKFwiZmFpbGVkIHRvIHBhcnNlIGRhdGFcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gXCJzdWNjZXNzXCI7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvZ291dCgpIDogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICAgICAgaWYoIWxvZ2dlZF9pbigpKVxyXG4gICAgICAgICAgICByZXR1cm47XHJcblxyXG4gICAgICAgIGxldCByZXNwb25zZTtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZSA9IGF3YWl0IG5ldyBQcm9taXNlPGFueT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgJC5hamF4KHtcclxuICAgICAgICAgICAgICAgICAgICB1cmw6IGFwaV91cmwoKSArIFwiP3dlYi1hcGkvdjEvbG9nb3V0XCIsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogXCJHRVRcIixcclxuICAgICAgICAgICAgICAgICAgICBjYWNoZTogZmFsc2UsXHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGNyb3NzRG9tYWluOiB0cnVlLFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBkYXRhOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiYXV0aC1rZXlcIjogX2RhdGEuYXV0aF9rZXlcclxuICAgICAgICAgICAgICAgICAgICB9LFxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzdWNjZXNzOiByZXNvbHZlLFxyXG4gICAgICAgICAgICAgICAgICAgIGVycm9yOiAoeGhyLCBzdGF0dXMsIGVycm9yKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiTG9nb3V0IHJlcXVlc3QgZmFpbGVkICVvOiAlb1wiKSwgc3RhdHVzLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdCh0cihcInJlcXVlc3QgZmFpbGVkXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KVxyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHRocm93IHRyKFwiZmFpbGVkIHRvIHNlbmQgbG9nb3V0IHJlcXVlc3RcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihyZXNwb25zZVtcInN0YXR1c1wiXSAhPT0gXCJva1wiKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJSZXNwb25zZSBzdGF0dXMgbm90IG9rZXkuIEVycm9yIGhhcHBlbmQ6ICVvXCIpLCByZXNwb25zZSk7XHJcbiAgICAgICAgICAgIHRocm93IChyZXNwb25zZVtcImVycm9yc1wiXSB8fCBbXSlbMF0gfHwgdHIoXCJVbmtub3duIGVycm9yXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYoIXJlc3BvbnNlW1wic3VjY2Vzc1wiXSkge1xyXG4gICAgICAgICAgICAvKiBjb2RlIDEgbWVhbnMgbm90IGxvZ2dlZCBpbiwgaXRzIGFuIHN1Y2Nlc3MgKi9cclxuICAgICAgICAgICAgaWYocmVzcG9uc2VbXCJjb2RlXCJdICE9IDEpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IFwiaW52YWxpZCBlcnJvciBjb2RlIChcIiArIHJlc3BvbnNlW1wiY29kZVwiXSArIFwiKVwiO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBfZGF0YSA9IHVuZGVmaW5lZDtcclxuICAgICAgICBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbShcInRlYXNwZWFrLWZvcnVtLWRhdGFcIik7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1zaWduXCIpO1xyXG4gICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFwidGVhc3BlYWstZm9ydW0tYXV0aFwiKTtcclxuICAgICAgICB1cGRhdGVfZm9ydW0oKTtcclxuICAgIH1cclxuXHJcbiAgICBsb2FkZXIucmVnaXN0ZXJfdGFzayhsb2FkZXIuU3RhZ2UuSkFWQVNDUklQVF9JTklUSUFMSVpJTkcsIHtcclxuICAgICAgICBuYW1lOiBcIlRlYUZvcm8gaW5pdGlhbGl6ZVwiLFxyXG4gICAgICAgIHByaW9yaXR5OiAxMCxcclxuICAgICAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCByYXdfZGF0YSA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKFwidGVhc3BlYWstZm9ydW0tZGF0YVwiKTtcclxuICAgICAgICAgICAgY29uc3QgcmF3X3NpZ24gPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGZvcnVtX2F1dGggPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShcInRlYXNwZWFrLWZvcnVtLWF1dGhcIik7XHJcbiAgICAgICAgICAgIGlmKCFyYXdfZGF0YSB8fCAhcmF3X3NpZ24gfHwgIWZvcnVtX2F1dGgpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKHRyKFwiTm8gVGVhRm9ybyBhdXRoZW50aWZpY2F0aW9uIGZvdW5kLiBUZWFGb3JvIGNvbm5lY3Rpb24gc3RhdHVzOiB1bmNvbm5lY3RlZFwiKSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBfZGF0YSA9IG5ldyBEYXRhKGZvcnVtX2F1dGgsIHJhd19kYXRhLCByYXdfc2lnbik7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodHIoXCJGYWlsZWQgdG8gaW5pdGlhbGl6ZSBUZWFGb3JvIGNvbm5lY3Rpb24gZnJvbSBsb2NhbCBkYXRhLiBFcnJvcjogJW9cIiksIGVycm9yKTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZihfZGF0YS5zaG91bGRfcmVuZXcoKSkge1xyXG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKHRyKFwiVGVhRm9ybyBkYXRhIHNob3VsZCBiZSByZW5ld2VkLiBFeGVjdXRpbmcgcmVuZXcuXCIpKTtcclxuICAgICAgICAgICAgICAgIHJlbmV3X2RhdGEoKS50aGVuKHN0YXR1cyA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoc3RhdHVzID09PSBcInN1Y2Nlc3NcIikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmluZm8odHIoXCJUZWFGb3JvIGRhdGEgaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHJlbmV3ZWQuXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4odHIoXCJGYWlsZWQgdG8gcmVuZXcgVGVhRm9ybyBkYXRhLiBOZXcgbG9naW4gcmVxdWlyZWQuXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oXCJ0ZWFzcGVhay1mb3J1bS1kYXRhXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBsb2NhbFN0b3JhZ2UucmVtb3ZlSXRlbShcInRlYXNwZWFrLWZvcnVtLXNpZ25cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5yZW1vdmVJdGVtKFwidGVhc3BlYWstZm9ydW0tYXV0aFwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS53YXJuKHRyKFwiRmFpbGVkIHRvIHJlbmV3IFRlYUZvcm8gZGF0YS4gQW4gZXJyb3Igb2NjdXJyZWQ6ICVvXCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYoX2RhdGEgJiYgX2RhdGEuaXNfZXhwaXJlZCgpKSB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKHRyKFwiVGVhRm9ybyBkYXRhIGlzIGV4cGlyZWQuIFRlYUZvcm8gY29ubmVjdGlvbiBpc24ndCBhdmFpbGFibGUhXCIpKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH0pXHJcbn0iLCIvLy8gPHJlZmVyZW5jZSBwYXRoPVwidWkvZWxlbWVudHMvbW9kYWwudHNcIiAvPlxyXG4vL1VzZWQgYnkgQ2VydEFjY2VwdCBwb3B1cFxyXG5cclxuaW1wb3J0IHtsb2csIExvZ0NhdGVnb3J5fSBmcm9tIFwiLi9sb2dcIjtcclxuaW1wb3J0IHtjcmVhdGVFcnJvck1vZGFsfSBmcm9tIFwiLi91aS9lbGVtZW50cy9tb2RhbFwiO1xyXG5cclxuaWYodHlwZW9mKGN1c3RvbUVsZW1lbnRzKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgdHJ5IHtcclxuICAgICAgICBjbGFzcyBYX1Byb3BlcnRpZXMgZXh0ZW5kcyBIVE1MRWxlbWVudCB7fVxyXG4gICAgICAgIGNsYXNzIFhfUHJvcGVydHkgZXh0ZW5kcyBIVE1MRWxlbWVudCB7fVxyXG5cclxuICAgICAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3gtcHJvcGVydGllcycsIFhfUHJvcGVydGllcywgeyBleHRlbmRzOiAnZGl2JyB9KTtcclxuICAgICAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3gtcHJvcGVydHknLCBYX1Byb3BlcnR5LCB7IGV4dGVuZHM6ICdkaXYnIH0pO1xyXG4gICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgIGNvbnNvbGUud2FybihcImZhaWxlZCB0byBkZWZpbmUgY29zdHVtIGVsZW1lbnRzXCIpO1xyXG4gICAgfVxyXG59XHJcblxyXG4vKiBUID0gdmFsdWUgdHlwZSAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIFNldHRpbmdzS2V5PFQ+IHtcclxuICAgIGtleTogc3RyaW5nO1xyXG5cclxuICAgIGZhbGxiYWNrX2tleXM/OiBzdHJpbmcgfCBzdHJpbmdbXTtcclxuICAgIGZhbGxiYWNrX2ltcG9ydHM/OiB7W2tleTogc3RyaW5nXToodmFsdWU6IHN0cmluZykgPT4gVH07XHJcbiAgICBkZXNjcmlwdGlvbj86IHN0cmluZztcclxuICAgIGRlZmF1bHRfdmFsdWU/OiBUO1xyXG5cclxuICAgIHJlcXVpcmVfcmVzdGFydD86IGJvb2xlYW47XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTZXR0aW5nc0Jhc2Uge1xyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyByZWFkb25seSBVUERBVEVfRElSRUNUOiBib29sZWFuID0gdHJ1ZTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgc3RhdGljIHRyYW5zZm9ybVN0Tz88VD4oaW5wdXQ/OiBzdHJpbmcsIF9kZWZhdWx0PzogVCwgZGVmYXVsdF90eXBlPzogc3RyaW5nKSA6IFQge1xyXG4gICAgICAgIGRlZmF1bHRfdHlwZSA9IGRlZmF1bHRfdHlwZSB8fCB0eXBlb2YgX2RlZmF1bHQ7XHJcblxyXG4gICAgICAgIGlmICAgICAgKHR5cGVvZiBpbnB1dCA9PT0gXCJ1bmRlZmluZWRcIikgcmV0dXJuIF9kZWZhdWx0O1xyXG4gICAgICAgIGlmICAgICAgKGRlZmF1bHRfdHlwZSA9PT0gXCJzdHJpbmdcIikgICAgIHJldHVybiBpbnB1dCBhcyBhbnk7XHJcbiAgICAgICAgZWxzZSBpZiAoZGVmYXVsdF90eXBlID09PSBcIm51bWJlclwiKSAgICAgcmV0dXJuIHBhcnNlSW50KGlucHV0KSBhcyBhbnk7XHJcbiAgICAgICAgZWxzZSBpZiAoZGVmYXVsdF90eXBlID09PSBcImJvb2xlYW5cIikgICAgcmV0dXJuIChpbnB1dCA9PSBcIjFcIiB8fCBpbnB1dCA9PSBcInRydWVcIikgYXMgYW55O1xyXG4gICAgICAgIGVsc2UgaWYgKGRlZmF1bHRfdHlwZSA9PT0gXCJ1bmRlZmluZWRcIikgICByZXR1cm4gaW5wdXQgYXMgYW55O1xyXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKGlucHV0KSBhcyBhbnk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyB0cmFuc2Zvcm1PdFM/PFQ+KGlucHV0OiBUKSA6IHN0cmluZyB7XHJcbiAgICAgICAgaWYgICAgICAodHlwZW9mIGlucHV0ID09PSBcInN0cmluZ1wiKSAgICAgcmV0dXJuIGlucHV0IGFzIHN0cmluZztcclxuICAgICAgICBlbHNlIGlmICh0eXBlb2YgaW5wdXQgPT09IFwibnVtYmVyXCIpICAgICByZXR1cm4gaW5wdXQudG9TdHJpbmcoKTtcclxuICAgICAgICBlbHNlIGlmICh0eXBlb2YgaW5wdXQgPT09IFwiYm9vbGVhblwiKSAgICByZXR1cm4gaW5wdXQgPyBcIjFcIiA6IFwiMFwiO1xyXG4gICAgICAgIGVsc2UgaWYgKHR5cGVvZiBpbnB1dCA9PT0gXCJ1bmRlZmluZWRcIikgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGlucHV0KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm90ZWN0ZWQgc3RhdGljIHJlc29sdmVLZXk8VD4oa2V5OiBTZXR0aW5nc0tleTxUPiwgX2RlZmF1bHQ6IFQsIHJlc29sdmVyOiAoa2V5OiBzdHJpbmcpID0+IHN0cmluZyB8IGJvb2xlYW4sIGRlZmF1bHRfdHlwZT86IHN0cmluZykgOiBUIHtcclxuICAgICAgICBsZXQgdmFsdWUgPSByZXNvbHZlcihrZXkua2V5KTtcclxuICAgICAgICBpZighdmFsdWUpIHtcclxuICAgICAgICAgICAgLyogdHJ5aW5nIGZhbGxiYWNrcyAqL1xyXG4gICAgICAgICAgICBmb3IoY29uc3QgZmFsbGJhY2sgb2Yga2V5LmZhbGxiYWNrX2tleXMgfHwgW10pIHtcclxuICAgICAgICAgICAgICAgIHZhbHVlID0gcmVzb2x2ZXIoZmFsbGJhY2spO1xyXG4gICAgICAgICAgICAgICAgaWYodHlwZW9mKHZhbHVlKSA9PT0gXCJzdHJpbmdcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIC8qIGZhbGxiYWNrIGtleSBzdWNjZWVkZWQgKi9cclxuICAgICAgICAgICAgICAgICAgICBjb25zdCBpbXBvcnRlciA9IChrZXkuZmFsbGJhY2tfaW1wb3J0cyB8fCB7fSlbZmFsbGJhY2tdO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKGltcG9ydGVyKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaW1wb3J0ZXIodmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmKHR5cGVvZih2YWx1ZSkgIT09ICdzdHJpbmcnKVxyXG4gICAgICAgICAgICByZXR1cm4gX2RlZmF1bHQ7XHJcblxyXG4gICAgICAgIHJldHVybiBTZXR0aW5nc0Jhc2UudHJhbnNmb3JtU3RPKHZhbHVlIGFzIHN0cmluZywgX2RlZmF1bHQsIGRlZmF1bHRfdHlwZSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvdGVjdGVkIHN0YXRpYyBrZXlpZnk8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPikgOiBTZXR0aW5nc0tleTxUPiB7XHJcbiAgICAgICAgaWYodHlwZW9mKGtleSkgPT09IFwic3RyaW5nXCIpXHJcbiAgICAgICAgICAgIHJldHVybiB7a2V5OiBrZXl9O1xyXG4gICAgICAgIGlmKHR5cGVvZihrZXkpID09PSBcIm9iamVjdFwiICYmIGtleS5rZXkpXHJcbiAgICAgICAgICAgIHJldHVybiBrZXk7XHJcbiAgICAgICAgdGhyb3cgXCJrZXkgaXMgbm90IGEga2V5XCI7XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTdGF0aWNTZXR0aW5ncyBleHRlbmRzIFNldHRpbmdzQmFzZSB7XHJcbiAgICBwcml2YXRlIHN0YXRpYyBfaW5zdGFuY2U6IFN0YXRpY1NldHRpbmdzO1xyXG4gICAgc3RhdGljIGdldCBpbnN0YW5jZSgpIDogU3RhdGljU2V0dGluZ3Mge1xyXG4gICAgICAgIGlmKCF0aGlzLl9pbnN0YW5jZSlcclxuICAgICAgICAgICAgdGhpcy5faW5zdGFuY2UgPSBuZXcgU3RhdGljU2V0dGluZ3ModHJ1ZSk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2luc3RhbmNlO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBfaGFuZGxlOiBTdGF0aWNTZXR0aW5ncztcclxuICAgIHByb3RlY3RlZCBfc3RhdGljUHJvcHNUYWc6IEpRdWVyeTtcclxuXHJcbiAgICBwcm90ZWN0ZWQgY29uc3RydWN0b3IoX3Jlc2VydmVkID0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgc3VwZXIoKTtcclxuICAgICAgICBpZihfcmVzZXJ2ZWQgJiYgIVN0YXRpY1NldHRpbmdzLl9pbnN0YW5jZSkge1xyXG4gICAgICAgICAgICB0aGlzLl9zdGF0aWNQcm9wc1RhZyA9ICQoXCIjcHJvcGVydGllc1wiKTtcclxuICAgICAgICAgICAgdGhpcy5pbml0aWFsaXplU3RhdGljKCk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5faGFuZGxlID0gU3RhdGljU2V0dGluZ3MuaW5zdGFuY2U7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHByaXZhdGUgaW5pdGlhbGl6ZVN0YXRpYygpIHtcclxuICAgICAgICBsZXQgc2VhcmNoO1xyXG4gICAgICAgIGlmKHdpbmRvdy5vcGVuZXIgJiYgd2luZG93Lm9wZW5lciAhPT0gd2luZG93KSB7XHJcbiAgICAgICAgICAgIHNlYXJjaCA9IG5ldyBVUkwod2luZG93LmxvY2F0aW9uLmhyZWYpLnNlYXJjaDtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBzZWFyY2ggPSBsb2NhdGlvbi5zZWFyY2g7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzZWFyY2guc3Vic3RyKDEpLnNwbGl0KFwiJlwiKS5mb3JFYWNoKHBhcnQgPT4ge1xyXG4gICAgICAgICAgICBsZXQgaXRlbSA9IHBhcnQuc3BsaXQoXCI9XCIpO1xyXG4gICAgICAgICAgICAkKFwiPHgtcHJvcGVydHk+PC94LXByb3BlcnR5PlwiKVxyXG4gICAgICAgICAgICAgICAgLmF0dHIoXCJrZXlcIiwgaXRlbVswXSlcclxuICAgICAgICAgICAgICAgIC5hdHRyKFwidmFsdWVcIiwgaXRlbVsxXSlcclxuICAgICAgICAgICAgICAgIC5hcHBlbmRUbyh0aGlzLl9zdGF0aWNQcm9wc1RhZyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljPzxUPihrZXk6IHN0cmluZyB8IFNldHRpbmdzS2V5PFQ+LCBfZGVmYXVsdD86IFQsIGRlZmF1bHRfdHlwZT86IHN0cmluZykgOiBUIHtcclxuICAgICAgICBpZih0aGlzLl9oYW5kbGUpIHJldHVybiB0aGlzLl9oYW5kbGUuc3RhdGljPFQ+KGtleSwgX2RlZmF1bHQsIGRlZmF1bHRfdHlwZSk7XHJcblxyXG4gICAgICAgIGtleSA9IFN0YXRpY1NldHRpbmdzLmtleWlmeShrZXkpO1xyXG4gICAgICAgIHJldHVybiBTdGF0aWNTZXR0aW5ncy5yZXNvbHZlS2V5KGtleSwgX2RlZmF1bHQsIGtleSA9PiB7XHJcbiAgICAgICAgICAgIGxldCByZXN1bHQgPSB0aGlzLl9zdGF0aWNQcm9wc1RhZy5maW5kKFwiW2tleT0nXCIgKyBrZXkgKyBcIiddXCIpO1xyXG4gICAgICAgICAgICBpZihyZXN1bHQubGVuZ3RoID4gMClcclxuICAgICAgICAgICAgICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQocmVzdWx0Lmxhc3QoKS5hdHRyKCd2YWx1ZScpKTtcclxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH0sIGRlZmF1bHRfdHlwZSk7XHJcbiAgICB9XHJcblxyXG4gICAgZGVsZXRlU3RhdGljPFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4pIHtcclxuICAgICAgICBpZih0aGlzLl9oYW5kbGUpIHtcclxuICAgICAgICAgICAgdGhpcy5faGFuZGxlLmRlbGV0ZVN0YXRpYzxUPihrZXkpO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBrZXkgPSBTdGF0aWNTZXR0aW5ncy5rZXlpZnkoa2V5KTtcclxuICAgICAgICBsZXQgcmVzdWx0ID0gdGhpcy5fc3RhdGljUHJvcHNUYWcuZmluZChcIltrZXk9J1wiICsga2V5LmtleSArIFwiJ11cIik7XHJcbiAgICAgICAgaWYocmVzdWx0Lmxlbmd0aCAhPSAwKSByZXN1bHQuZGV0YWNoKCk7XHJcbiAgICB9XHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBTZXR0aW5ncyBleHRlbmRzIFN0YXRpY1NldHRpbmdzIHtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfVVNFUl9JU19ORVc6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ3VzZXJfaXNfbmV3X3VzZXInLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWVcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX0NPU01FVElDX1NMT1dET1dOOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlX2Nvc21ldGljX3Nsb3dkb3duJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0Rpc2FibGUgdGhlIGNvc21ldGljIHNsb3dkb3dzIGluIHNvbWUgcHJvY2Vzc2VzLCBsaWtlIGljb24gdXBsb2FkLidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX0NPTlRFWFRfTUVOVTogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnZGlzYWJsZUNvbnRleHRNZW51JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0Rpc2FibGUgdGhlIGNvbnRleHQgbWVudSBmb3IgdGhlIGNoYW5uZWwgdHJlZSB3aGljaCBhbGxvd3MgdG8gZGVidWcgdGhlIERPTSBlYXNpZXInXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfRElTQUJMRV9HTE9CQUxfQ09OVEVYVF9NRU5VOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlR2xvYmFsQ29udGV4dE1lbnUnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZSB0aGUgZ2VuZXJhbCBjb250ZXh0IG1lbnUgcHJldmVudGlvbicsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2VcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX1VOTE9BRF9ESUFMT0c6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2Rpc2FibGVVbmxvYWREaWFsb2cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZXMgdGhlIHVubG9hZCBwb3B1cCBvbiBzaWRlIGNsb3NpbmcnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9ESVNBQkxFX1ZPSUNFOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlVm9pY2UnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRGlzYWJsZXMgdGhlIHZvaWNlIGJyaWRnZS4gSWYgZGlzYWJsZWQsIHRoZSBhdWRpbyBhbmQgY29kZWMgd29ya2VycyBhcmVuXFwndCByZXF1aXJlZCBhbnltb3JlJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfRElTQUJMRV9NVUxUSV9TRVNTSU9OOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkaXNhYmxlTXVsdGlTZXNzaW9uJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZSxcclxuICAgICAgICByZXF1aXJlX3Jlc3RhcnQ6IHRydWVcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9MT0FEX0RVTU1ZX0VSUk9SOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdkdW1teV9sb2FkX2Vycm9yJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RyaWdnZXJzIGEgbG9hZGluZyBlcnJvciBhdCB0aGUgZW5kIG9mIHRoZSBsb2FkaW5nIHByb2Nlc3MuJ1xyXG4gICAgfTtcclxuXHJcbiAgICAvKiBDb250cm9sIGJhciAqL1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05UUk9MX01VVEVfSU5QVVQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ211dGVfaW5wdXQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05UUk9MX01VVEVfT1VUUFVUOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdtdXRlX291dHB1dCdcclxuICAgIH07XHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NPTlRST0xfU0hPV19RVUVSSUVTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdzaG93X3NlcnZlcl9xdWVyaWVzJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OVFJPTF9DSEFOTkVMX1NVQlNDUklCRV9BTEw6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2NoYW5uZWxfc3Vic2NyaWJlX2FsbCdcclxuICAgIH07XHJcblxyXG4gICAgLyogQ29ubmVjdCBwYXJhbWV0ZXJzICovXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0ZMQUdfQ09OTkVDVF9ERUZBVUxUOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjb25uZWN0X2RlZmF1bHQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX0FERFJFU1M6IFNldHRpbmdzS2V5PHN0cmluZz4gPSB7XHJcbiAgICAgICAga2V5OiAnY29ubmVjdF9hZGRyZXNzJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OTkVDVF9QUk9GSUxFOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcHJvZmlsZScsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogJ2RlZmF1bHQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX1VTRVJOQU1FOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfdXNlcm5hbWUnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX1BBU1NXT1JEOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcGFzc3dvcmQnXHJcbiAgICB9O1xyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9GTEFHX0NPTk5FQ1RfUEFTU1dPUkQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfcGFzc3dvcmRfaGFzaGVkJ1xyXG4gICAgfTtcclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ09OTkVDVF9ISVNUT1JZOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2Nvbm5lY3RfaGlzdG9yeSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DT05ORUNUX05PX0ROU1BST1hZOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjb25uZWN0X25vX2Ruc3Byb3h5JyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZVxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NFUlRJRklDQVRFX0NBTExCQUNLOiBTZXR0aW5nc0tleTxzdHJpbmc+ID0ge1xyXG4gICAgICAgIGtleTogJ2NlcnRpZmljYXRlX2NhbGxiYWNrJ1xyXG4gICAgfTtcclxuXHJcbiAgICAvKiBzb3VuZHMgKi9cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU09VTkRfTUFTVEVSOiBTZXR0aW5nc0tleTxudW1iZXI+ID0ge1xyXG4gICAgICAgIGtleTogJ2F1ZGlvX21hc3Rlcl92b2x1bWUnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IDEwMFxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX1NPVU5EX01BU1RFUl9TT1VORFM6IFNldHRpbmdzS2V5PG51bWJlcj4gPSB7XHJcbiAgICAgICAga2V5OiAnYXVkaW9fbWFzdGVyX3ZvbHVtZV9zb3VuZHMnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IDEwMFxyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NIQVRfRklYRURfVElNRVNUQU1QUzogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9maXhlZF90aW1lc3RhbXBzJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBmYWxzZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgZml4ZWQgdGltZXN0YW1wcyBmb3IgY2hhdCBtZXNzYWdlcyBhbmQgZGlzYWJsZWQgdGhlIHVwZGF0aW5nIG9uY2UgKDIgc2Vjb25kcyBhZ28uLi4gZXRjKSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0NPTExPUVVJQUxfVElNRVNUQU1QUzogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9jb2xsb3F1aWFsX3RpbWVzdGFtcHMnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVkIGNvbGxvcXVpYWwgdGltZXN0YW1wIGZvcm1hdHRpbmcgbGlrZSBcIlllc3RlcmRheSBhdCAuLi5cIiBvciBcIlRvZGF5IGF0IC4uLlwiJ1xyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX0NIQVRfQ09MT1JFRF9FTU9KSUVTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2NvbG9yZWRfZW1vamllcycsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogdHJ1ZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgY29sb3JlZCBlbW9qaWVzIHBvd2VyZWQgYnkgVHdlbW9qaSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX1RBR19VUkxTOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X3RhZ191cmxzJyxcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiB0cnVlLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQXV0b21hdGljYWxseSBsaW5rIHVybHMgd2l0aCBbdXJsXSdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0VOQUJMRV9NQVJLRE9XTjogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnY2hhdF9lbmFibGVfbWFya2Rvd24nLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVkIG1hcmtkb3duIGNoYXQgc3VwcG9ydC4nXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfQ0hBVF9FTkFCTEVfQkJDT0RFOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2VuYWJsZV9iYmNvZGUnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlZCBiYmNvZGUgc3VwcG9ydCBpbiBjaGF0LidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFUX0lNQUdFX1dISVRFTElTVF9SRUdFWDogU2V0dGluZ3NLZXk8c3RyaW5nPiA9IHtcclxuICAgICAgICBrZXk6ICdjaGF0X2ltYWdlX3doaXRlbGlzdF9yZWdleCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogSlNPTi5zdHJpbmdpZnkoW10pXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU1dJVENIX0lOU1RBTlRfQ0hBVDogU2V0dGluZ3NLZXk8Ym9vbGVhbj4gPSB7XHJcbiAgICAgICAga2V5OiAnc3dpdGNoX2luc3RhbnRfY2hhdCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogdHJ1ZSxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdGx5IHN3aXRjaCB0byBjaGFubmVsIGNoYXQgb24gY2hhbm5lbCBzZWxlY3QnXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfU1dJVENIX0lOU1RBTlRfQ0xJRU5UOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdzd2l0Y2hfaW5zdGFudF9jbGllbnQnLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IHRydWUsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RseSBzd2l0Y2ggdG8gY2xpZW50IGluZm8gb24gY2xpZW50IHNlbGVjdCdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9IT1NUQkFOTkVSX0JBQ0tHUk9VTkQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2hvc3RiYW5uZXJfYmFja2dyb3VuZCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIGEgZGVmYXVsdCBiYWNrZ3JvdW5kIGJlZ2luZCB0aGUgaG9zdGJhbm5lcidcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9DSEFOTkVMX0VESVRfQURWQU5DRUQ6IFNldHRpbmdzS2V5PGJvb2xlYW4+ID0ge1xyXG4gICAgICAgIGtleTogJ2NoYW5uZWxfZWRpdF9hZHZhbmNlZCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFZGl0IGNoYW5uZWxzIGluIGFkdmFuY2VkIG1vZGUgd2l0aCBhIGxvdCBtb3JlIHNldHRpbmdzJ1xyXG4gICAgfTtcclxuXHJcbiAgICBzdGF0aWMgcmVhZG9ubHkgS0VZX1BFUk1JU1NJT05TX1NIT1dfQUxMOiBTZXR0aW5nc0tleTxib29sZWFuPiA9IHtcclxuICAgICAgICBrZXk6ICdwZXJtaXNzaW9uc19zaG93X2FsbCcsXHJcbiAgICAgICAgZGVmYXVsdF92YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTaG93IGFsbCBwZXJtaXNzaW9ucyBldmVuIHRob3UgdGhleSBkb250IG1ha2Ugc2Vuc2UgZm9yIHRoZSBzZXJ2ZXIvY2hhbm5lbCBncm91cCdcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9URUFGT1JPX1VSTDogU2V0dGluZ3NLZXk8c3RyaW5nPiA9IHtcclxuICAgICAgICBrZXk6IFwidGVhZm9yb191cmxcIixcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiBcImh0dHBzOi8vZm9ydW0udGVhc3BlYWsuZGUvXCJcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9GT05UX1NJWkU6IFNldHRpbmdzS2V5PG51bWJlcj4gPSB7XHJcbiAgICAgICAga2V5OiBcImZvbnRfc2l6ZVwiXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBLRVlfSUNPTl9TSVpFOiBTZXR0aW5nc0tleTxudW1iZXI+ID0ge1xyXG4gICAgICAgIGtleTogXCJpY29uX3NpemVcIixcclxuICAgICAgICBkZWZhdWx0X3ZhbHVlOiAxMDBcclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWV9MQVNUX0lOVklURV9MSU5LX1RZUEU6IFNldHRpbmdzS2V5PHN0cmluZz4gPSB7XHJcbiAgICAgICAga2V5OiBcImxhc3RfaW52aXRlX2xpbmtfdHlwZVwiLFxyXG4gICAgICAgIGRlZmF1bHRfdmFsdWU6IFwidGVhLXdlYlwiXHJcbiAgICB9O1xyXG5cclxuICAgIHN0YXRpYyByZWFkb25seSBGTl9JTlZJVEVfTElOS19TRVRUSU5HOiAobmFtZTogc3RyaW5nKSA9PiBTZXR0aW5nc0tleTxzdHJpbmc+ID0gbmFtZSA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAnaW52aXRlX2xpbmtfc2V0dGluZ18nICsgbmFtZVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEZOX1NFUlZFUl9DSEFOTkVMX1NVQlNDUklCRV9NT0RFOiAoY2hhbm5lbF9pZDogbnVtYmVyKSA9PiBTZXR0aW5nc0tleTxudW1iZXI+ID0gY2hhbm5lbCA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAnY2hhbm5lbF9zdWJzY3JpYmVfbW9kZV8nICsgY2hhbm5lbFxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEZOX1BST0ZJTEVfUkVDT1JEOiAobmFtZTogc3RyaW5nKSA9PiBTZXR0aW5nc0tleTxhbnk+ID0gbmFtZSA9PiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAga2V5OiAncHJvZmlsZV9yZWNvcmQnICsgbmFtZVxyXG4gICAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgc3RhdGljIHJlYWRvbmx5IEtFWVMgPSAoKCkgPT4ge1xyXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IFtdO1xyXG5cclxuICAgICAgICBmb3IoY29uc3Qga2V5IGluIFNldHRpbmdzKSB7XHJcbiAgICAgICAgICAgIGlmKCFrZXkudG9VcHBlckNhc2UoKS5zdGFydHNXaXRoKFwiS0VZX1wiKSlcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICBpZihrZXkudG9VcHBlckNhc2UoKSA9PSBcIktFWVNcIilcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgcmVzdWx0LnB1c2goa2V5KTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9KSgpO1xyXG5cclxuICAgIHN0YXRpYyBpbml0aWFsaXplKCkge1xyXG4gICAgICAgIHNldHRpbmdzID0gbmV3IFNldHRpbmdzKCk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBjYWNoZUdsb2JhbCA9IHt9O1xyXG4gICAgcHJpdmF0ZSBzYXZlV29ya2VyOiBOb2RlSlMuVGltZXI7XHJcbiAgICBwcml2YXRlIHVwZGF0ZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcigpIHtcclxuICAgICAgICBzdXBlcigpO1xyXG5cclxuICAgICAgICBjb25zdCBqc29uID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJzZXR0aW5ncy5nbG9iYWxcIik7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgdGhpcy5jYWNoZUdsb2JhbCA9IEpTT04ucGFyc2UoanNvbik7XHJcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBnbG9iYWwgc2V0dGluZ3MhXFxuSnNvbjogJXNcXG5FcnJvcjogJW9cIiksIGpzb24sIGVycm9yKTtcclxuXHJcbiAgICAgICAgICAgIGNvbnN0IHNob3dfcG9wdXAgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBjcmVhdGVFcnJvck1vZGFsKHRyKFwiRmFpbGVkIHRvIGxvYWQgZ2xvYmFsIHNldHRpbmdzXCIpLCB0cihcIkZhaWxlZCB0byBsb2FkIGdsb2JhbCBjbGllbnQgc2V0dGluZ3MhXFxuTG9va3VwIGNvbnNvbGUgZm9yIG1vcmUgaW5mb3JtYXRpb24uXCIpKS5vcGVuKCk7XHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICAgIGlmKCFsb2FkZXIuZmluaXNoZWQoKSlcclxuICAgICAgICAgICAgICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5MT0FERUQsIHtcclxuICAgICAgICAgICAgICAgICAgICBwcmlvcml0eTogMCxcclxuICAgICAgICAgICAgICAgICAgICBuYW1lOiBcIlNldHRpbmdzIGVycm9yXCIsXHJcbiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHNob3dfcG9wdXAoKVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgIHNob3dfcG9wdXAoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYoIXRoaXMuY2FjaGVHbG9iYWwpIHRoaXMuY2FjaGVHbG9iYWwgPSB7fTtcclxuICAgICAgICB0aGlzLnNhdmVXb3JrZXIgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICAgICAgICAgIGlmKHRoaXMudXBkYXRlZClcclxuICAgICAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgIH0sIDUgKiAxMDAwKTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWNfZ2xvYmFsPzxUPihrZXk6IHN0cmluZyB8IFNldHRpbmdzS2V5PFQ+LCBfZGVmYXVsdD86IFQpIDogVCB7XHJcbiAgICAgICAgY29uc3QgYWN0dWFsX2RlZmF1bHQgPSB0eXBlb2YoX2RlZmF1bHQpID09PSBcInVuZGVmaW5lZFwiICYmIHR5cGVvZihrZXkpID09PSBcIm9iamVjdFwiICYmICdkZWZhdWx0X3ZhbHVlJyBpbiBrZXkgPyBrZXkuZGVmYXVsdF92YWx1ZSA6IF9kZWZhdWx0O1xyXG5cclxuICAgICAgICBjb25zdCBkZWZhdWx0X29iamVjdCA9IHsgc2VlZDogTWF0aC5yYW5kb20oKSB9IGFzIGFueTtcclxuICAgICAgICBsZXQgX3N0YXRpYyA9IHRoaXMuc3RhdGljKGtleSwgZGVmYXVsdF9vYmplY3QsIHR5cGVvZiBfZGVmYXVsdCk7XHJcbiAgICAgICAgaWYoX3N0YXRpYyAhPT0gZGVmYXVsdF9vYmplY3QpIHJldHVybiBTdGF0aWNTZXR0aW5ncy50cmFuc2Zvcm1TdE8oX3N0YXRpYywgYWN0dWFsX2RlZmF1bHQpO1xyXG4gICAgICAgIHJldHVybiB0aGlzLmdsb2JhbDxUPihrZXksIGFjdHVhbF9kZWZhdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICBnbG9iYWw/PFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4sIF9kZWZhdWx0PzogVCkgOiBUIHtcclxuICAgICAgICBjb25zdCBhY3R1YWxfZGVmYXVsdCA9IHR5cGVvZihfZGVmYXVsdCkgPT09IFwidW5kZWZpbmVkXCIgJiYgdHlwZW9mKGtleSkgPT09IFwib2JqZWN0XCIgJiYgJ2RlZmF1bHRfdmFsdWUnIGluIGtleSA/IGtleS5kZWZhdWx0X3ZhbHVlIDogX2RlZmF1bHQ7XHJcbiAgICAgICAgcmV0dXJuIFN0YXRpY1NldHRpbmdzLnJlc29sdmVLZXkoU2V0dGluZ3Mua2V5aWZ5KGtleSksIGFjdHVhbF9kZWZhdWx0LCBrZXkgPT4gdGhpcy5jYWNoZUdsb2JhbFtrZXldKTtcclxuICAgIH1cclxuXHJcbiAgICBjaGFuZ2VHbG9iYWw8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPiwgdmFsdWU/OiBUKXtcclxuICAgICAgICBrZXkgPSBTZXR0aW5ncy5rZXlpZnkoa2V5KTtcclxuXHJcblxyXG4gICAgICAgIGlmKHRoaXMuY2FjaGVHbG9iYWxba2V5LmtleV0gPT0gdmFsdWUpIHJldHVybjtcclxuXHJcbiAgICAgICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcclxuICAgICAgICB0aGlzLmNhY2hlR2xvYmFsW2tleS5rZXldID0gU3RhdGljU2V0dGluZ3MudHJhbnNmb3JtT3RTKHZhbHVlKTtcclxuXHJcbiAgICAgICAgaWYoU2V0dGluZ3MuVVBEQVRFX0RJUkVDVClcclxuICAgICAgICAgICAgdGhpcy5zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2F2ZSgpIHtcclxuICAgICAgICB0aGlzLnVwZGF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsZXQgZ2xvYmFsID0gSlNPTi5zdHJpbmdpZnkodGhpcy5jYWNoZUdsb2JhbCk7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJzZXR0aW5ncy5nbG9iYWxcIiwgZ2xvYmFsKTtcclxuICAgICAgICBpZihsb2NhbFN0b3JhZ2Uuc2F2ZSlcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNhdmUoKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIFNlcnZlclNldHRpbmdzIGV4dGVuZHMgU2V0dGluZ3NCYXNlIHtcclxuICAgIHByaXZhdGUgY2FjaGVTZXJ2ZXIgPSB7fTtcclxuICAgIHByaXZhdGUgX3NlcnZlcl91bmlxdWVfaWQ6IHN0cmluZztcclxuICAgIHByaXZhdGUgX3NlcnZlcl9zYXZlX3dvcmtlcjogTm9kZUpTLlRpbWVyO1xyXG4gICAgcHJpdmF0ZSBfc2VydmVyX3NldHRpbmdzX3VwZGF0ZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuICAgIHByaXZhdGUgX2Rlc3Ryb3llZCA9IGZhbHNlO1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIHN1cGVyKCk7XHJcbiAgICAgICAgdGhpcy5fc2VydmVyX3NhdmVfd29ya2VyID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfc2V0dGluZ3NfdXBkYXRlZClcclxuICAgICAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgIH0sIDUgKiAxMDAwKTtcclxuICAgIH1cclxuXHJcbiAgICBkZXN0cm95KCkge1xyXG4gICAgICAgIHRoaXMuX2Rlc3Ryb3llZCA9IHRydWU7XHJcblxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgdGhpcy5jYWNoZVNlcnZlciA9IHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLl9zZXJ2ZXJfc2F2ZV93b3JrZXIpO1xyXG4gICAgICAgIHRoaXMuX3NlcnZlcl9zYXZlX3dvcmtlciA9IHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBzZXJ2ZXI/PFQ+KGtleTogc3RyaW5nIHwgU2V0dGluZ3NLZXk8VD4sIF9kZWZhdWx0PzogVCkgOiBUIHtcclxuICAgICAgICBpZih0aGlzLl9kZXN0cm95ZWQpIHRocm93IFwiZGVzdHJveWVkXCI7XHJcbiAgICAgICAgcmV0dXJuIFN0YXRpY1NldHRpbmdzLnJlc29sdmVLZXkoU2V0dGluZ3Mua2V5aWZ5KGtleSksIF9kZWZhdWx0LCBrZXkgPT4gdGhpcy5jYWNoZVNlcnZlcltrZXldKTtcclxuICAgIH1cclxuXHJcbiAgICBjaGFuZ2VTZXJ2ZXI8VD4oa2V5OiBzdHJpbmcgfCBTZXR0aW5nc0tleTxUPiwgdmFsdWU/OiBUKSB7XHJcbiAgICAgICAgaWYodGhpcy5fZGVzdHJveWVkKSB0aHJvdyBcImRlc3Ryb3llZFwiO1xyXG4gICAgICAgIGtleSA9IFNldHRpbmdzLmtleWlmeShrZXkpO1xyXG5cclxuICAgICAgICBpZih0aGlzLmNhY2hlU2VydmVyW2tleS5rZXldID09IHZhbHVlKSByZXR1cm47XHJcblxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl9zZXR0aW5nc191cGRhdGVkID0gdHJ1ZTtcclxuICAgICAgICB0aGlzLmNhY2hlU2VydmVyW2tleS5rZXldID0gU3RhdGljU2V0dGluZ3MudHJhbnNmb3JtT3RTKHZhbHVlKTtcclxuXHJcbiAgICAgICAgaWYoU2V0dGluZ3MuVVBEQVRFX0RJUkVDVClcclxuICAgICAgICAgICAgdGhpcy5zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0U2VydmVyKHNlcnZlcl91bmlxdWVfaWQ6IHN0cmluZykge1xyXG4gICAgICAgIGlmKHRoaXMuX2Rlc3Ryb3llZCkgdGhyb3cgXCJkZXN0cm95ZWRcIjtcclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgIHRoaXMuc2F2ZSgpO1xyXG4gICAgICAgICAgICB0aGlzLmNhY2hlU2VydmVyID0ge307XHJcbiAgICAgICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuX3NlcnZlcl91bmlxdWVfaWQgPSBzZXJ2ZXJfdW5pcXVlX2lkO1xyXG5cclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcblxyXG4gICAgICAgICAgICBjb25zdCBqc29uID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJzZXR0aW5ncy5zZXJ2ZXJfXCIgKyBzZXJ2ZXJfdW5pcXVlX2lkKTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuY2FjaGVTZXJ2ZXIgPSBKU09OLnBhcnNlKGpzb24pO1xyXG4gICAgICAgICAgICB9IGNhdGNoKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICBsb2cuZXJyb3IoTG9nQ2F0ZWdvcnkuR0VORVJBTCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBzZXJ2ZXIgc2V0dGluZ3MgZm9yIHNlcnZlciAlcyFcXG5Kc29uOiAlc1xcbkVycm9yOiAlb1wiKSwgc2VydmVyX3VuaXF1ZV9pZCwganNvbiwgZXJyb3IpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmKCF0aGlzLmNhY2hlU2VydmVyKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5jYWNoZVNlcnZlciA9IHt9O1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzYXZlKCkge1xyXG4gICAgICAgIGlmKHRoaXMuX2Rlc3Ryb3llZCkgdGhyb3cgXCJkZXN0cm95ZWRcIjtcclxuICAgICAgICB0aGlzLl9zZXJ2ZXJfc2V0dGluZ3NfdXBkYXRlZCA9IGZhbHNlO1xyXG5cclxuICAgICAgICBpZih0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkKSB7XHJcbiAgICAgICAgICAgIGxldCBzZXJ2ZXIgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmNhY2hlU2VydmVyKTtcclxuICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJzZXR0aW5ncy5zZXJ2ZXJfXCIgKyB0aGlzLl9zZXJ2ZXJfdW5pcXVlX2lkLCBzZXJ2ZXIpO1xyXG4gICAgICAgICAgICBpZihsb2NhbFN0b3JhZ2Uuc2F2ZSlcclxuICAgICAgICAgICAgICAgIGxvY2FsU3RvcmFnZS5zYXZlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgbGV0IHNldHRpbmdzOiBTZXR0aW5nczsiLCJpbXBvcnQge0tleUNvZGV9IGZyb20gXCIuLi8uLi9QUFRMaXN0ZW5lclwiO1xyXG5cclxuZXhwb3J0IGVudW0gRWxlbWVudFR5cGUge1xyXG4gICAgSEVBREVSLFxyXG4gICAgQk9EWSxcclxuICAgIEZPT1RFUlxyXG59XHJcblxyXG5leHBvcnQgdHlwZSBCb2R5Q3JlYXRvciA9ICgoKSA9PiBKUXVlcnkgfCBKUXVlcnlbXSB8IHN0cmluZykgfCBzdHJpbmcgfCBKUXVlcnkgfCBKUXVlcnlbXTtcclxuZXhwb3J0IGNvbnN0IE1vZGFsRnVuY3Rpb25zID0ge1xyXG4gICAgZGl2aWZ5OiBmdW5jdGlvbiAodmFsOiBKUXVlcnkpIHtcclxuICAgICAgICBpZih2YWwubGVuZ3RoID4gMSlcclxuICAgICAgICAgICAgcmV0dXJuICQuc3Bhd24oXCJkaXZcIikuYXBwZW5kKHZhbCk7XHJcbiAgICAgICAgcmV0dXJuIHZhbDtcclxuICAgIH0sXHJcblxyXG4gICAganF1ZXJpZWZ5OiBmdW5jdGlvbih2YWw6IEJvZHlDcmVhdG9yLCB0eXBlPzogRWxlbWVudFR5cGUpIDogSlF1ZXJ5W10gfCBKUXVlcnkgfCB1bmRlZmluZWQge1xyXG4gICAgICAgIGlmKHR5cGVvZih2YWwpID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgICAgIHZhbCA9IHZhbCgpO1xyXG5cclxuICAgICAgICBpZih2YWwgaW5zdGFuY2VvZiBqUXVlcnkpXHJcbiAgICAgICAgICAgIHJldHVybiB2YWwgYXMgSlF1ZXJ5O1xyXG5cclxuICAgICAgICBpZihBcnJheS5pc0FycmF5KHZhbCkpIHtcclxuICAgICAgICAgICAgaWYodmFsLmxlbmd0aCA9PSAwKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuXHJcbiAgICAgICAgICAgIHJldHVybiB2YWwubWFwKGUgPT4gdGhpcy5qcXVlcmllZnkoZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgc3dpdGNoICh0eXBlb2YgdmFsKXtcclxuICAgICAgICAgICAgY2FzZSBcInN0cmluZ1wiOlxyXG4gICAgICAgICAgICAgICAgaWYodHlwZSA9PSBFbGVtZW50VHlwZS5IRUFERVIpXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJtb2RhbC10aXRsZVwiKS50ZXh0KHZhbCk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gJChcIjxkaXY+XCIgKyB2YWwgKyBcIjwvZGl2PlwiKTtcclxuICAgICAgICAgICAgY2FzZSBcIm9iamVjdFwiOiByZXR1cm4gdmFsIGFzIEpRdWVyeTtcclxuICAgICAgICAgICAgY2FzZSBcInVuZGVmaW5lZFwiOlxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoKFwiSW52YWxpZCB0eXBlICVvXCIpLCB0eXBlb2YgdmFsKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiAkKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICB3YXJwUHJvcGVydGllcyhkYXRhOiBNb2RhbFByb3BlcnRpZXMgfCBhbnkpIDogTW9kYWxQcm9wZXJ0aWVzIHtcclxuICAgICAgICBpZihkYXRhIGluc3RhbmNlb2YgTW9kYWxQcm9wZXJ0aWVzKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBkYXRhO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHByb3BzID0gbmV3IE1vZGFsUHJvcGVydGllcygpO1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGRhdGEpKVxyXG4gICAgICAgICAgICAgICAgcHJvcHNba2V5XSA9IGRhdGFba2V5XTtcclxuICAgICAgICAgICAgcmV0dXJuIHByb3BzO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufTtcclxuXHJcbmV4cG9ydCBjbGFzcyBNb2RhbFByb3BlcnRpZXMge1xyXG4gICAgdGVtcGxhdGU/OiBzdHJpbmc7XHJcbiAgICBoZWFkZXI6IEJvZHlDcmVhdG9yID0gKCkgPT4gXCJIRUFERVJcIjtcclxuICAgIGJvZHk6IEJvZHlDcmVhdG9yID0gKCkgICAgPT4gXCJCT0RZXCI7XHJcbiAgICBmb290ZXI6IEJvZHlDcmVhdG9yID0gKCkgID0+IFwiRk9PVEVSXCI7XHJcblxyXG4gICAgY2xvc2VMaXN0ZW5lcjogKCgpID0+IHZvaWQpIHwgKCgpID0+IHZvaWQpW10gPSAoKSA9PiB7fTtcclxuICAgIHJlZ2lzdGVyQ2xvc2VMaXN0ZW5lcihsaXN0ZW5lcjogKCkgPT4gdm9pZCkgOiB0aGlzIHtcclxuICAgICAgICBpZih0aGlzLmNsb3NlTGlzdGVuZXIpIHtcclxuICAgICAgICAgICAgaWYoJC5pc0FycmF5KHRoaXMuY2xvc2VMaXN0ZW5lcikpXHJcbiAgICAgICAgICAgICAgICB0aGlzLmNsb3NlTGlzdGVuZXIucHVzaChsaXN0ZW5lcik7XHJcbiAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgIHRoaXMuY2xvc2VMaXN0ZW5lciA9IFt0aGlzLmNsb3NlTGlzdGVuZXIsIGxpc3RlbmVyXTtcclxuICAgICAgICB9IGVsc2UgdGhpcy5jbG9zZUxpc3RlbmVyID0gbGlzdGVuZXI7XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9XHJcbiAgICB3aWR0aDogbnVtYmVyIHwgc3RyaW5nO1xyXG4gICAgbWluX3dpZHRoPzogbnVtYmVyIHwgc3RyaW5nO1xyXG4gICAgaGVpZ2h0OiBudW1iZXIgfCBzdHJpbmcgPSBcImF1dG9cIjtcclxuXHJcbiAgICBjbG9zZWFibGU6IGJvb2xlYW4gPSB0cnVlO1xyXG5cclxuICAgIHRyaWdnZXJDbG9zZSgpe1xyXG4gICAgICAgIGlmKCQuaXNBcnJheSh0aGlzLmNsb3NlTGlzdGVuZXIpKVxyXG4gICAgICAgICAgICBmb3IobGV0IGxpc3RlbmVyIG9mIHRoaXMuY2xvc2VMaXN0ZW5lcilcclxuICAgICAgICAgICAgICAgIGxpc3RlbmVyKCk7XHJcbiAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICB0aGlzLmNsb3NlTGlzdGVuZXIoKTtcclxuICAgIH1cclxuXHJcbiAgICB0ZW1wbGF0ZV9wcm9wZXJ0aWVzPzogYW55ID0ge307XHJcbiAgICB0cmlnZ2VyX3RhYjogYm9vbGVhbiA9IHRydWU7XHJcbiAgICBmdWxsX3NpemU/OiBib29sZWFuID0gZmFsc2U7XHJcbn1cclxuXHJcbmV4cG9ydCBuYW1lc3BhY2UgbW9kYWwge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGluaXRpYWxpemVfbW9kYWxzKCkge1xyXG4gICAgICAgIHJlZ2lzdGVyX2dsb2JhbF9ldmVudHMoKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBzY3JvbGxTaXplID0gMTg7XHJcbiAgICBmdW5jdGlvbiBzY3JvbGxfYmFyX2NsaWNrZWQoZXZlbnQpe1xyXG4gICAgICAgIGNvbnN0IHggPSBldmVudC5wYWdlWCxcclxuICAgICAgICAgICAgeSA9IGV2ZW50LnBhZ2VZLFxyXG4gICAgICAgICAgICBlID0gJChldmVudC50YXJnZXQpO1xyXG5cclxuICAgICAgICBpZihlLmhhc1Njcm9sbEJhcihcImhlaWdodFwiKSl7XHJcbiAgICAgICAgICAgIGNvbnN0IHRvcCA9IGUub2Zmc2V0KCkudG9wO1xyXG4gICAgICAgICAgICBjb25zdCByaWdodCA9IGUub2Zmc2V0KCkubGVmdCArIGUud2lkdGgoKTtcclxuICAgICAgICAgICAgY29uc3QgYm90dG9tID0gdG9wICtlLmhlaWdodCgpO1xyXG4gICAgICAgICAgICBjb25zdCBsZWZ0ID0gcmlnaHQgLSBzY3JvbGxTaXplO1xyXG5cclxuICAgICAgICAgICAgaWYoKHkgPj0gdG9wICYmIHkgPD0gYm90dG9tKSAmJiAoeCA+PSBsZWZ0ICYmIHggPD0gcmlnaHQpKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZihlLmhhc1Njcm9sbEJhcihcIndpZHRoXCIpKXtcclxuICAgICAgICAgICAgY29uc3QgYm90dG9tID0gZS5vZmZzZXQoKS50b3AgKyBlLmhlaWdodCgpO1xyXG4gICAgICAgICAgICBjb25zdCB0b3AgPSBib3R0b20gLSBzY3JvbGxTaXplO1xyXG4gICAgICAgICAgICBjb25zdCBsZWZ0ID0gZS5vZmZzZXQoKS5sZWZ0O1xyXG4gICAgICAgICAgICBjb25zdCByaWdodCA9IGxlZnQgKyBlLndpZHRoKCk7XHJcblxyXG4gICAgICAgICAgICBpZigoeSA+PSB0b3AgJiYgeSA8PSBib3R0b20pICYmICh4ID49IGxlZnQgJiYgeCA8PSByaWdodCkpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICBmdW5jdGlvbiByZWdpc3Rlcl9nbG9iYWxfZXZlbnRzKCkge1xyXG4gICAgICAgICQoZG9jdW1lbnQpLm9uKCdtb3VzZWRvd24nLCAoZXZlbnQ6IEpRdWVyeS5Nb3VzZURvd25FdmVudCkgPT4ge1xyXG4gICAgICAgICAgICAvKiBwYWdlWCBvciBwYWdlWSBhcmUgdW5kZWZpbmVkIGlmIHRoaXMgaXMgYW4gZXZlbnQgZXhlY3V0ZWQgdmlhIC50cmlnZ2VyKCdjbGljaycpOyAqL1xyXG4gICAgICAgICAgICBpZihfZ2xvYmFsX21vZGFsX2NvdW50ID09IDAgfHwgdHlwZW9mKGV2ZW50LnBhZ2VYKSA9PT0gXCJ1bmRlZmluZWRcIiB8fCB0eXBlb2YoZXZlbnQucGFnZVkpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuXHJcbiAgICAgICAgICAgIGxldCBlbGVtZW50ID0gZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50O1xyXG4gICAgICAgICAgICBjb25zdCBvcmlnaW5hbCA9IGVsZW1lbnQ7XHJcbiAgICAgICAgICAgIGRvIHtcclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbC1jb250ZW50JykpXHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIWVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbCcpKVxyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQgPT0gX2dsb2JhbF9tb2RhbF9sYXN0ICYmIF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lICsgMTAwID4gRGF0ZS5ub3coKSlcclxuICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuXHJcbiAgICAgICAgICAgICAgICBpZihlbGVtZW50ID09PSBvcmlnaW5hbCAmJiBzY3JvbGxfYmFyX2NsaWNrZWQoZXZlbnQpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0X3RpbWUgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgJChlbGVtZW50KS5maW5kKFwiPiAubW9kYWwtZGlhbG9nID4gLm1vZGFsLWNvbnRlbnQgPiAubW9kYWwtaGVhZGVyIC5idXR0b24tbW9kYWwtY2xvc2VcIikudHJpZ2dlcignY2xpY2snKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9IHdoaWxlKChlbGVtZW50ID0gZWxlbWVudC5wYXJlbnRFbGVtZW50KSk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICQoZG9jdW1lbnQpLm9uKCdrZXl1cCcsIChldmVudDogSlF1ZXJ5LktleVVwRXZlbnQpID0+IHtcclxuICAgICAgICAgICAgaWYoX2dsb2JhbF9tb2RhbF9jb3VudCA9PSAwIHx8IHR5cGVvZihldmVudC50YXJnZXQpID09PSBcInVuZGVmaW5lZFwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgaWYoZXZlbnQua2V5ICE9PSBcIkVzY2FwZVwiKVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgbGV0IGVsZW1lbnQgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XHJcbiAgICAgICAgICAgIGlmKGVsZW1lbnQubm9kZU5hbWUgPT0gXCJIVE1MSW5wdXRFbGVtZW50XCIgfHwgZWxlbWVudC5ub2RlTmFtZSA9PSBcIkhUTUxTZWxlY3RFbGVtZW50XCIgfHwgZWxlbWVudC5ub2RlTmFtZSA9PSBcIkhUTUxUZXh0QXJlYUVsZW1lbnRcIilcclxuICAgICAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgICAgIGRvIHtcclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbC1jb250ZW50JykpXHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYoIWVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKCdtb2RhbCcpKVxyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGVsZW1lbnQgPT0gX2dsb2JhbF9tb2RhbF9sYXN0ICYmIF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lICsgMTAwID4gRGF0ZS5ub3coKSlcclxuICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuXHJcbiAgICAgICAgICAgICAgICAkKGVsZW1lbnQpLmZpbmQoXCI+IC5tb2RhbC1kaWFsb2cgPiAubW9kYWwtY29udGVudCA+IC5tb2RhbC1oZWFkZXIgLmJ1dHRvbi1tb2RhbC1jbG9zZVwiKS50cmlnZ2VyKCdjbGljaycpO1xyXG4gICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIH0gd2hpbGUoKGVsZW1lbnQgPSBlbGVtZW50LnBhcmVudEVsZW1lbnQpKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxufVxyXG5tb2RhbC5pbml0aWFsaXplX21vZGFscygpO1xyXG5cclxubGV0IF9nbG9iYWxfbW9kYWxfY291bnQgPSAwO1xyXG5sZXQgX2dsb2JhbF9tb2RhbF9sYXN0OiBIVE1MRWxlbWVudDtcclxubGV0IF9nbG9iYWxfbW9kYWxfbGFzdF90aW1lOiBudW1iZXI7XHJcblxyXG5leHBvcnQgY2xhc3MgTW9kYWwge1xyXG4gICAgcHJpdmF0ZSBfaHRtbFRhZzogSlF1ZXJ5O1xyXG4gICAgcHJvcGVydGllczogTW9kYWxQcm9wZXJ0aWVzO1xyXG4gICAgc2hvd246IGJvb2xlYW47XHJcblxyXG4gICAgb3Blbl9saXN0ZW5lcjogKCgpID0+IGFueSlbXSA9IFtdO1xyXG4gICAgY2xvc2VfbGlzdGVuZXI6ICgoKSA9PiBhbnkpW10gPSBbXTtcclxuICAgIGNsb3NlX2VsZW1lbnRzOiBKUXVlcnk7XHJcblxyXG4gICAgY29uc3RydWN0b3IocHJvcHM6IE1vZGFsUHJvcGVydGllcykge1xyXG4gICAgICAgIHRoaXMucHJvcGVydGllcyA9IHByb3BzO1xyXG4gICAgICAgIHRoaXMuc2hvd24gPSBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgaHRtbFRhZygpIDogSlF1ZXJ5IHtcclxuICAgICAgICBpZighdGhpcy5faHRtbFRhZykgdGhpcy5fY3JlYXRlKCk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2h0bWxUYWc7XHJcbiAgICB9XHJcblxyXG4gICAgcHJpdmF0ZSBfY3JlYXRlKCkge1xyXG4gICAgICAgIGNvbnN0IGhlYWRlciA9IE1vZGFsRnVuY3Rpb25zLmpxdWVyaWVmeSh0aGlzLnByb3BlcnRpZXMuaGVhZGVyLCBFbGVtZW50VHlwZS5IRUFERVIpO1xyXG4gICAgICAgIGNvbnN0IGJvZHkgPSBNb2RhbEZ1bmN0aW9ucy5qcXVlcmllZnkodGhpcy5wcm9wZXJ0aWVzLmJvZHksIEVsZW1lbnRUeXBlLkJPRFkpO1xyXG4gICAgICAgIGNvbnN0IGZvb3RlciA9IE1vZGFsRnVuY3Rpb25zLmpxdWVyaWVmeSh0aGlzLnByb3BlcnRpZXMuZm9vdGVyLCBFbGVtZW50VHlwZS5GT09URVIpO1xyXG5cclxuICAgICAgICAvL0ZJWE1FOiBjYWNoZSB0ZW1wbGF0ZVxyXG4gICAgICAgIGNvbnN0IHRlbXBsYXRlID0gJCh0aGlzLnByb3BlcnRpZXMudGVtcGxhdGUgfHwgXCIjdG1wbF9tb2RhbFwiKTtcclxuXHJcbiAgICAgICAgY29uc3QgcHJvcGVydGllcyA9IHtcclxuICAgICAgICAgICAgbW9kYWxfaGVhZGVyOiBoZWFkZXIsXHJcbiAgICAgICAgICAgIG1vZGFsX2JvZHk6IGJvZHksXHJcbiAgICAgICAgICAgIG1vZGFsX2Zvb3RlcjogZm9vdGVyLFxyXG5cclxuICAgICAgICAgICAgY2xvc2VhYmxlOiB0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlLFxyXG4gICAgICAgICAgICBmdWxsX3NpemU6IHRoaXMucHJvcGVydGllcy5mdWxsX3NpemVcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBpZih0aGlzLnByb3BlcnRpZXMudGVtcGxhdGVfcHJvcGVydGllcylcclxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihwcm9wZXJ0aWVzLCB0aGlzLnByb3BlcnRpZXMudGVtcGxhdGVfcHJvcGVydGllcyk7XHJcblxyXG4gICAgICAgIGNvbnN0IHRhZyA9IHRlbXBsYXRlLnJlbmRlclRhZyhwcm9wZXJ0aWVzKTtcclxuICAgICAgICBpZih0eXBlb2YodGhpcy5wcm9wZXJ0aWVzLndpZHRoKSAhPT0gXCJ1bmRlZmluZWRcIiAmJiB0eXBlb2YodGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aCkgIT09IFwidW5kZWZpbmVkXCIpXHJcbiAgICAgICAgICAgIHRhZy5maW5kKFwiLm1vZGFsLWNvbnRlbnRcIilcclxuICAgICAgICAgICAgICAgIC5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aClcclxuICAgICAgICAgICAgICAgIC5jc3MoXCJ3aWR0aFwiLCB0aGlzLnByb3BlcnRpZXMud2lkdGgpO1xyXG4gICAgICAgIGVsc2UgaWYodHlwZW9mKHRoaXMucHJvcGVydGllcy53aWR0aCkgIT09IFwidW5kZWZpbmVkXCIpIC8vTGVnYWN5IHN1cHBvcnRcclxuICAgICAgICAgICAgdGFnLmZpbmQoXCIubW9kYWwtY29udGVudFwiKS5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLndpZHRoKTtcclxuICAgICAgICBlbHNlIGlmKHR5cGVvZih0aGlzLnByb3BlcnRpZXMubWluX3dpZHRoKSAhPT0gXCJ1bmRlZmluZWRcIilcclxuICAgICAgICAgICAgdGFnLmZpbmQoXCIubW9kYWwtY29udGVudFwiKS5jc3MoXCJtaW4td2lkdGhcIiwgdGhpcy5wcm9wZXJ0aWVzLm1pbl93aWR0aCk7XHJcblxyXG4gICAgICAgIHRoaXMuY2xvc2VfZWxlbWVudHMgPSB0YWcuZmluZChcIi5idXR0b24tbW9kYWwtY2xvc2VcIik7XHJcbiAgICAgICAgdGhpcy5jbG9zZV9lbGVtZW50cy50b2dnbGUodGhpcy5wcm9wZXJ0aWVzLmNsb3NlYWJsZSkub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICBpZih0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlKVxyXG4gICAgICAgICAgICAgICAgdGhpcy5jbG9zZSgpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIHRoaXMuX2h0bWxUYWcgPSB0YWc7XHJcblxyXG4gICAgICAgIHRoaXMuX2h0bWxUYWcuZmluZChcImlucHV0XCIpLm9uKCdjaGFuZ2UnLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICQoZXZlbnQudGFyZ2V0KS5wYXJlbnRzKFwiLmZvcm0tZ3JvdXBcIikudG9nZ2xlQ2xhc3MoJ2lzLWZpbGxlZCcsICEhKGV2ZW50LnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50KS52YWx1ZSk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vVE9ETzogQWZ0ZXIgdGhlIGFuaW1hdGlvbiFcclxuICAgICAgICB0aGlzLl9odG1sVGFnLm9uKCdoaWRlLmJzLm1vZGFsJywgZXZlbnQgPT4gIXRoaXMucHJvcGVydGllcy5jbG9zZWFibGUgfHwgdGhpcy5jbG9zZSgpKTtcclxuICAgICAgICB0aGlzLl9odG1sVGFnLm9uKCdoaWRkZW4uYnMubW9kYWwnLCBldmVudCA9PiB0aGlzLl9odG1sVGFnLnJlbW92ZSgpKTtcclxuICAgIH1cclxuXHJcbiAgICBvcGVuKCkge1xyXG4gICAgICAgIGlmKHRoaXMuc2hvd24pXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuXHJcbiAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0X3RpbWUgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgIF9nbG9iYWxfbW9kYWxfbGFzdCA9IHRoaXMuaHRtbFRhZ1swXTtcclxuXHJcbiAgICAgICAgdGhpcy5zaG93biA9IHRydWU7XHJcbiAgICAgICAgdGhpcy5odG1sVGFnLmFwcGVuZFRvKCQoXCJib2R5XCIpKTtcclxuXHJcbiAgICAgICAgX2dsb2JhbF9tb2RhbF9jb3VudCsrO1xyXG4gICAgICAgIHRoaXMuaHRtbFRhZy5zaG93KCk7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmh0bWxUYWcuYWRkQ2xhc3MoJ3Nob3duJyksIDApO1xyXG5cclxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgZm9yKGNvbnN0IGxpc3RlbmVyIG9mIHRoaXMub3Blbl9saXN0ZW5lcikgbGlzdGVuZXIoKTtcclxuICAgICAgICAgICAgdGhpcy5odG1sVGFnLmZpbmQoXCIudGFiXCIpLnRyaWdnZXIoJ3RhYi5yZXNpemUnKTtcclxuICAgICAgICB9LCAzMDApO1xyXG4gICAgfVxyXG5cclxuICAgIGNsb3NlKCkge1xyXG4gICAgICAgIGlmKCF0aGlzLnNob3duKSByZXR1cm47XHJcblxyXG4gICAgICAgIF9nbG9iYWxfbW9kYWxfY291bnQtLTtcclxuICAgICAgICBpZihfZ2xvYmFsX21vZGFsX2xhc3QgPT09IHRoaXMuaHRtbFRhZ1swXSlcclxuICAgICAgICAgICAgX2dsb2JhbF9tb2RhbF9sYXN0ID0gdW5kZWZpbmVkO1xyXG5cclxuICAgICAgICB0aGlzLnNob3duID0gZmFsc2U7XHJcbiAgICAgICAgdGhpcy5odG1sVGFnLnJlbW92ZUNsYXNzKCdzaG93bicpO1xyXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLmh0bWxUYWcucmVtb3ZlKCk7XHJcbiAgICAgICAgICAgIHRoaXMuX2h0bWxUYWcgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgfSwgMzAwKTtcclxuICAgICAgICB0aGlzLnByb3BlcnRpZXMudHJpZ2dlckNsb3NlKCk7XHJcbiAgICAgICAgZm9yKGNvbnN0IGxpc3RlbmVyIG9mIHRoaXMuY2xvc2VfbGlzdGVuZXIpXHJcbiAgICAgICAgICAgIGxpc3RlbmVyKCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2V0X2Nsb3NlYWJsZShmbGFnOiBib29sZWFuKSB7XHJcbiAgICAgICAgaWYoZmxhZyA9PT0gdGhpcy5wcm9wZXJ0aWVzLmNsb3NlYWJsZSlcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICB0aGlzLnByb3BlcnRpZXMuY2xvc2VhYmxlID0gZmxhZztcclxuICAgICAgICB0aGlzLmNsb3NlX2VsZW1lbnRzLnRvZ2dsZShmbGFnKTtcclxuICAgIH1cclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1vZGFsKGRhdGE6IE1vZGFsUHJvcGVydGllcyB8IGFueSkgOiBNb2RhbCB7XHJcbiAgICByZXR1cm4gbmV3IE1vZGFsKE1vZGFsRnVuY3Rpb25zLndhcnBQcm9wZXJ0aWVzKGRhdGEpKTtcclxufVxyXG5cclxuZXhwb3J0IGNsYXNzIElucHV0TW9kYWxQcm9wZXJ0aWVzIGV4dGVuZHMgTW9kYWxQcm9wZXJ0aWVzIHtcclxuICAgIG1heExlbmd0aD86IG51bWJlcjtcclxuXHJcbiAgICBmaWVsZF90aXRsZT86IHN0cmluZztcclxuICAgIGZpZWxkX2xhYmVsPzogc3RyaW5nO1xyXG4gICAgZmllbGRfcGxhY2Vob2xkZXI/OiBzdHJpbmc7XHJcblxyXG4gICAgZXJyb3JfbWVzc2FnZT86IHN0cmluZztcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlucHV0TW9kYWwoaGVhZE1lc3NhZ2U6IEJvZHlDcmVhdG9yLCBxdWVzdGlvbjogQm9keUNyZWF0b3IsIHZhbGlkYXRvcjogKGlucHV0OiBzdHJpbmcpID0+IGJvb2xlYW4sIGNhbGxiYWNrOiAoZmxhZzogYm9vbGVhbiB8IHN0cmluZykgPT4gdm9pZCwgcHJvcHM6IElucHV0TW9kYWxQcm9wZXJ0aWVzIHwgYW55ID0ge30pIDogTW9kYWwge1xyXG4gICAgcHJvcHMgPSBNb2RhbEZ1bmN0aW9ucy53YXJwUHJvcGVydGllcyhwcm9wcyk7XHJcbiAgICBwcm9wcy50ZW1wbGF0ZV9wcm9wZXJ0aWVzIHx8IChwcm9wcy50ZW1wbGF0ZV9wcm9wZXJ0aWVzID0ge30pO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF90aXRsZSA9IHByb3BzLmZpZWxkX3RpdGxlO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF9sYWJlbCA9IHByb3BzLmZpZWxkX2xhYmVsO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5maWVsZF9wbGFjZWhvbGRlciA9IHByb3BzLmZpZWxkX3BsYWNlaG9sZGVyO1xyXG4gICAgcHJvcHMudGVtcGxhdGVfcHJvcGVydGllcy5lcnJvcl9tZXNzYWdlID0gcHJvcHMuZXJyb3JfbWVzc2FnZTtcclxuXHJcbiAgICBwcm9wcy50ZW1wbGF0ZSA9IFwiI3RtcGxfbW9kYWxfaW5wdXRcIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkTWVzc2FnZTtcclxuICAgIHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMucXVlc3Rpb24gPSBNb2RhbEZ1bmN0aW9ucy5qcXVlcmllZnkocXVlc3Rpb24pO1xyXG5cclxuICAgIGNvbnN0IG1vZGFsID0gY3JlYXRlTW9kYWwocHJvcHMpO1xyXG5cclxuICAgIGNvbnN0IGlucHV0ID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci12YWx1ZSBpbnB1dFwiKTtcclxuICAgIGNvbnN0IGJ1dHRvbl9jYW5jZWwgPSBtb2RhbC5odG1sVGFnLmZpbmQoXCIuYnV0dG9uLWNhbmNlbFwiKTtcclxuICAgIGNvbnN0IGJ1dHRvbl9zdWJtaXQgPSBtb2RhbC5odG1sVGFnLmZpbmQoXCIuYnV0dG9uLXN1Ym1pdFwiKTtcclxuXHJcbiAgICBsZXQgc3VibWl0ZWQgPSBmYWxzZTtcclxuICAgIGlucHV0Lm9uKCdrZXl1cCBjaGFuZ2UnLCBldmVudCA9PiB7XHJcbiAgICAgICAgY29uc3Qgc3RyID0gaW5wdXQudmFsKCkgYXMgc3RyaW5nO1xyXG4gICAgICAgIGNvbnN0IHZhbGlkID0gc3RyICE9PSB1bmRlZmluZWQgJiYgdmFsaWRhdG9yKHN0cik7XHJcblxyXG4gICAgICAgIGlucHV0LmF0dHIoXCJwYXR0ZXJuXCIsIHZhbGlkID8gbnVsbCA6IFwiXlthXXsxMDAwfSRcIikudG9nZ2xlQ2xhc3MoXCJpcy1pbnZhbGlkXCIsICF2YWxpZCk7XHJcbiAgICAgICAgYnV0dG9uX3N1Ym1pdC5wcm9wKFwiZGlzYWJsZWRcIiwgIXZhbGlkKTtcclxuICAgIH0pO1xyXG4gICAgaW5wdXQub24oJ2tleWRvd24nLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoZXZlbnQua2V5Q29kZSAhPT0gS2V5Q29kZS5LRVlfUkVUVVJOIHx8IGV2ZW50LnNoaWZ0S2V5KVxyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgaWYoYnV0dG9uX3N1Ym1pdC5wcm9wKFwiZGlzYWJsZWRcIikpXHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICBidXR0b25fc3VibWl0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBidXR0b25fc3VibWl0Lm9uKCdjbGljaycsIGV2ZW50ID0+IHtcclxuICAgICAgICBpZighc3VibWl0ZWQpIHtcclxuICAgICAgICAgICAgc3VibWl0ZWQgPSB0cnVlO1xyXG4gICAgICAgICAgICBjb25zdCBzdHIgPSBpbnB1dC52YWwoKSBhcyBzdHJpbmc7XHJcbiAgICAgICAgICAgIGlmKHN0ciAhPT0gdW5kZWZpbmVkICYmIHZhbGlkYXRvcihzdHIpKVxyXG4gICAgICAgICAgICAgICAgY2FsbGJhY2soc3RyKTtcclxuICAgICAgICAgICAgZWxzZVxyXG4gICAgICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG4gICAgfSkucHJvcChcImRpc2FibGVkXCIsICF2YWxpZGF0b3IoXCJcIikpOyAvKiBkaXNhYmxlZCBpZiBlbXB0eSBpbnB1dCBpc24ndCBhbGxvd2VkICovXHJcblxyXG4gICAgYnV0dG9uX2NhbmNlbC5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgaWYoIXN1Ym1pdGVkKSB7XHJcbiAgICAgICAgICAgIHN1Ym1pdGVkID0gdHJ1ZTtcclxuICAgICAgICAgICAgY2FsbGJhY2soZmFsc2UpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbW9kYWwub3Blbl9saXN0ZW5lci5wdXNoKCgpID0+IGlucHV0LmZvY3VzKCkpO1xyXG4gICAgbW9kYWwuY2xvc2VfbGlzdGVuZXIucHVzaCgoKSA9PiBidXR0b25fY2FuY2VsLnRyaWdnZXIoJ2NsaWNrJykpO1xyXG4gICAgcmV0dXJuIG1vZGFsO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRXJyb3JNb2RhbChoZWFkZXI6IEJvZHlDcmVhdG9yLCBtZXNzYWdlOiBCb2R5Q3JlYXRvciwgcHJvcHM6IE1vZGFsUHJvcGVydGllcyB8IGFueSA9IHsgZm9vdGVyOiB1bmRlZmluZWQgfSkge1xyXG4gICAgcHJvcHMgPSBNb2RhbEZ1bmN0aW9ucy53YXJwUHJvcGVydGllcyhwcm9wcyk7XHJcbiAgICAocHJvcHMudGVtcGxhdGVfcHJvcGVydGllcyB8fCAocHJvcHMudGVtcGxhdGVfcHJvcGVydGllcyA9IHt9KSkuaGVhZGVyX2NsYXNzID0gXCJtb2RhbC1oZWFkZXItZXJyb3JcIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkZXI7XHJcbiAgICBwcm9wcy5ib2R5ID0gbWVzc2FnZTtcclxuXHJcbiAgICBjb25zdCBtb2RhbCA9IGNyZWF0ZU1vZGFsKHByb3BzKTtcclxuICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtZXJyb3JcIik7XHJcbiAgICByZXR1cm4gbW9kYWw7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJbmZvTW9kYWwoaGVhZGVyOiBCb2R5Q3JlYXRvciwgbWVzc2FnZTogQm9keUNyZWF0b3IsIHByb3BzOiBNb2RhbFByb3BlcnRpZXMgfCBhbnkgPSB7IGZvb3RlcjogdW5kZWZpbmVkIH0pIHtcclxuICAgIHByb3BzID0gTW9kYWxGdW5jdGlvbnMud2FycFByb3BlcnRpZXMocHJvcHMpO1xyXG4gICAgKHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMgfHwgKHByb3BzLnRlbXBsYXRlX3Byb3BlcnRpZXMgPSB7fSkpLmhlYWRlcl9jbGFzcyA9IFwibW9kYWwtaGVhZGVyLWluZm9cIjtcclxuXHJcbiAgICBwcm9wcy5oZWFkZXIgPSBoZWFkZXI7XHJcbiAgICBwcm9wcy5ib2R5ID0gbWVzc2FnZTtcclxuXHJcbiAgICBjb25zdCBtb2RhbCA9IGNyZWF0ZU1vZGFsKHByb3BzKTtcclxuICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtaW5mb1wiKTtcclxuICAgIHJldHVybiBtb2RhbDtcclxufVxyXG4iLCJpbXBvcnQge2xvZywgTG9nQ2F0ZWdvcnl9IGZyb20gXCIuLi8uLi9sb2dcIjtcclxuXHJcbmV4cG9ydCBlbnVtIENoYXRUeXBlIHtcclxuICAgIEdFTkVSQUwsXHJcbiAgICBTRVJWRVIsXHJcbiAgICBDSEFOTkVMLFxyXG4gICAgQ0xJRU5UXHJcbn1cclxuXHJcbmRlY2xhcmUgY29uc3QgeGJiY29kZTogYW55O1xyXG5leHBvcnQgbmFtZXNwYWNlIE1lc3NhZ2VIZWxwZXIge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGh0bWxFc2NhcGUobWVzc2FnZTogc3RyaW5nKSA6IHN0cmluZ1tdIHtcclxuICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgICAgICBkaXYuaW5uZXJUZXh0ID0gbWVzc2FnZTtcclxuICAgICAgICBtZXNzYWdlID0gIGRpdi5pbm5lckhUTUw7XHJcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2UucmVwbGFjZSgvIC9nLCAnJm5ic3A7Jykuc3BsaXQoLzxicj4vKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gZm9ybWF0RWxlbWVudChvYmplY3Q6IGFueSwgZXNjYXBlX2h0bWw6IGJvb2xlYW4gPSB0cnVlKSA6IEpRdWVyeVtdIHtcclxuICAgICAgICBpZigkLmlzQXJyYXkob2JqZWN0KSkge1xyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gW107XHJcbiAgICAgICAgICAgIGZvcihsZXQgZWxlbWVudCBvZiBvYmplY3QpXHJcbiAgICAgICAgICAgICAgICByZXN1bHQucHVzaCguLi5mb3JtYXRFbGVtZW50KGVsZW1lbnQsIGVzY2FwZV9odG1sKSk7XHJcbiAgICAgICAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09IFwic3RyaW5nXCIpIHtcclxuICAgICAgICAgICAgaWYob2JqZWN0Lmxlbmd0aCA9PSAwKSByZXR1cm4gW107XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gZXNjYXBlX2h0bWwgP1xyXG4gICAgICAgICAgICAgICAgaHRtbEVzY2FwZShvYmplY3QpLm1hcCgoZW50cnksIGlkeCwgYXJyYXkpID0+ICQuc3Bhd24oXCJhXCIpLmNzcyhcImRpc3BsYXlcIiwgKGlkeCA9PSAwIHx8IGlkeCArIDEgPT0gYXJyYXkubGVuZ3RoID8gXCJpbmxpbmVcIiA6IFwiXCIpICsgXCJibG9ja1wiKS5odG1sKGVudHJ5ID09IFwiXCIgJiYgaWR4ICE9IDAgPyBcIiZuYnNwO1wiIDogZW50cnkpKSA6XHJcbiAgICAgICAgICAgICAgICBbJC5zcGF3bihcImRpdlwiKS5jc3MoXCJkaXNwbGF5XCIsIFwiaW5saW5lLWJsb2NrXCIpLmh0bWwob2JqZWN0KV07XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcIm9iamVjdFwiKSB7XHJcbiAgICAgICAgICAgIGlmKG9iamVjdCBpbnN0YW5jZW9mICQpXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gW29iamVjdCBhcyBhbnldO1xyXG4gICAgICAgICAgICByZXR1cm4gZm9ybWF0RWxlbWVudChcIjx1bmtud29uIG9iamVjdD5cIik7XHJcbiAgICAgICAgfSBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcImZ1bmN0aW9uXCIpIHJldHVybiBmb3JtYXRFbGVtZW50KG9iamVjdCgpLCBlc2NhcGVfaHRtbCk7XHJcbiAgICAgICAgZWxzZSBpZih0eXBlb2Yob2JqZWN0KSA9PT0gXCJ1bmRlZmluZWRcIikgcmV0dXJuIGZvcm1hdEVsZW1lbnQoXCI8dW5kZWZpbmVkPlwiKTtcclxuICAgICAgICBlbHNlIGlmKHR5cGVvZihvYmplY3QpID09PSBcIm51bWJlclwiKSByZXR1cm4gWyQuc3Bhd24oXCJhXCIpLnRleHQob2JqZWN0KV07XHJcbiAgICAgICAgcmV0dXJuIGZvcm1hdEVsZW1lbnQoXCI8dW5rbm93biBvYmplY3QgdHlwZSBcIiArIHR5cGVvZiBvYmplY3QgKyBcIj5cIik7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdE1lc3NhZ2UocGF0dGVybjogc3RyaW5nLCAuLi5vYmplY3RzOiBhbnlbXSkgOiBKUXVlcnlbXSB7XHJcbiAgICAgICAgbGV0IGJlZ2luID0gMCwgZm91bmQgPSAwO1xyXG5cclxuICAgICAgICBsZXQgcmVzdWx0OiBKUXVlcnlbXSA9IFtdO1xyXG4gICAgICAgIGRvIHtcclxuICAgICAgICAgICAgZm91bmQgPSBwYXR0ZXJuLmluZGV4T2YoJ3snLCBmb3VuZCk7XHJcbiAgICAgICAgICAgIGlmKGZvdW5kID09IC0xIHx8IHBhdHRlcm4ubGVuZ3RoIDw9IGZvdW5kICsgMSkge1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goLi4uZm9ybWF0RWxlbWVudChwYXR0ZXJuLnN1YnN0cihiZWdpbikpKTtcclxuICAgICAgICAgICAgICAgIGJyZWFrO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZihmb3VuZCA+IDAgJiYgcGF0dGVybltmb3VuZCAtIDFdID09ICdcXFxcJykge1xyXG4gICAgICAgICAgICAgICAgLy9UT0RPIHJlbW92ZSB0aGUgZXNjYXBlIVxyXG4gICAgICAgICAgICAgICAgZm91bmQrKztcclxuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXN1bHQucHVzaCguLi5mb3JtYXRFbGVtZW50KHBhdHRlcm4uc3Vic3RyKGJlZ2luLCBmb3VuZCAtIGJlZ2luKSkpOyAvL0FwcGVuZCB0aGUgdGV4dFxyXG5cclxuICAgICAgICAgICAgbGV0IG9mZnNldCA9IDA7XHJcbiAgICAgICAgICAgIGlmKHBhdHRlcm5bZm91bmQgKyAxXSA9PSAnOicpIHtcclxuICAgICAgICAgICAgICAgIG9mZnNldCsrOyAvKiB0aGUgYmVnaW5uaW5nIDogKi9cclxuICAgICAgICAgICAgICAgIHdoaWxlIChwYXR0ZXJuW2ZvdW5kICsgMSArIG9mZnNldF0gIT0gJzonICYmIGZvdW5kICsgMSArIG9mZnNldCA8IHBhdHRlcm4ubGVuZ3RoKSBvZmZzZXQrKztcclxuICAgICAgICAgICAgICAgIGNvbnN0IHRhZyA9IHBhdHRlcm4uc3Vic3RyKGZvdW5kICsgMiwgb2Zmc2V0IC0gMSk7XHJcblxyXG4gICAgICAgICAgICAgICAgb2Zmc2V0Kys7IC8qIHRoZSBlbmRpbmcgOiAqL1xyXG4gICAgICAgICAgICAgICAgaWYocGF0dGVybltmb3VuZCArIG9mZnNldCArIDFdICE9ICd9JyAmJiBmb3VuZCArIDEgKyBvZmZzZXQgPCBwYXR0ZXJuLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGZvdW5kKys7XHJcbiAgICAgICAgICAgICAgICAgICAgY29udGludWU7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgcmVzdWx0LnB1c2goJC5zcGF3bih0YWcgYXMgYW55KSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgbnVtYmVyO1xyXG4gICAgICAgICAgICAgICAgd2hpbGUgKFwiMDEyMzQ1Njc4OVwiLmluY2x1ZGVzKHBhdHRlcm5bZm91bmQgKyAxICsgb2Zmc2V0XSkpIG9mZnNldCsrO1xyXG4gICAgICAgICAgICAgICAgbnVtYmVyID0gcGFyc2VJbnQob2Zmc2V0ID4gMCA/IHBhdHRlcm4uc3Vic3RyKGZvdW5kICsgMSwgb2Zmc2V0KSA6IFwiMFwiKTtcclxuICAgICAgICAgICAgICAgIGlmKHBhdHRlcm5bZm91bmQgKyBvZmZzZXQgKyAxXSAhPSAnfScpIHtcclxuICAgICAgICAgICAgICAgICAgICBmb3VuZCsrO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmKG9iamVjdHMubGVuZ3RoIDwgbnVtYmVyKVxyXG4gICAgICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkdFTkVSQUwsIHRyKFwiTWVzc2FnZSB0byBmb3JtYXQgY29udGFpbnMgaW52YWxpZCBpbmRleCAoJW8pXCIpLCBudW1iZXIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLmZvcm1hdEVsZW1lbnQob2JqZWN0c1tudW1iZXJdKSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvdW5kID0gZm91bmQgKyAxICsgb2Zmc2V0O1xyXG4gICAgICAgICAgICBiZWdpbiA9IGZvdW5kICsgMTtcclxuICAgICAgICB9IHdoaWxlKGZvdW5kKyspO1xyXG5cclxuICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfVxyXG5cclxuICAgIC8vVE9ETzogUmVtb3ZlIHRoaXMgKG9ubHkgbGVnYWN5KVxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGJiY29kZV9jaGF0KG1lc3NhZ2U6IHN0cmluZykgOiBKUXVlcnlbXSB7XHJcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VzLmZvcm1hdHRlci5iYmNvZGUuZm9ybWF0KG1lc3NhZ2UsIHtcclxuICAgICAgICAgICAgaXNfY2hhdF9tZXNzYWdlOiB0cnVlXHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IG5hbWVzcGFjZSBuZXR3b3JrIHtcclxuICAgICAgICBleHBvcnQgY29uc3QgS0IgPSAxMDI0O1xyXG4gICAgICAgIGV4cG9ydCBjb25zdCBNQiA9IDEwMjQgKiBLQjtcclxuICAgICAgICBleHBvcnQgY29uc3QgR0IgPSAxMDI0ICogTUI7XHJcbiAgICAgICAgZXhwb3J0IGNvbnN0IFRCID0gMTAyNCAqIEdCO1xyXG5cclxuICAgICAgICBleHBvcnQgZnVuY3Rpb24gZm9ybWF0X2J5dGVzKHZhbHVlOiBudW1iZXIsIG9wdGlvbnM/OiB7XHJcbiAgICAgICAgICAgIHRpbWU/OiBzdHJpbmcsXHJcbiAgICAgICAgICAgIHVuaXQ/OiBzdHJpbmcsXHJcbiAgICAgICAgICAgIGV4YWN0PzogYm9vbGVhblxyXG4gICAgICAgIH0pIDogc3RyaW5nIHtcclxuICAgICAgICAgICAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge307XHJcbiAgICAgICAgICAgIGlmKHR5cGVvZiBvcHRpb25zLmV4YWN0ICE9PSBcImJvb2xlYW5cIilcclxuICAgICAgICAgICAgICAgIG9wdGlvbnMuZXhhY3QgPSB0cnVlO1xyXG4gICAgICAgICAgICBpZih0eXBlb2Ygb3B0aW9ucy51bml0ICE9PSBcInN0cmluZ1wiKVxyXG4gICAgICAgICAgICAgICAgb3B0aW9ucy51bml0ID0gXCJCeXRlc1wiO1xyXG5cclxuICAgICAgICAgICAgbGV0IHBvaW50cyA9IHZhbHVlLnRvRml4ZWQoMCkucmVwbGFjZSgvKFxcZCkoPz0oXFxkezN9KSsoPyFcXGQpKS9nLCAnJDEsJyk7XHJcblxyXG4gICAgICAgICAgICBsZXQgdiwgdW5pdDtcclxuICAgICAgICAgICAgaWYodmFsdWUgPiAyICogVEIpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIlRCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBUQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gR0IpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIkdCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBHQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gTUIpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIk1CXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBNQjtcclxuICAgICAgICAgICAgfSBlbHNlIGlmKHZhbHVlID4gS0IpIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIktCXCI7XHJcbiAgICAgICAgICAgICAgICB2ID0gdmFsdWUgLyBLQjtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgIHVuaXQgPSBcIlwiO1xyXG4gICAgICAgICAgICAgICAgdiA9IHZhbHVlO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBsZXQgcmVzdWx0ID0gXCJcIjtcclxuICAgICAgICAgICAgaWYob3B0aW9ucy5leGFjdCB8fCAhdW5pdCkge1xyXG4gICAgICAgICAgICAgICAgcmVzdWx0ICs9IHBvaW50cztcclxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMudW5pdCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIG9wdGlvbnMudW5pdDtcclxuICAgICAgICAgICAgICAgICAgICBpZihvcHRpb25zLnRpbWUpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSBcIi9cIiArIG9wdGlvbnMudGltZTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZih1bml0KSB7XHJcbiAgICAgICAgICAgICAgICByZXN1bHQgKz0gKHJlc3VsdCA/IFwiIC8gXCIgOiBcIlwiKSArIHYudG9GaXhlZCgyKSArIFwiIFwiICsgdW5pdDtcclxuICAgICAgICAgICAgICAgIGlmKG9wdGlvbnMudGltZSlcclxuICAgICAgICAgICAgICAgICAgICByZXN1bHQgKz0gXCIvXCIgKyBvcHRpb25zLnRpbWU7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGNvbnN0IEsgPSAxMDAwO1xyXG4gICAgZXhwb3J0IGNvbnN0IE0gPSAxMDAwICogSztcclxuICAgIGV4cG9ydCBjb25zdCBHID0gMTAwMCAqIE07XHJcbiAgICBleHBvcnQgY29uc3QgVCA9IDEwMDAgKiBHO1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdF9udW1iZXIodmFsdWU6IG51bWJlciwgb3B0aW9ucz86IHtcclxuICAgICAgICB0aW1lPzogc3RyaW5nLFxyXG4gICAgICAgIHVuaXQ/OiBzdHJpbmdcclxuICAgIH0pIHtcclxuICAgICAgICBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihvcHRpb25zIHx8IHt9LCB7fSk7XHJcblxyXG4gICAgICAgIGxldCBwb2ludHMgPSB2YWx1ZS50b0ZpeGVkKDApLnJlcGxhY2UoLyhcXGQpKD89KFxcZHszfSkrKD8hXFxkKSkvZywgJyQxLCcpO1xyXG5cclxuICAgICAgICBsZXQgdiwgdW5pdDtcclxuICAgICAgICBpZih2YWx1ZSA+IDIgKiBUKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIlRcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gVDtcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBHKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIkdcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gRztcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBNKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIk1cIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gTTtcclxuICAgICAgICB9IGVsc2UgaWYodmFsdWUgPiBLKSB7XHJcbiAgICAgICAgICAgIHVuaXQgPSBcIktcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlIC8gSztcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICB1bml0ID0gXCJcIjtcclxuICAgICAgICAgICAgdiA9IHZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZih1bml0ICYmIG9wdGlvbnMudGltZSlcclxuICAgICAgICAgICAgdW5pdCA9IHVuaXQgKyBcIi9cIiArIG9wdGlvbnMudGltZTtcclxuICAgICAgICByZXR1cm4gcG9pbnRzICsgXCIgXCIgKyAob3B0aW9ucy51bml0IHx8IFwiXCIpICsgKHVuaXQgPyAoXCIgLyBcIiArIHYudG9GaXhlZCgyKSArIFwiIFwiICsgdW5pdCkgOiBcIlwiKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY29uc3QgVElNRV9TRUNPTkQgPSAxMDAwO1xyXG4gICAgZXhwb3J0IGNvbnN0IFRJTUVfTUlOVVRFID0gNjAgKiBUSU1FX1NFQ09ORDtcclxuICAgIGV4cG9ydCBjb25zdCBUSU1FX0hPVVIgPSA2MCAqIFRJTUVfTUlOVVRFO1xyXG4gICAgZXhwb3J0IGNvbnN0IFRJTUVfREFZID0gMjQgKiBUSU1FX0hPVVI7XHJcbiAgICBleHBvcnQgY29uc3QgVElNRV9XRUVLID0gNyAqIFRJTUVfREFZO1xyXG5cclxuICAgIGV4cG9ydCBmdW5jdGlvbiBmb3JtYXRfdGltZSh0aW1lOiBudW1iZXIsIGRlZmF1bHRfdmFsdWU6IHN0cmluZykge1xyXG4gICAgICAgIGxldCByZXN1bHQgPSBcIlwiO1xyXG4gICAgICAgIGlmKHRpbWUgPiBUSU1FX1dFRUspIHtcclxuICAgICAgICAgICAgY29uc3QgYW1vdW50ID0gTWF0aC5mbG9vcih0aW1lIC8gVElNRV9XRUVLKTtcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiIFwiICsgYW1vdW50ICsgXCIgXCIgKyAoYW1vdW50ID4gMSA/IHRyKFwiV2Vla3NcIikgOiB0cihcIldlZWtcIikpO1xyXG4gICAgICAgICAgICB0aW1lIC09IGFtb3VudCAqIFRJTUVfV0VFSztcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKHRpbWUgPiBUSU1FX0RBWSkge1xyXG4gICAgICAgICAgICBjb25zdCBhbW91bnQgPSBNYXRoLmZsb29yKHRpbWUgLyBUSU1FX0RBWSk7XHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIGFtb3VudCArIFwiIFwiICsgKGFtb3VudCA+IDEgPyB0cihcIkRheXNcIikgOiB0cihcIkRheVwiKSk7XHJcbiAgICAgICAgICAgIHRpbWUgLT0gYW1vdW50ICogVElNRV9EQVk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZih0aW1lID4gVElNRV9IT1VSKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGFtb3VudCA9IE1hdGguZmxvb3IodGltZSAvIFRJTUVfSE9VUik7XHJcbiAgICAgICAgICAgIHJlc3VsdCArPSBcIiBcIiArIGFtb3VudCArIFwiIFwiICsgKGFtb3VudCA+IDEgPyB0cihcIkhvdXJzXCIpIDogdHIoXCJIb3VyXCIpKTtcclxuICAgICAgICAgICAgdGltZSAtPSBhbW91bnQgKiBUSU1FX0hPVVI7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZih0aW1lID4gVElNRV9NSU5VVEUpIHtcclxuICAgICAgICAgICAgY29uc3QgYW1vdW50ID0gTWF0aC5mbG9vcih0aW1lIC8gVElNRV9NSU5VVEUpO1xyXG4gICAgICAgICAgICByZXN1bHQgKz0gXCIgXCIgKyBhbW91bnQgKyBcIiBcIiArIChhbW91bnQgPiAxID8gdHIoXCJNaW51dGVzXCIpIDogdHIoXCJNaW51dGVcIikpO1xyXG4gICAgICAgICAgICB0aW1lIC09IGFtb3VudCAqIFRJTUVfTUlOVVRFO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYodGltZSA+IFRJTUVfU0VDT05EKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGFtb3VudCA9IE1hdGguZmxvb3IodGltZSAvIFRJTUVfU0VDT05EKTtcclxuICAgICAgICAgICAgcmVzdWx0ICs9IFwiIFwiICsgYW1vdW50ICsgXCIgXCIgKyAoYW1vdW50ID4gMSA/IHRyKFwiU2Vjb25kc1wiKSA6IHRyKFwiU2Vjb25kXCIpKTtcclxuICAgICAgICAgICAgdGltZSAtPSBhbW91bnQgKiBUSU1FX1NFQ09ORDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiByZXN1bHQubGVuZ3RoID4gMCA/IHJlc3VsdC5zdWJzdHJpbmcoMSkgOiBkZWZhdWx0X3ZhbHVlO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBfaWNvbl9zaXplX3N0eWxlOiBKUXVlcnk8SFRNTFN0eWxlRWxlbWVudD47XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gc2V0X2ljb25fc2l6ZShzaXplOiBzdHJpbmcpIHtcclxuICAgICAgICBpZighX2ljb25fc2l6ZV9zdHlsZSlcclxuICAgICAgICAgICAgX2ljb25fc2l6ZV9zdHlsZSA9ICQuc3Bhd24oXCJzdHlsZVwiKS5hcHBlbmRUbygkKFwiI3N0eWxlXCIpKTtcclxuXHJcbiAgICAgICAgX2ljb25fc2l6ZV9zdHlsZS50ZXh0KFwiXFxuXCIgK1xyXG4gICAgICAgICAgICBcIi5tZXNzYWdlID4gLmVtb2ppIHtcXG5cIiArXHJcbiAgICAgICAgICAgIFwiICBoZWlnaHQ6IFwiICsgc2l6ZSArIFwiIWltcG9ydGFudDtcXG5cIiArXHJcbiAgICAgICAgICAgIFwiICB3aWR0aDogXCIgKyBzaXplICsgXCIhaW1wb3J0YW50O1xcblwiICtcclxuICAgICAgICAgICAgXCJ9XFxuXCJcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgICAgIG5hbWU6IFwiaWNvbiBzaXplIGluaXRcIixcclxuICAgICAgICBmdW5jdGlvbjogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAgICAgICBNZXNzYWdlSGVscGVyLnNldF9pY29uX3NpemUoKHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0lDT05fU0laRSkgLyAxMDApLnRvRml4ZWQoMikgKyBcImVtXCIpO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgcHJpb3JpdHk6IDEwXHJcbiAgICB9KTtcclxufSIsIi8vRklYTUU6IE1vdmUgdGhpcyBzaGl0IG91dCBvZiB0aGlzIGZpbGUhXHJcbmV4cG9ydCBuYW1lc3BhY2UgY29ubmVjdGlvbl9sb2cge1xyXG4gICAgLy9UT0RPOiBTYXZlIHBhc3N3b3JkIGRhdGFcclxuICAgIGV4cG9ydCB0eXBlIENvbm5lY3Rpb25EYXRhID0ge1xyXG4gICAgICAgIG5hbWU6IHN0cmluZztcclxuICAgICAgICBpY29uX2lkOiBudW1iZXI7XHJcbiAgICAgICAgY291bnRyeTogc3RyaW5nO1xyXG4gICAgICAgIGNsaWVudHNfb25saW5lOiBudW1iZXI7XHJcbiAgICAgICAgY2xpZW50c190b3RhbDogbnVtYmVyO1xyXG5cclxuICAgICAgICBmbGFnX3Bhc3N3b3JkOiBib29sZWFuO1xyXG4gICAgICAgIHBhc3N3b3JkX2hhc2g6IHN0cmluZztcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgdHlwZSBDb25uZWN0aW9uRW50cnkgPSBDb25uZWN0aW9uRGF0YSAmIHtcclxuICAgICAgICBhZGRyZXNzOiB7IGhvc3RuYW1lOiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9LFxyXG4gICAgICAgIHRvdGFsX2Nvbm5lY3Rpb246IG51bWJlcjtcclxuXHJcbiAgICAgICAgZmlyc3RfdGltZXN0YW1wOiBudW1iZXI7XHJcbiAgICAgICAgbGFzdF90aW1lc3RhbXA6IG51bWJlcjtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgX2hpc3Rvcnk6IENvbm5lY3Rpb25FbnRyeVtdID0gW107XHJcbiAgICBleHBvcnQgZnVuY3Rpb24gbG9nX2Nvbm5lY3QoYWRkcmVzczogeyBob3N0bmFtZTogc3RyaW5nOyBwb3J0OiBudW1iZXIgfSkge1xyXG4gICAgICAgIGxldCBlbnRyeSA9IF9oaXN0b3J5LmZpbmQoZSA9PiBlLmFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSA9PSBhZGRyZXNzLmhvc3RuYW1lLnRvTG93ZXJDYXNlKCkgJiYgZS5hZGRyZXNzLnBvcnQgPT0gYWRkcmVzcy5wb3J0KTtcclxuICAgICAgICBpZighZW50cnkpIHtcclxuICAgICAgICAgICAgX2hpc3RvcnkucHVzaChlbnRyeSA9IHtcclxuICAgICAgICAgICAgICAgIGxhc3RfdGltZXN0YW1wOiBEYXRlLm5vdygpLFxyXG4gICAgICAgICAgICAgICAgZmlyc3RfdGltZXN0YW1wOiBEYXRlLm5vdygpLFxyXG4gICAgICAgICAgICAgICAgYWRkcmVzczogYWRkcmVzcyxcclxuICAgICAgICAgICAgICAgIGNsaWVudHNfb25saW5lOiAwLFxyXG4gICAgICAgICAgICAgICAgY2xpZW50c190b3RhbDogMCxcclxuICAgICAgICAgICAgICAgIGNvdW50cnk6ICd1bmtub3duJyxcclxuICAgICAgICAgICAgICAgIG5hbWU6ICdVbmtub3duJyxcclxuICAgICAgICAgICAgICAgIGljb25faWQ6IDAsXHJcbiAgICAgICAgICAgICAgICB0b3RhbF9jb25uZWN0aW9uOiAwLFxyXG5cclxuICAgICAgICAgICAgICAgIGZsYWdfcGFzc3dvcmQ6IGZhbHNlLFxyXG4gICAgICAgICAgICAgICAgcGFzc3dvcmRfaGFzaDogdW5kZWZpbmVkXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbnRyeS5sYXN0X3RpbWVzdGFtcCA9IERhdGUubm93KCk7XHJcbiAgICAgICAgZW50cnkudG90YWxfY29ubmVjdGlvbisrO1xyXG4gICAgICAgIF9zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZV9hZGRyZXNzX2luZm8oYWRkcmVzczogeyBob3N0bmFtZTogc3RyaW5nOyBwb3J0OiBudW1iZXIgfSwgZGF0YTogQ29ubmVjdGlvbkRhdGEpIHtcclxuICAgICAgICBfaGlzdG9yeS5maWx0ZXIoZSA9PiBlLmFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSA9PSBhZGRyZXNzLmhvc3RuYW1lLnRvTG93ZXJDYXNlKCkgJiYgZS5hZGRyZXNzLnBvcnQgPT0gYWRkcmVzcy5wb3J0KS5mb3JFYWNoKGUgPT4ge1xyXG4gICAgICAgICAgICBmb3IoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGRhdGEpKSB7XHJcbiAgICAgICAgICAgICAgICBpZih0eXBlb2YoZGF0YVtrZXldKSAhPT0gXCJ1bmRlZmluZWRcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGVba2V5XSA9IGRhdGFba2V5XTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIF9zYXZlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZV9hZGRyZXNzX3Bhc3N3b3JkKGFkZHJlc3M6IHsgaG9zdG5hbWU6IHN0cmluZzsgcG9ydDogbnVtYmVyIH0sIHBhc3N3b3JkX2hhc2g6IHN0cmluZykge1xyXG4gICAgICAgIF9oaXN0b3J5LmZpbHRlcihlID0+IGUuYWRkcmVzcy5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpID09IGFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSAmJiBlLmFkZHJlc3MucG9ydCA9PSBhZGRyZXNzLnBvcnQpLmZvckVhY2goZSA9PiB7XHJcbiAgICAgICAgICAgIGUucGFzc3dvcmRfaGFzaCA9IHBhc3N3b3JkX2hhc2g7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgICAgX3NhdmUoKTtcclxuICAgIH1cclxuXHJcbiAgICBmdW5jdGlvbiBfc2F2ZSgpIHtcclxuICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfSElTVE9SWSwgSlNPTi5zdHJpbmdpZnkoX2hpc3RvcnkpKTtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgZnVuY3Rpb24gaGlzdG9yeSgpIDogQ29ubmVjdGlvbkVudHJ5W10ge1xyXG4gICAgICAgIHJldHVybiBfaGlzdG9yeS5zb3J0KChhLCBiKSA9PiBiLmxhc3RfdGltZXN0YW1wIC0gYS5sYXN0X3RpbWVzdGFtcCk7XHJcbiAgICB9XHJcblxyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIGRlbGV0ZV9lbnRyeShhZGRyZXNzOiB7IGhvc3RuYW1lOiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9KSB7XHJcbiAgICAgICAgX2hpc3RvcnkgPSBfaGlzdG9yeS5maWx0ZXIoZSA9PiAhKGUuYWRkcmVzcy5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpID09IGFkZHJlc3MuaG9zdG5hbWUudG9Mb3dlckNhc2UoKSAmJiBlLmFkZHJlc3MucG9ydCA9PSBhZGRyZXNzLnBvcnQpKTtcclxuICAgICAgICBfc2F2ZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIGxvYWRlci5yZWdpc3Rlcl90YXNrKGxvYWRlci5TdGFnZS5KQVZBU0NSSVBUX0lOSVRJQUxJWklORywge1xyXG4gICAgICAgIG5hbWU6ICdjb25uZWN0aW9uIGhpc3RvcnkgbG9hZCcsXHJcbiAgICAgICAgcHJpb3JpdHk6IDEsXHJcbiAgICAgICAgZnVuY3Rpb246IGFzeW5jICgpID0+IHtcclxuICAgICAgICAgICAgX2hpc3RvcnkgPSBbXTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIF9oaXN0b3J5ID0gSlNPTi5wYXJzZShzZXR0aW5ncy5nbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfSElTVE9SWSwgXCJbXVwiKSk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIGxvZy53YXJuKExvZ0NhdGVnb3J5LkNMSUVOVCwgdHIoXCJGYWlsZWQgdG8gbG9hZCBjb25uZWN0aW9uIGhpc3Rvcnk6IHt9XCIpLCBlcnJvcik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9KTtcclxufVxyXG5cclxuZXhwb3J0IG5hbWVzcGFjZSBNb2RhbHMge1xyXG4gICAgZXhwb3J0IGZ1bmN0aW9uIHNwYXduQ29ubmVjdE1vZGFsKG9wdGlvbnM6IHtcclxuICAgICAgICBkZWZhdWx0X2Nvbm5lY3RfbmV3X3RhYj86IGJvb2xlYW4gLyogZGVmYXVsdCBmYWxzZSAqL1xyXG4gICAgfSwgZGVmYXVsdEhvc3Q6IHsgdXJsOiBzdHJpbmcsIGVuZm9yY2U6IGJvb2xlYW59ID0geyB1cmw6IFwidHMuVGVhU3BlYWsuZGVcIiwgZW5mb3JjZTogZmFsc2V9LCBjb25uZWN0X3Byb2ZpbGU/OiB7IHByb2ZpbGU6IHByb2ZpbGVzLkNvbm5lY3Rpb25Qcm9maWxlLCBlbmZvcmNlOiBib29sZWFufSkge1xyXG4gICAgICAgIGxldCBzZWxlY3RlZF9wcm9maWxlOiBwcm9maWxlcy5Db25uZWN0aW9uUHJvZmlsZTtcclxuXHJcbiAgICAgICAgY29uc3QgcmFuZG9tX2lkID0gKCgpID0+IHtcclxuICAgICAgICAgICAgY29uc3QgYXJyYXkgPSBuZXcgVWludDMyQXJyYXkoMTApO1xyXG4gICAgICAgICAgICB3aW5kb3cuY3J5cHRvLmdldFJhbmRvbVZhbHVlcyhhcnJheSk7XHJcbiAgICAgICAgICAgIHJldHVybiBhcnJheS5qb2luKFwiXCIpO1xyXG4gICAgICAgIH0pKCk7XHJcblxyXG4gICAgICAgIGNvbnN0IG1vZGFsID0gY3JlYXRlTW9kYWwoe1xyXG4gICAgICAgICAgICBoZWFkZXI6IHRyKFwiQ29ubmVjdCB0byBhIHNlcnZlclwiKSxcclxuICAgICAgICAgICAgYm9keTogJChcIiN0bXBsX2Nvbm5lY3RcIikucmVuZGVyVGFnKHtcclxuICAgICAgICAgICAgICAgIGNsaWVudDogbmF0aXZlX2NsaWVudCxcclxuICAgICAgICAgICAgICAgIGZvcnVtX3BhdGg6IHNldHRpbmdzLnN0YXRpYyhcImZvcnVtX3BhdGhcIiksXHJcbiAgICAgICAgICAgICAgICBwYXNzd29yZF9pZDogcmFuZG9tX2lkLFxyXG4gICAgICAgICAgICAgICAgbXVsdGlfdGFiOiAhc2V0dGluZ3Muc3RhdGljX2dsb2JhbChTZXR0aW5ncy5LRVlfRElTQUJMRV9NVUxUSV9TRVNTSU9OKSxcclxuICAgICAgICAgICAgICAgIGRlZmF1bHRfY29ubmVjdF9uZXdfdGFiOiB0eXBlb2Yob3B0aW9ucy5kZWZhdWx0X2Nvbm5lY3RfbmV3X3RhYikgPT09IFwiYm9vbGVhblwiICYmIG9wdGlvbnMuZGVmYXVsdF9jb25uZWN0X25ld190YWJcclxuICAgICAgICAgICAgfSksXHJcbiAgICAgICAgICAgIGZvb3RlcjogKCkgPT4gdW5kZWZpbmVkLFxyXG4gICAgICAgICAgICBtaW5fd2lkdGg6IFwiMjhlbVwiXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIG1vZGFsLmh0bWxUYWcuZmluZChcIi5tb2RhbC1ib2R5XCIpLmFkZENsYXNzKFwibW9kYWwtY29ubmVjdFwiKTtcclxuXHJcbiAgICAgICAgLyogc2VydmVyIGxpc3QgdG9nZ2xlICovXHJcbiAgICAgICAge1xyXG4gICAgICAgICAgICBjb25zdCBjb250YWluZXJfbGFzdF9zZXJ2ZXJzID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci1sYXN0LXNlcnZlcnNcIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGJ1dHRvbiA9IG1vZGFsLmh0bWxUYWcuZmluZChcIi5idXR0b24tdG9nZ2xlLWxhc3Qtc2VydmVyc1wiKTtcclxuICAgICAgICAgICAgY29uc3Qgc2V0X3Nob3cgPSBzaG93biA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb250YWluZXJfbGFzdF9zZXJ2ZXJzLnRvZ2dsZUNsYXNzKCdzaG93bicsIHNob3duKTtcclxuICAgICAgICAgICAgICAgIGJ1dHRvbi5maW5kKFwiLmFycm93XCIpLnRvZ2dsZUNsYXNzKCdkb3duJywgc2hvd24pLnRvZ2dsZUNsYXNzKCd1cCcsICFzaG93bik7XHJcbiAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoXCJjb25uZWN0X3Nob3dfbGFzdF9zZXJ2ZXJzXCIsIHNob3duKTtcclxuICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgYnV0dG9uLm9uKCdjbGljaycsIGV2ZW50ID0+IHtcclxuICAgICAgICAgICAgICAgIHNldF9zaG93KCFjb250YWluZXJfbGFzdF9zZXJ2ZXJzLmhhc0NsYXNzKFwic2hvd25cIikpO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgc2V0X3Nob3coc2V0dGluZ3Muc3RhdGljX2dsb2JhbChcImNvbm5lY3Rfc2hvd19sYXN0X3NlcnZlcnNcIiwgZmFsc2UpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IGFwcGx5ID0gKGhlYWRlciwgYm9keSwgZm9vdGVyKSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IGNvbnRhaW5lcl9sYXN0X3NlcnZlcl9ib2R5ID0gbW9kYWwuaHRtbFRhZy5maW5kKFwiLmNvbnRhaW5lci1sYXN0LXNlcnZlcnMgLnRhYmxlIC5ib2R5XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBjb250YWluZXJfZW1wdHkgPSBjb250YWluZXJfbGFzdF9zZXJ2ZXJfYm9keS5maW5kKFwiLmJvZHktZW1wdHlcIik7XHJcbiAgICAgICAgICAgIGxldCBjdXJyZW50X2Nvbm5lY3RfZGF0YTogY29ubmVjdGlvbl9sb2cuQ29ubmVjdGlvbkVudHJ5O1xyXG5cclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX2Nvbm5lY3QgPSBmb290ZXIuZmluZChcIi5idXR0b24tY29ubmVjdFwiKTtcclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX2Nvbm5lY3RfdGFiID0gZm9vdGVyLmZpbmQoXCIuYnV0dG9uLWNvbm5lY3QtbmV3LXRhYlwiKTtcclxuICAgICAgICAgICAgY29uc3QgYnV0dG9uX21hbmFnZSA9IGJvZHkuZmluZChcIi5idXR0b24tbWFuYWdlLXByb2ZpbGVzXCIpO1xyXG5cclxuICAgICAgICAgICAgY29uc3QgaW5wdXRfcHJvZmlsZSA9IGJvZHkuZmluZChcIi5jb250YWluZXItc2VsZWN0LXByb2ZpbGUgc2VsZWN0XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBpbnB1dF9hZGRyZXNzID0gYm9keS5maW5kKFwiLmNvbnRhaW5lci1hZGRyZXNzIGlucHV0XCIpO1xyXG4gICAgICAgICAgICBjb25zdCBpbnB1dF9uaWNrbmFtZSA9IGJvZHkuZmluZChcIi5jb250YWluZXItbmlja25hbWUgaW5wdXRcIik7XHJcbiAgICAgICAgICAgIGNvbnN0IGlucHV0X3Bhc3N3b3JkID0gYm9keS5maW5kKFwiLmNvbnRhaW5lci1wYXNzd29yZCBpbnB1dFwiKTtcclxuXHJcbiAgICAgICAgICAgIGxldCB1cGRhdGVGaWVsZHMgPSAocmVzZXRfY3VycmVudF9kYXRhOiBib29sZWFuKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZihyZXNldF9jdXJyZW50X2RhdGEpIHtcclxuICAgICAgICAgICAgICAgICAgICBjdXJyZW50X2Nvbm5lY3RfZGF0YSA9IHVuZGVmaW5lZDtcclxuICAgICAgICAgICAgICAgICAgICBjb250YWluZXJfbGFzdF9zZXJ2ZXJfYm9keS5maW5kKFwiLnNlbGVjdGVkXCIpLnJlbW92ZUNsYXNzKFwic2VsZWN0ZWRcIik7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgbGV0IGFkZHJlc3MgPSBpbnB1dF9hZGRyZXNzLnZhbCgpLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfQUREUkVTUywgYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICBsZXQgZmxhZ19hZGRyZXNzID0gISFhZGRyZXNzLm1hdGNoKFJlZ2V4LklQX1Y0KSB8fCAhIWFkZHJlc3MubWF0Y2goUmVnZXguSVBfVjYpIHx8ICEhYWRkcmVzcy5tYXRjaChSZWdleC5ET01BSU4pO1xyXG5cclxuICAgICAgICAgICAgICAgIGxldCBuaWNrbmFtZSA9IGlucHV0X25pY2tuYW1lLnZhbCgpLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICAgICAgICBpZihuaWNrbmFtZSlcclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfVVNFUk5BTUUsIG5pY2tuYW1lKTtcclxuICAgICAgICAgICAgICAgIGVsc2VcclxuICAgICAgICAgICAgICAgICAgICBuaWNrbmFtZSA9IGlucHV0X25pY2tuYW1lLmF0dHIoXCJwbGFjZWhvbGRlclwiKSB8fCBcIlwiO1xyXG4gICAgICAgICAgICAgICAgbGV0IGZsYWdfbmlja25hbWUgPSBuaWNrbmFtZS5sZW5ndGggPj0gMyAmJiBuaWNrbmFtZS5sZW5ndGggPD0gMzI7XHJcblxyXG4gICAgICAgICAgICAgICAgaW5wdXRfYWRkcmVzcy5hdHRyKCdwYXR0ZXJuJywgZmxhZ19hZGRyZXNzID8gbnVsbCA6ICdeW2FdezEwMDB9JCcpLnRvZ2dsZUNsYXNzKCdpcy1pbnZhbGlkJywgIWZsYWdfYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICBpbnB1dF9uaWNrbmFtZS5hdHRyKCdwYXR0ZXJuJywgZmxhZ19uaWNrbmFtZSA/IG51bGwgOiAnXlthXXsxMDAwfSQnKS50b2dnbGVDbGFzcygnaXMtaW52YWxpZCcsICFmbGFnX25pY2tuYW1lKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBmbGFnX2Rpc2FibGVkID0gIWZsYWdfbmlja25hbWUgfHwgIWZsYWdfYWRkcmVzcyB8fCAhc2VsZWN0ZWRfcHJvZmlsZSB8fCAhc2VsZWN0ZWRfcHJvZmlsZS52YWxpZCgpO1xyXG4gICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3QucHJvcChcImRpc2FibGVkXCIsIGZsYWdfZGlzYWJsZWQpO1xyXG4gICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3RfdGFiLnByb3AoXCJkaXNhYmxlZFwiLCBmbGFnX2Rpc2FibGVkKTtcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIGlucHV0X2FkZHJlc3MudmFsKGRlZmF1bHRIb3N0LmVuZm9yY2UgPyBkZWZhdWx0SG9zdC51cmwgOiBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX0FERFJFU1MsIGRlZmF1bHRIb3N0LnVybCkpO1xyXG4gICAgICAgICAgICBpbnB1dF9hZGRyZXNzXHJcbiAgICAgICAgICAgICAgICAub24oXCJrZXl1cFwiLCAoKSA9PiB1cGRhdGVGaWVsZHModHJ1ZSkpXHJcbiAgICAgICAgICAgICAgICAub24oJ2tleWRvd24nLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYoZXZlbnQua2V5Q29kZSA9PSBLZXlDb2RlLktFWV9FTlRFUiAmJiAhZXZlbnQuc2hpZnRLZXkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbl9jb25uZWN0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgYnV0dG9uX21hbmFnZS5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBjb25zdCBtb2RhbCA9IE1vZGFscy5zcGF3blNldHRpbmdzTW9kYWwoXCJpZGVudGl0eS1wcm9maWxlc1wiKTtcclxuICAgICAgICAgICAgICAgIG1vZGFsLmNsb3NlX2xpc3RlbmVyLnB1c2goKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGlucHV0X3Byb2ZpbGUudHJpZ2dlcignY2hhbmdlJyk7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgIC8qIENvbm5lY3QgUHJvZmlsZXMgKi9cclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgZm9yKGNvbnN0IHByb2ZpbGUgb2YgcHJvZmlsZXMucHJvZmlsZXMoKSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGlucHV0X3Byb2ZpbGUuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwib3B0aW9uXCIpLnRleHQocHJvZmlsZS5wcm9maWxlX25hbWUpLnZhbChwcm9maWxlLmlkKVxyXG4gICAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgaW5wdXRfcHJvZmlsZS5vbignY2hhbmdlJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkX3Byb2ZpbGUgPSBwcm9maWxlcy5maW5kX3Byb2ZpbGUoaW5wdXRfcHJvZmlsZS52YWwoKSBhcyBzdHJpbmcpIHx8IHByb2ZpbGVzLmRlZmF1bHRfcHJvZmlsZSgpO1xyXG4gICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgc2V0dGluZ3MuY2hhbmdlR2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX1VTRVJOQU1FLCB1bmRlZmluZWQpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9uaWNrbmFtZVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLmF0dHIoJ3BsYWNlaG9sZGVyJywgc2VsZWN0ZWRfcHJvZmlsZS5jb25uZWN0X3VzZXJuYW1lKCkgfHwgXCJBbm90aGVyIFRlYVNwZWFrIHVzZXJcIilcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC52YWwoXCJcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5jaGFuZ2VHbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfUFJPRklMRSwgc2VsZWN0ZWRfcHJvZmlsZS5pZCk7XHJcbiAgICAgICAgICAgICAgICAgICAgaW5wdXRfcHJvZmlsZS50b2dnbGVDbGFzcyhcImlzLWludmFsaWRcIiwgIXNlbGVjdGVkX3Byb2ZpbGUgfHwgIXNlbGVjdGVkX3Byb2ZpbGUudmFsaWQoKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgdXBkYXRlRmllbGRzKHRydWUpO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICBpbnB1dF9wcm9maWxlLnZhbChjb25uZWN0X3Byb2ZpbGUgJiYgY29ubmVjdF9wcm9maWxlLnByb2ZpbGUgP1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3RfcHJvZmlsZS5wcm9maWxlLmlkIDpcclxuICAgICAgICAgICAgICAgICAgICBzZXR0aW5ncy5zdGF0aWNfZ2xvYmFsKFNldHRpbmdzLktFWV9DT05ORUNUX1BST0ZJTEUsIFwiZGVmYXVsdFwiKVxyXG4gICAgICAgICAgICAgICAgKS50cmlnZ2VyKCdjaGFuZ2UnKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgY29uc3QgbGFzdF9uaWNrbmFtZSA9IHNldHRpbmdzLnN0YXRpY19nbG9iYWwoU2V0dGluZ3MuS0VZX0NPTk5FQ1RfVVNFUk5BTUUsIHVuZGVmaW5lZCk7XHJcbiAgICAgICAgICAgIGlmKGxhc3Rfbmlja25hbWUpIC8qIHJlc3RvcmUgKi9cclxuICAgICAgICAgICAgICAgIHNldHRpbmdzLmNoYW5nZUdsb2JhbChTZXR0aW5ncy5LRVlfQ09OTkVDVF9VU0VSTkFNRSwgbGFzdF9uaWNrbmFtZSk7XHJcblxyXG4gICAgICAgICAgICBpbnB1dF9uaWNrbmFtZS52YWwobGFzdF9uaWNrbmFtZSk7XHJcbiAgICAgICAgICAgIGlucHV0X25pY2tuYW1lLm9uKFwia2V5dXBcIiwgKCkgPT4gdXBkYXRlRmllbGRzKHRydWUpKTtcclxuICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB1cGRhdGVGaWVsZHMoZmFsc2UpLCAxMDApO1xyXG5cclxuICAgICAgICAgICAgY29uc3Qgc2VydmVyX2FkZHJlc3MgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBsZXQgYWRkcmVzcyA9IGlucHV0X2FkZHJlc3MudmFsKCkudG9TdHJpbmcoKTtcclxuICAgICAgICAgICAgICAgIGlmKGFkZHJlc3MubWF0Y2goUmVnZXguSVBfVjYpICYmICFhZGRyZXNzLnN0YXJ0c1dpdGgoXCJbXCIpKVxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIltcIiArIGFkZHJlc3MgKyBcIl1cIjtcclxuICAgICAgICAgICAgICAgIHJldHVybiBhZGRyZXNzO1xyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICBidXR0b25fY29ubmVjdC5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICBtb2RhbC5jbG9zZSgpO1xyXG5cclxuICAgICAgICAgICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBzZXJ2ZXJfY29ubmVjdGlvbnMuYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcigpO1xyXG4gICAgICAgICAgICAgICAgaWYoY29ubmVjdGlvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uc3RhcnRDb25uZWN0aW9uKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50X2Nvbm5lY3RfZGF0YSA/IGN1cnJlbnRfY29ubmVjdF9kYXRhLmFkZHJlc3MuaG9zdG5hbWUgKyBcIjpcIiArIGN1cnJlbnRfY29ubmVjdF9kYXRhLmFkZHJlc3MucG9ydCA6IHNlcnZlcl9hZGRyZXNzKCksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGVkX3Byb2ZpbGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5pY2tuYW1lOiBpbnB1dF9uaWNrbmFtZS52YWwoKS50b1N0cmluZygpIHx8ICBpbnB1dF9uaWNrbmFtZS5hdHRyKFwicGxhY2Vob2xkZXJcIiksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXNzd29yZDogKGN1cnJlbnRfY29ubmVjdF9kYXRhICYmIGN1cnJlbnRfY29ubmVjdF9kYXRhLnBhc3N3b3JkX2hhc2gpID8ge3Bhc3N3b3JkOiBjdXJyZW50X2Nvbm5lY3RfZGF0YS5wYXNzd29yZF9oYXNoLCBoYXNoZWQ6IHRydWV9IDoge3Bhc3N3b3JkOiBpbnB1dF9wYXNzd29yZC52YWwoKS50b1N0cmluZygpLCBoYXNoZWQ6IGZhbHNlfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgYnV0dG9uX2Nvbm5lY3RfdGFiLnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICBidXR0b25fY29ubmVjdF90YWIub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgbW9kYWwuY2xvc2UoKTtcclxuXHJcbiAgICAgICAgICAgICAgICBjb25zdCBjb25uZWN0aW9uID0gc2VydmVyX2Nvbm5lY3Rpb25zLnNwYXduX3NlcnZlcl9jb25uZWN0aW9uX2hhbmRsZXIoKTtcclxuICAgICAgICAgICAgICAgIHNlcnZlcl9jb25uZWN0aW9ucy5zZXRfYWN0aXZlX2Nvbm5lY3Rpb25faGFuZGxlcihjb25uZWN0aW9uKTtcclxuICAgICAgICAgICAgICAgIGNvbm5lY3Rpb24uc3RhcnRDb25uZWN0aW9uKFxyXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfY29ubmVjdF9kYXRhID8gY3VycmVudF9jb25uZWN0X2RhdGEuYWRkcmVzcy5ob3N0bmFtZSArIFwiOlwiICsgY3VycmVudF9jb25uZWN0X2RhdGEuYWRkcmVzcy5wb3J0IDogIHNlcnZlcl9hZGRyZXNzKCksXHJcbiAgICAgICAgICAgICAgICAgICAgc2VsZWN0ZWRfcHJvZmlsZSxcclxuICAgICAgICAgICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbmlja25hbWU6IGlucHV0X25pY2tuYW1lLnZhbCgpLnRvU3RyaW5nKCkgfHwgIGlucHV0X25pY2tuYW1lLmF0dHIoXCJwbGFjZWhvbGRlclwiKSxcclxuICAgICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6IChjdXJyZW50X2Nvbm5lY3RfZGF0YSAmJiBjdXJyZW50X2Nvbm5lY3RfZGF0YS5wYXNzd29yZF9oYXNoKSA/IHtwYXNzd29yZDogY3VycmVudF9jb25uZWN0X2RhdGEucGFzc3dvcmRfaGFzaCwgaGFzaGVkOiB0cnVlfSA6IHtwYXNzd29yZDogaW5wdXRfcGFzc3dvcmQudmFsKCkudG9TdHJpbmcoKSwgaGFzaGVkOiBmYWxzZX1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9KTtcclxuXHJcblxyXG4gICAgICAgICAgICAvKiBjb25uZWN0IGhpc3Rvcnkgc2hvdyAqL1xyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICBmb3IoY29uc3QgZW50cnkgb2YgY29ubmVjdGlvbl9sb2cuaGlzdG9yeSgpLnNsaWNlKDAsIDEwKSkge1xyXG4gICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJyb3dcIikuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGRlbGV0ZVwiKS5hcHBlbmQoJC5zcGF3bihcImRpdlwiKS5hZGRDbGFzcyhcImljb25fZW0gY2xpZW50LWRlbGV0ZVwiKSkub24oJ2NsaWNrJywgZXZlbnQgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCByb3cgPSAkKGV2ZW50LnRhcmdldCkucGFyZW50cygnLnJvdycpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93LmhpZGUoMjUwLCAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93LmRldGFjaCgpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25uZWN0aW9uX2xvZy5kZWxldGVfZW50cnkoZW50cnkuYWRkcmVzcyk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250YWluZXJfZW1wdHkudG9nZ2xlKGNvbnRhaW5lcl9sYXN0X3NlcnZlcl9ib2R5LmNoaWxkcmVuKCkubGVuZ3RoID4gMSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICAgICAgKS5hcHBlbmQoXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb2x1bW4gbmFtZVwiKS5hcHBlbmQoW1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgSWNvbk1hbmFnZXIuZ2VuZXJhdGVfdGFnKEljb25NYW5hZ2VyLmxvYWRfY2FjaGVkX2ljb24oZW50cnkuaWNvbl9pZCkpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImFcIikudGV4dChlbnRyeS5uYW1lKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBdKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGFkZHJlc3NcIikudGV4dChlbnRyeS5hZGRyZXNzLmhvc3RuYW1lICsgKGVudHJ5LmFkZHJlc3MucG9ydCAhPSA5OTg3ID8gKFwiOlwiICsgZW50cnkuYWRkcmVzcy5wb3J0KSA6IFwiXCIpKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIHBhc3N3b3JkXCIpLnRleHQoZW50cnkuZmxhZ19wYXNzd29yZCA/IHRyKFwiWWVzXCIpIDogdHIoXCJOb1wiKSlcclxuICAgICAgICAgICAgICAgICAgICApLmFwcGVuZChcclxuICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImRpdlwiKS5hZGRDbGFzcyhcImNvbHVtbiBjb3VudHJ5LW5hbWVcIikuYXBwZW5kKFtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb3VudHJ5IGZsYWctXCIgKyBlbnRyeS5jb3VudHJ5LnRvTG93ZXJDYXNlKCkpLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJC5zcGF3bihcImFcIikudGV4dChpMThuLmNvdW50cnlfbmFtZShlbnRyeS5jb3VudHJ5LCB0cihcIkdsb2JhbFwiKSkpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIF0pXHJcbiAgICAgICAgICAgICAgICAgICAgKS5hcHBlbmQoXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICQuc3Bhd24oXCJkaXZcIikuYWRkQ2xhc3MoXCJjb2x1bW4gY2xpZW50c1wiKS50ZXh0KGVudHJ5LmNsaWVudHNfb25saW5lICsgXCIvXCIgKyBlbnRyeS5jbGllbnRzX3RvdGFsKVxyXG4gICAgICAgICAgICAgICAgICAgICkuYXBwZW5kKFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAkLnNwYXduKFwiZGl2XCIpLmFkZENsYXNzKFwiY29sdW1uIGNvbm5lY3Rpb25zXCIpLnRleHQoZW50cnkudG90YWxfY29ubmVjdGlvbiArIFwiXCIpXHJcbiAgICAgICAgICAgICAgICAgICAgKS5vbignY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmKGV2ZW50LmlzRGVmYXVsdFByZXZlbnRlZCgpKVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudF9jb25uZWN0X2RhdGEgPSBlbnRyeTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGFpbmVyX2xhc3Rfc2VydmVyX2JvZHkuZmluZChcIi5zZWxlY3RlZFwiKS5yZW1vdmVDbGFzcyhcInNlbGVjdGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAkKGV2ZW50LnRhcmdldCkucGFyZW50KCcucm93JykuYWRkQ2xhc3MoJ3NlbGVjdGVkJyk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9hZGRyZXNzLnZhbChlbnRyeS5hZGRyZXNzLmhvc3RuYW1lICsgKGVudHJ5LmFkZHJlc3MucG9ydCAhPSA5OTg3ID8gKFwiOlwiICsgZW50cnkuYWRkcmVzcy5wb3J0KSA6IFwiXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXRfcGFzc3dvcmQudmFsKGVudHJ5LmZsYWdfcGFzc3dvcmQgJiYgZW50cnkucGFzc3dvcmRfaGFzaCA/IFwiV29sdmVyaW5ERVYgWWVhaHIhXCIgOiBcIlwiKS50cmlnZ2VyKCdjaGFuZ2UnKTtcclxuICAgICAgICAgICAgICAgICAgICB9KS5vbignZGJsY2xpY2snLCBldmVudCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfY29ubmVjdF9kYXRhID0gZW50cnk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbl9jb25uZWN0LnRyaWdnZXIoJ2NsaWNrJyk7XHJcbiAgICAgICAgICAgICAgICAgICAgfSkuYXBwZW5kVG8oY29udGFpbmVyX2xhc3Rfc2VydmVyX2JvZHkpO1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRhaW5lcl9lbXB0eS50b2dnbGUoZmFsc2UpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfTtcclxuICAgICAgICBhcHBseShtb2RhbC5odG1sVGFnLCBtb2RhbC5odG1sVGFnLCBtb2RhbC5odG1sVGFnKTtcclxuXHJcbiAgICAgICAgbW9kYWwub3BlbigpO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBleHBvcnQgY29uc3QgUmVnZXggPSB7XHJcbiAgICAgICAgLy9ET01BSU48OnBvcnQ+XHJcbiAgICAgICAgRE9NQUlOOiAvXihsb2NhbGhvc3R8KCgoW2EtekEtWjAtOV8tXXswLDYzfVxcLil7MCwyNTN9KT9bYS16QS1aMC05Xy1dezAsNjN9XFwuW2EtekEtWl17Miw2NH0pKSh8Oig2NTUzWzAtNV18NjU1WzAtMl1bMC05XXw2NVswLTRdWzAtOV17Mn18NlswLTRdWzAtOV17M318WzAtNV0/WzAtOV17MSw0Nn0pKSQvLFxyXG4gICAgICAgIC8vSVA8OnBvcnQ+XHJcbiAgICAgICAgSVBfVjQ6IC8oXigoMjVbMC01XXwyWzAtNF1bMC05XXxbMDFdP1swLTldWzAtOV0/KVxcLil7M30oMjVbMC01XXwyWzAtNF1bMC05XXxbMDFdP1swLTldWzAtOV0/KSkofDooNjU1M1swLTVdfDY1NVswLTJdWzAtOV18NjVbMC00XVswLTldezJ9fDZbMC00XVswLTldezN9fFswLTVdP1swLTldezEsNH0pKSQvLFxyXG4gICAgICAgIElQX1Y2OiAvKChbMC05YS1mQS1GXXsxLDR9Oil7Nyw3fVswLTlhLWZBLUZdezEsNH18KFswLTlhLWZBLUZdezEsNH06KXsxLDd9OnwoWzAtOWEtZkEtRl17MSw0fTopezEsNn06WzAtOWEtZkEtRl17MSw0fXwoWzAtOWEtZkEtRl17MSw0fTopezEsNX0oOlswLTlhLWZBLUZdezEsNH0pezEsMn18KFswLTlhLWZBLUZdezEsNH06KXsxLDR9KDpbMC05YS1mQS1GXXsxLDR9KXsxLDN9fChbMC05YS1mQS1GXXsxLDR9Oil7MSwzfSg6WzAtOWEtZkEtRl17MSw0fSl7MSw0fXwoWzAtOWEtZkEtRl17MSw0fTopezEsMn0oOlswLTlhLWZBLUZdezEsNH0pezEsNX18WzAtOWEtZkEtRl17MSw0fTooKDpbMC05YS1mQS1GXXsxLDR9KXsxLDZ9KXw6KCg6WzAtOWEtZkEtRl17MSw0fSl7MSw3fXw6KXxmZTgwOig6WzAtOWEtZkEtRl17MCw0fSl7MCw0fSVbMC05YS16QS1aXXsxLH18OjooZmZmZig6MHsxLDR9KXswLDF9Oil7MCwxfSgoMjVbMC01XXwoMlswLTRdfDF7MCwxfVswLTldKXswLDF9WzAtOV0pXFwuKXszLDN9KDI1WzAtNV18KDJbMC00XXwxezAsMX1bMC05XSl7MCwxfVswLTldKXwoWzAtOWEtZkEtRl17MSw0fTopezEsNH06KCgyNVswLTVdfCgyWzAtNF18MXswLDF9WzAtOV0pezAsMX1bMC05XSlcXC4pezMsM30oMjVbMC01XXwoMlswLTRdfDF7MCwxfVswLTldKXswLDF9WzAtOV0pKS8sXHJcbiAgICAgICAgSVA6IC9eKChbMC05XXxbMS05XVswLTldfDFbMC05XXsyfXwyWzAtNF1bMC05XXwyNVswLTVdKVxcLil7M30oWzAtOV18WzEtOV1bMC05XXwxWzAtOV17Mn18MlswLTRdWzAtOV18MjVbMC01XSkkfF4oKFthLXpBLVpdfFthLXpBLVpdW2EtekEtWjAtOVxcLV0qW2EtekEtWjAtOV0pXFwuKSooW0EtWmEtel18W0EtWmEtel1bQS1aYS16MC05XFwtXSpbQS1aYS16MC05XSkkfF5cXHMqKCgoWzAtOUEtRmEtZl17MSw0fTopezd9KFswLTlBLUZhLWZdezEsNH18OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezZ9KDpbMC05QS1GYS1mXXsxLDR9fCgoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NX0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwyfSl8OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NH0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwzfSl8KCg6WzAtOUEtRmEtZl17MSw0fSk/OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KSl8OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezN9KCgoOlswLTlBLUZhLWZdezEsNH0pezEsNH0pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsMn06KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7Mn0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSw1fSl8KCg6WzAtOUEtRmEtZl17MSw0fSl7MCwzfTooKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKShcXC4oMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKXszfSkpfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXsxfSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDZ9KXwoKDpbMC05QS1GYS1mXXsxLDR9KXswLDR9OigoMjVbMC01XXwyWzAtNF1cXGR8MVxcZFxcZHxbMS05XT9cXGQpKFxcLigyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkpezN9KSl8OikpfCg6KCgoOlswLTlBLUZhLWZdezEsNH0pezEsN30pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsNX06KCgyNVswLTVdfDJbMC00XVxcZHwxXFxkXFxkfFsxLTldP1xcZCkoXFwuKDI1WzAtNV18MlswLTRdXFxkfDFcXGRcXGR8WzEtOV0/XFxkKSl7M30pKXw6KSkpKCUuKyk/XFxzKiQvLFxyXG4gICAgfTtcclxufSJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b30fd67e..da1ca552 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,12 +4,292 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", + "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/generator": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", + "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", + "requires": { + "@babel/types": "^7.9.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-replace-supers": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" + }, + "@babel/helpers": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", + "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/emscripten": { "version": "1.39.2", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz", @@ -768,6 +1048,11 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, + "babylon": { + "version": "7.0.0-beta.47", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", + "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==" + }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -888,8 +1173,7 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, "binary-extensions": { "version": "1.13.1", @@ -1443,7 +1727,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1451,8 +1734,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-support": { "version": "1.1.3", @@ -1548,7 +1830,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -2151,8 +2432,7 @@ "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "encodeurl": { "version": "1.0.2", @@ -2287,8 +2567,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint-scope": { "version": "4.0.3", @@ -3688,6 +3967,11 @@ "globule": "^1.0.0" } }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -4202,6 +4486,11 @@ "which": "^1.2.14" } }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -4351,8 +4640,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.1", @@ -5049,6 +5337,11 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -5083,7 +5376,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, "requires": { "minimist": "^1.2.0" }, @@ -5091,8 +5383,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" } } }, @@ -5208,7 +5499,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5236,8 +5526,12 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" }, "loose-envify": { "version": "1.4.0", @@ -6671,8 +6965,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-root": { "version": "0.1.1", @@ -7104,6 +7397,16 @@ "unpipe": "1.0.0" } }, + "raw-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz", + "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, "react": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", @@ -7677,7 +7980,6 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -7759,8 +8061,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -7984,8 +8285,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-greatest-satisfied-range": { "version": "1.1.0", @@ -8702,6 +9002,11 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -9284,6 +9589,102 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, + "wabt": { + "version": "1.0.0-nightly.20180421", + "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.0-nightly.20180421.tgz", + "integrity": "sha512-bsu9zk672KACjoabONcAS94IS20prRm05IbiIUGfa8eBpRLjWZv8ugocdinV/ONh0mFMfXrVWkvF1/BNtwIfUw==", + "dev": true + }, + "wasm-dce": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz", + "integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==", + "requires": { + "@babel/core": "^7.0.0-beta.39", + "@babel/traverse": "^7.0.0-beta.39", + "@babel/types": "^7.0.0-beta.39", + "babylon": "^7.0.0-beta.39", + "webassembly-interpreter": "0.0.30" + } + }, + "wasm-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wasm-loader/-/wasm-loader-1.3.0.tgz", + "integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==", + "requires": { + "loader-utils": "^1.1.0", + "wasm-dce": "^1.0.0" + } + }, + "wasm2wat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wasm2wat/-/wasm2wat-1.1.0.tgz", + "integrity": "sha512-4+mCqEbDpomTbjtCtOzU/7Gzz0xdSPK2809n/t0H7w38xom6+i62C7u4rMRjla6QANb3YzX0TFYZly+QD8eBfA==", + "requires": { + "chalk": "^3.0.0", + "wabt": "^1.0.12" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "wabt": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.13.tgz", + "integrity": "sha512-nkWPyUjYt+SqPox2mjJF5jrpWv30awdXKdG0OmryzfhtahHBrPz/BGSbakPgcJU2SFjC1s7Mb8MadRhQ6lmqUg==" + } + } + }, + "wast-loader": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/wast-loader/-/wast-loader-1.9.0.tgz", + "integrity": "sha512-5AufW8qi2X/G/qLeuQUgtqvv4bifxWXnhJ9ZgMOC1ZmI4wuytyaIB0uskfa2l/i286xVZFAYgsz1b4YsoaGVcQ==", + "dev": true, + "requires": { + "wabt": "1.0.0-nightly.20180421" + } + }, "wat2wasm": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wat2wasm/-/wat2wasm-1.0.2.tgz", @@ -9672,6 +10073,21 @@ } } }, + "webassembly-floating-point-hex-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz", + "integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==" + }, + "webassembly-interpreter": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz", + "integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==", + "requires": { + "@babel/code-frame": "^7.0.0-beta.36", + "long": "^3.2.0", + "webassembly-floating-point-hex-parser": "0.1.2" + } + }, "webpack": { "version": "4.42.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", diff --git a/package.json b/package.json index 7008ffd1..9c817a91 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "mini-css-extract-plugin": "^0.9.0", "mkdirp": "^0.5.1", "node-sass": "^4.13.1", + "raw-loader": "^4.0.0", "sass": "1.22.10", "sass-loader": "^8.0.2", "sha256": "^0.2.0", @@ -57,6 +58,7 @@ "ts-loader": "^6.2.2", "ttypescript": "^1.5.10", "typescript": "3.6.5", + "wast-loader": "^1.9.0", "wat2wasm": "^1.0.2", "webpack": "^4.42.1", "webpack-bundle-analyzer": "^3.6.1", @@ -76,6 +78,8 @@ "@types/fs-extra": "^8.0.1", "circular-dependency-plugin": "^5.2.0", "react": "^16.13.1", - "react-dom": "^16.13.1" + "react-dom": "^16.13.1", + "wasm-loader": "^1.3.0", + "wasm2wat": "^1.1.0" } } diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts index 9c70f1f0..06a0c9e2 100644 --- a/shared/js/profiles/identities/TeamSpeakIdentity.ts +++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts @@ -279,9 +279,10 @@ class IdentityPOWWorker { private _worker: Worker; private _current_hash: string; private _best_level: number; + private _initialized = false; async initialize(key: string) { - this._worker = new Worker(settings.static("worker_directory", "js/workers/") + "WorkerPOW.js"); + this._worker = new Worker("tc-shared/workers/pow", { type: "module" }); /* initialize */ await new Promise((resolve, reject) => { @@ -309,6 +310,7 @@ class IdentityPOWWorker { reject("Failed to load worker (" + event.message + ")"); }; }); + this._initialized = true; /* set data */ await new Promise((resolve, reject) => { @@ -389,35 +391,37 @@ class IdentityPOWWorker { } async finalize(timeout?: number) { - try { - await new Promise((resolve, reject) => { - this._worker.postMessage({ - type: "finalize", - code: "finalize" + if(this._initialized) { + try { + await new Promise((resolve, reject) => { + this._worker.postMessage({ + type: "finalize", + code: "finalize" + }); + + const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); + + this._worker.onmessage = event => { + this._worker.onmessage = event => this.handle_message(event.data); + + clearTimeout(timeout_id); + + if (!event.data) { + reject("invalid data"); + return; + } + + if (!event.data.success) { + reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); + return; + } + + resolve(); + }; }); - - const timeout_id = setTimeout(() => reject("timeout"), timeout || 250); - - this._worker.onmessage = event => { - this._worker.onmessage = event => this.handle_message(event.data); - - clearTimeout(timeout_id); - - if (!event.data) { - reject("invalid data"); - return; - } - - if (!event.data.success) { - reject("failed to finalize (" + event.data.success + " | " + (event.data.message || "unknown eroror") + ")"); - return; - } - - resolve(); - }; - }); - } catch(error) { - log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } catch(error) { + log.error(LogCategory.IDENTITIES, tr("Failed to finalize POW worker! (%o)"), error); + } } this._worker.terminate(); @@ -652,113 +656,116 @@ export class TeaSpeakIdentity implements Identity { return current_hash; }; - { /* init */ - const initialize_promise: Promise[] = []; - for(let index = 0; index < threads; index++) { - const worker = new IdentityPOWWorker(); - workers.push(worker); - initialize_promise.push(worker.initialize(this.public_key)); + try { + { /* init */ + const initialize_promise: Promise[] = []; + for (let index = 0; index < threads; index++) { + const worker = new IdentityPOWWorker(); + workers.push(worker); + initialize_promise.push(worker.initialize(this.public_key)); + } + + try { + await Promise.all(initialize_promise); + } catch (error) { + log.error(LogCategory.IDENTITIES, error); + throw "failed to initialize"; + } } + let result = false; + let best_level = 0; + let target_level = target > 0 ? target : await this.level() + 1; + + const worker_promise: Promise[] = []; + + const hash_timestamps: number[] = []; + let last_hashrate_update: number = 0; + + const update_hashrate = () => { + if (!callback_status) return; + const now = Date.now(); + hash_timestamps.push(now); + + if (last_hashrate_update + 1000 < now) { + last_hashrate_update = now; + + const timeout = now - 10 * 1000; /* 10s */ + const rounds = hash_timestamps.filter(e => e > timeout); + callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) + } + }; + try { - await Promise.all(initialize_promise); - } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to initialize"; - } - } + result = await new Promise((resolve, reject) => { + let active = true; - let result = false; - let best_level = 0; - let target_level = target > 0 ? target : await this.level() + 1; - - const worker_promise: Promise[] = []; - - const hash_timestamps: number[] = []; - let last_hashrate_update: number = 0; - - const update_hashrate = () => { - if(!callback_status) return; - const now = Date.now(); - hash_timestamps.push(now); - - if(last_hashrate_update + 1000 < now) { - last_hashrate_update = now; - - const timeout = now - 10 * 1000; /* 10s */ - const rounds = hash_timestamps.filter(e => e > timeout); - callback_status(Math.ceil((rounds.length * iterations) / Math.ceil((now - rounds[0]) / 1000))) - } - }; - - try { - result = await new Promise((resolve, reject) => { - let active = true; - - const exit = () => { - const timeout = setTimeout(() => resolve(true), 1000); - Promise.all(worker_promise).then(result => { - clearTimeout(timeout); - resolve(true); - }).catch(error => resolve(true)); - active = false; - }; - - for(const worker of workers) { - const worker_mine = () => { - if(!active) return; - - const promise = worker.mine(next_hash(), iterations, target_level); - const p = promise.then(result => { - update_hashrate(); - - worker_promise.remove(p); - - if(result.valueOf()) { - if(worker.current_level() > best_level) { - this.hash_number = worker.current_hash(); - - log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); - best_level = worker.current_level(); - if(callback_level) - callback_level(best_level); - } - - if(active) { - if(target > 0) - exit(); - else - target_level = best_level + 1; - } - } - - if(active && (active = active_callback())) - setTimeout(() => worker_mine(), 0); - else { - exit(); - } - - return Promise.resolve(); - }).catch(error => { - worker_promise.remove(p); - - log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); - reject(error); - - return Promise.resolve(); - }); - - worker_promise.push(p); + const exit = () => { + const timeout = setTimeout(() => resolve(true), 1000); + Promise.all(worker_promise).then(result => { + clearTimeout(timeout); + resolve(true); + }).catch(error => resolve(true)); + active = false; }; - worker_mine(); - } - }); - } catch(error) { - //error already printed before reject had been called - } + for (const worker of workers) { + const worker_mine = () => { + if (!active) return; - { /* shutdown */ + const promise = worker.mine(next_hash(), iterations, target_level); + const p = promise.then(result => { + update_hashrate(); + + worker_promise.remove(p); + + if (result.valueOf()) { + if (worker.current_level() > best_level) { + this.hash_number = worker.current_hash(); + + log.info(LogCategory.IDENTITIES, "Found new best at %s (%d). Old was %d", this.hash_number, worker.current_level(), best_level); + best_level = worker.current_level(); + if (callback_level) + callback_level(best_level); + } + + if (active) { + if (target > 0) + exit(); + else + target_level = best_level + 1; + } + } + + if (active && (active = active_callback())) + setTimeout(() => worker_mine(), 0); + else { + exit(); + } + + return Promise.resolve(); + }).catch(error => { + worker_promise.remove(p); + + log.warn(LogCategory.IDENTITIES, "POW worker error %o", error); + reject(error); + + return Promise.resolve(); + }); + + worker_promise.push(p); + }; + + worker_mine(); + } + }); + } catch (error) { + //error already printed before reject had been called + } + + return result; + } finally { + /* shutdown */ const finalize_promise: Promise[] = []; for(const worker of workers) finalize_promise.push(worker.finalize(250)); @@ -766,13 +773,10 @@ export class TeaSpeakIdentity implements Identity { try { await Promise.all(finalize_promise); } catch(error) { - log.error(LogCategory.IDENTITIES, error); - throw "failed to finalize"; + log.error(LogCategory.IDENTITIES, "Failed to shutdown worker: %o", error); } } - - - return result; + throw "this should never be reached"; } private async initialize() { @@ -783,7 +787,7 @@ export class TeaSpeakIdentity implements Identity { try { jwk = await CryptoHelper.decode_tomcrypt_key(this.private_key); if(!jwk) - throw "result undefined"; + throw tr("result undefined"); } catch(error) { throw "failed to parse key (" + error + ")"; } diff --git a/shared/js/workers/pow/POWWorker.ts b/shared/js/workers/pow/index.ts similarity index 80% rename from shared/js/workers/pow/POWWorker.ts rename to shared/js/workers/pow/index.ts index 6a743032..262b9490 100644 --- a/shared/js/workers/pow/POWWorker.ts +++ b/shared/js/workers/pow/index.ts @@ -1,12 +1,8 @@ -declare namespace WebAssembly { - export function instantiateStreaming(stream: Promise, imports?: any) : Promise; -} declare function postMessage(message: any): void; const prefix = "[POWWorker] "; let initialized = false; - let memory: WebAssembly.Memory; let memory_u8: Uint8Array; let wasm_object: WebAssembly.WebAssemblyInstantiatedSource; @@ -31,15 +27,7 @@ function post_status(code: string | undefined, result: boolean | string | any) { memory = new WebAssembly.Memory({ initial: 1 }); memory_u8 = new Uint8Array(memory.buffer); - if(typeof(WebAssembly.instantiateStreaming) === "undefined") { - WebAssembly.instantiateStreaming = async (stream: Promise, imports?: any) => { - const response = await stream; - const buffer = await response.arrayBuffer(); - return WebAssembly.instantiate(buffer, imports); - } - } - - WebAssembly.instantiateStreaming(fetch('../../wat/pow/sha1.wasm'), { + WebAssembly.instantiate(require("./sha1.wat") as Uint8Array, { env: { memory: memory } @@ -95,4 +83,6 @@ onmessage = function(e: MessageEvent) { post_status(data.code, true); } -}; \ No newline at end of file +}; + +export = {}; \ No newline at end of file diff --git a/shared/wat/pow/sha1.wat b/shared/js/workers/pow/sha1.wat similarity index 93% rename from shared/wat/pow/sha1.wat rename to shared/js/workers/pow/sha1.wat index b3d5f3e0..8a98ff24 100644 --- a/shared/wat/pow/sha1.wat +++ b/shared/js/workers/pow/sha1.wat @@ -1,5 +1,6 @@ ;; SHA-1 code from https://github.com/Snack-X/wasm-works/blob/master/modules/sha1.wat by Snack-X - +;; TODO: Cache the sha1 state after the first 64 bytes. +;; Maybe as well for 128 bytes. But this block may be recalculated on number change because not every identity is 128 bytes long (module ;; import 1 page of memory from env.memory ;; [0x000;0x03f] will be used as input chunk @@ -136,7 +137,7 @@ ;; calculate f and determine k (block $get_key (if (i32.lt_s (get_local $w) (i32.const 20)) - (block + (block ;; depth: 6 ;; f = (b & c) | (~b & d) (set_local $f (i32.or @@ -146,7 +147,7 @@ ) ) (set_local $k (i32.const 0x5a827999)) - (br $get_key) + (br 2) ;; $get_key ) ) (if (i32.lt_s (get_local $w) (i32.const 40)) @@ -159,7 +160,7 @@ ) ) (set_local $k (i32.const 0x6ed9eba1)) - (br $get_key) + (br 2) ;; $get_key ) ) (if (i32.lt_s (get_local $w) (i32.const 60)) @@ -175,7 +176,7 @@ ) ) (set_local $k (i32.const 0x8f1bbcdc)) - (br $get_key) + (br 2) ;; $get_key ) ) (if (i32.lt_s (get_local $w) (i32.const 80)) @@ -188,7 +189,7 @@ ) ) (set_local $k (i32.const 0xca62c1d6)) - (br $get_key) + (br 2) ;; $get_key ) ) ) @@ -221,10 +222,10 @@ (set_local $w (i32.add (get_local $w) (i32.const 1))) ;; if 80 <= w, break - (br_if $done (i32.ge_s (get_local $w) (i32.const 80))) + (br_if 1 (i32.ge_s (get_local $w) (i32.const 80))) ;; 1 := $done ;; else, continue - (br $loop) + (br 0) ;; $loop ) ) @@ -259,11 +260,11 @@ (block $done_pad ;; zero pad (loop $loop - (br_if $done_pad (i32.ge_s (get_local $i) (i32.const 64))) + (br_if 1 (i32.ge_s (get_local $i) (i32.const 64))) ;; 1 := $done_pad (i32.store8 (get_local $i) (i32.const 0)) (set_local $i (i32.add (get_local $i) (i32.const 1))) - (br $loop) + (br 0) ;; $loop ) ) @@ -284,11 +285,11 @@ (block $done_pad ;; zero pad (loop $loop - (br_if $done_pad (i32.ge_s (get_local $i) (i32.const 56))) + (br_if 1 (i32.ge_s (get_local $i) (i32.const 56))) ;; 1 := $done_pad (i32.store8 (get_local $i) (i32.const 0)) (set_local $i (i32.add (get_local $i) (i32.const 1))) - (br $loop) + (br 0) ;; $loop ) ) ) @@ -350,7 +351,7 @@ ;; Set it to '0' and decrease $index (i32.store8 (get_local $index) (i32.const 48)) - (br $main_loop) + (br 2) ;; 2 := $main_loop ) ) @@ -362,7 +363,7 @@ ) - (func $mine + (func $mine ;; depth := 0 ;; Length of the base 64 string (param $length64 i32) ;; Length of the counter @@ -384,8 +385,8 @@ (set_local $best_level (get_local $target_level)) - (block $done - (loop $main_loop + (block $done ;; depth := 1 + (loop $main_loop ;; depth := 2 call $sha1_init ;; Load the first 64 bytes @@ -403,7 +404,7 @@ (set_local $write_index (i32.const 0x0E0)) (set_local $write_offset (i32.const 0)) - (loop $write_loop + (loop $write_loop ;; depth := 3 (i32.store8 (get_local $write_offset) (i32.load8_u (get_local $write_index))) (set_local $write_offset (i32.add (get_local $write_offset) (i32.const 1))) @@ -415,7 +416,7 @@ ) (set_local $write_index (i32.add (get_local $write_index) (i32.const 1))) - (br_if $write_loop (i32.lt_s (get_local $write_index) (get_local $max_write_index))) + (br_if 0 (i32.lt_s (get_local $write_index) (get_local $max_write_index))) ;; 0 := $write_loop ) (call $sha1_end (get_local $write_offset)) @@ -468,7 +469,7 @@ ;; If we have a target level then break here (if (i32.ne (get_local $target_level) (i32.const 0)) - (br $done) + (br 4) ;; $done maybe 4? ) ) ) @@ -479,7 +480,7 @@ (call $increase_counter (i32.add (i32.const 0x0A0) (get_local $length64)) (get_local $length_counter)) ) - (br_if $main_loop (i32.gt_u (get_local $iterations) (i32.const 0))) + (br_if 0 (i32.gt_u (get_local $iterations) (i32.const 0))) ;; 0 := $main_loop ) ) diff --git a/shared/wat/pow/sha1.wasm b/shared/wat/pow/sha1.wasm deleted file mode 100644 index 0f96e515bf72ad8f84a06542e11a265a37272872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1288 zcmb7Ey=xRf6n}4K_9JeRUBM!i&M;e?g%}$>#p0fYMuq$hu}IKOE^>Dv9}wINR>3xn zjcAwBO4RDWuZRi<29v_V&O#7D2nrH?vwL@>(84Y5_nY_LzWKe6Te#M$BLJZGr!dES zO5{0A0q_AFXN$`D!Cb^Br{@>_`gHwfb438kp%4&+kjnFq`8g6q|NIF;RP`J4(-0im z7zz{6KyicATtz@4;^^?epfM4r1w<#ErQL2XH)w3Jpud6!g^Nwc_a)N>X1|f$3(aO6 z%>cm#I3t0=5|R?eO3)=#N*FJpS^}g%YcDsR`YF=-&ZGTXsi6HQUtX-FlJ+)VuVJd_ z+p{F)U?g1pkqK2}BD8F1L3vB7M$`Co_xt*`XM$l7MI2Na1hK!x?gu-c_xyqT{J_mQ zTmmh7X+Hk^`eS(z*M&h~FVBbDciyj-{zxNvOWM!0r-LlhkU(Zat&FFoQLOFgl93vX z(4j%rgqd+=c42Y^hk<3H#VkcFtjJO>$I2{a8CGSf(w_A)Zsa>e!Hv|M*`;XXIdC~R zmEa4iS6nNKJ__zN=Vc?f5K)AoG<%T#W|pVjiX(GUaqAn>PRF3-N)myTzlH=(}W)O=@>okQ%#*dt Date: Tue, 31 Mar 2020 13:50:17 +0200 Subject: [PATCH 14/23] Updated the package file and added the package-lock.json --- .gitignore | 1 - package-lock.json | 589 ++++------------------------------------------ package.json | 22 +- 3 files changed, 51 insertions(+), 561 deletions(-) diff --git a/.gitignore b/.gitignore index 1492c4f3..ed6e9cce 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ asm/generated/ node_modules/ auth/certs/ auth/js/auth.js.map -package-lock.json .sass-cache/ .idea/ diff --git a/package-lock.json b/package-lock.json index da1ca552..947f5178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,292 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/core": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", - "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.0", - "@babel/parser": "^7.9.0", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", - "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/generator": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", - "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", - "requires": { - "@babel/types": "^7.9.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-replace-supers": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" - }, - "@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", - "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" - } - }, - "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", - "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, "@types/emscripten": { "version": "1.39.2", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz", @@ -306,6 +26,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.1.tgz", "integrity": "sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==", + "dev": true, "requires": { "@types/node": "*" }, @@ -313,7 +34,8 @@ "@types/node": { "version": "13.7.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz", - "integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==" + "integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==", + "dev": true } } }, @@ -1048,11 +770,6 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, - "babylon": { - "version": "7.0.0-beta.47", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", - "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==" - }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -1173,7 +890,8 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true }, "binary-extensions": { "version": "1.13.1", @@ -1567,7 +1285,8 @@ "circular-dependency-plugin": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", - "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==" + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", + "dev": true }, "clap": { "version": "1.2.3", @@ -1727,6 +1446,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1734,7 +1454,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "color-support": { "version": "1.1.3", @@ -1830,6 +1551,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -2432,7 +2154,8 @@ "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true }, "encodeurl": { "version": "1.0.2", @@ -2567,7 +2290,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "eslint-scope": { "version": "4.0.3", @@ -2826,44 +2550,6 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, - "file-loader": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", - "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", - "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } - } - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -3367,9 +3053,9 @@ "dev": true }, "fsevents": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", - "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", "dev": true, "optional": true, "requires": { @@ -3423,7 +3109,7 @@ } }, "chownr": { - "version": "1.1.3", + "version": "1.1.4", "bundled": true, "dev": true, "optional": true @@ -3595,7 +3281,7 @@ } }, "minimist": { - "version": "0.0.8", + "version": "1.2.5", "bundled": true, "dev": true, "optional": true @@ -3620,12 +3306,12 @@ } }, "mkdirp": { - "version": "0.5.1", + "version": "0.5.3", "bundled": true, "dev": true, "optional": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { @@ -3635,7 +3321,7 @@ "optional": true }, "needle": { - "version": "2.4.0", + "version": "2.3.3", "bundled": true, "dev": true, "optional": true, @@ -3664,7 +3350,7 @@ } }, "nopt": { - "version": "4.0.1", + "version": "4.0.3", "bundled": true, "dev": true, "optional": true, @@ -3689,13 +3375,14 @@ "optional": true }, "npm-packlist": { - "version": "1.4.7", + "version": "1.4.8", "bundled": true, "dev": true, "optional": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npmlog": { @@ -3775,18 +3462,10 @@ "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } } }, "readable-stream": { - "version": "2.3.6", + "version": "2.3.7", "bundled": true, "dev": true, "optional": true, @@ -3967,11 +3646,6 @@ "globule": "^1.0.0" } }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" - }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -4486,11 +4160,6 @@ "which": "^1.2.14" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -4640,7 +4309,8 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.1", @@ -5337,11 +5007,6 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -5376,6 +5041,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, "requires": { "minimist": "^1.2.0" }, @@ -5383,7 +5049,8 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true } } }, @@ -5499,6 +5166,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5526,12 +5194,8 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true }, "loose-envify": { "version": "1.4.0", @@ -6601,18 +6265,6 @@ } } }, - "object.entries": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", - "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, "object.getownpropertydescriptors": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", @@ -6965,7 +6617,8 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-root": { "version": "0.1.1", @@ -7980,6 +7633,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -8061,7 +7715,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -8285,7 +7940,8 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, "semver-greatest-satisfied-range": { "version": "1.1.0", @@ -9002,11 +8658,6 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -9589,120 +9240,6 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, - "wabt": { - "version": "1.0.0-nightly.20180421", - "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.0-nightly.20180421.tgz", - "integrity": "sha512-bsu9zk672KACjoabONcAS94IS20prRm05IbiIUGfa8eBpRLjWZv8ugocdinV/ONh0mFMfXrVWkvF1/BNtwIfUw==", - "dev": true - }, - "wasm-dce": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz", - "integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==", - "requires": { - "@babel/core": "^7.0.0-beta.39", - "@babel/traverse": "^7.0.0-beta.39", - "@babel/types": "^7.0.0-beta.39", - "babylon": "^7.0.0-beta.39", - "webassembly-interpreter": "0.0.30" - } - }, - "wasm-loader": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/wasm-loader/-/wasm-loader-1.3.0.tgz", - "integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==", - "requires": { - "loader-utils": "^1.1.0", - "wasm-dce": "^1.0.0" - } - }, - "wasm2wat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/wasm2wat/-/wasm2wat-1.1.0.tgz", - "integrity": "sha512-4+mCqEbDpomTbjtCtOzU/7Gzz0xdSPK2809n/t0H7w38xom6+i62C7u4rMRjla6QANb3YzX0TFYZly+QD8eBfA==", - "requires": { - "chalk": "^3.0.0", - "wabt": "^1.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "wabt": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.13.tgz", - "integrity": "sha512-nkWPyUjYt+SqPox2mjJF5jrpWv30awdXKdG0OmryzfhtahHBrPz/BGSbakPgcJU2SFjC1s7Mb8MadRhQ6lmqUg==" - } - } - }, - "wast-loader": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/wast-loader/-/wast-loader-1.9.0.tgz", - "integrity": "sha512-5AufW8qi2X/G/qLeuQUgtqvv4bifxWXnhJ9ZgMOC1ZmI4wuytyaIB0uskfa2l/i286xVZFAYgsz1b4YsoaGVcQ==", - "dev": true, - "requires": { - "wabt": "1.0.0-nightly.20180421" - } - }, - "wat2wasm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wat2wasm/-/wat2wasm-1.0.2.tgz", - "integrity": "sha512-QYwX0jctZxlKCMMn5BH6ZYRsGT4a+lzYXYCFQn1rakwmc3BWrtLSMoNSSpi2u279UQoahyvd+28w8pan7IlY/A==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "watchpack": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", @@ -10073,21 +9610,6 @@ } } }, - "webassembly-floating-point-hex-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz", - "integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==" - }, - "webassembly-interpreter": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz", - "integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==", - "requires": { - "@babel/code-frame": "^7.0.0-beta.36", - "long": "^3.2.0", - "webassembly-floating-point-hex-parser": "0.1.2" - } - }, "webpack": { "version": "4.42.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", @@ -10802,31 +10324,6 @@ } } }, - "webpack-manifest-plugin": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", - "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", - "dev": true, - "requires": { - "fs-extra": "^7.0.0", - "lodash": ">=3.5 <5", - "object.entries": "^1.1.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, "webpack-sources": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", diff --git a/package.json b/package.json index 9c817a91..61c8b1e8 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "author": "TeaSpeak (WolverinDEV)", "license": "ISC", "devDependencies": { + "@types/fs-extra": "^8.0.1", "@types/emscripten": "^1.38.0", "@types/jquery": "^3.3.34", "@types/lodash": "^4.14.149", @@ -37,13 +38,10 @@ "chunk-manifest-webpack-plugin": "^1.1.2", "clean-css": "^4.2.1", "clean-webpack-plugin": "^3.0.0", - "css-loader": "^3.4.2", "csso-cli": "^2.0.2", "exports-loader": "^0.7.0", - "file-loader": "^6.0.0", "fs-extra": "latest", "gulp": "^4.0.2", - "html-loader": "^1.0.0", "html-webpack-plugin": "^4.0.3", "mime-types": "^2.1.24", "mini-css-extract-plugin": "^0.9.0", @@ -51,20 +49,20 @@ "node-sass": "^4.13.1", "raw-loader": "^4.0.0", "sass": "1.22.10", - "sass-loader": "^8.0.2", "sha256": "^0.2.0", - "style-loader": "^1.1.3", "terser": "^4.2.1", "ts-loader": "^6.2.2", "ttypescript": "^1.5.10", "typescript": "3.6.5", - "wast-loader": "^1.9.0", - "wat2wasm": "^1.0.2", "webpack": "^4.42.1", "webpack-bundle-analyzer": "^3.6.1", "webpack-cli": "^3.3.11", - "webpack-manifest-plugin": "^2.2.0", - "worker-plugin": "^4.0.2" + "worker-plugin": "^4.0.2", + "circular-dependency-plugin": "^5.2.0", + "css-loader": "^3.4.2", + "html-loader": "^1.0.0", + "sass-loader": "^8.0.2", + "style-loader": "^1.1.3" }, "repository": { "type": "git", @@ -75,11 +73,7 @@ }, "homepage": "https://www.teaspeak.de", "dependencies": { - "@types/fs-extra": "^8.0.1", - "circular-dependency-plugin": "^5.2.0", "react": "^16.13.1", - "react-dom": "^16.13.1", - "wasm-loader": "^1.3.0", - "wasm2wat": "^1.1.0" + "react-dom": "^16.13.1" } } From d5ed46150e2f84ce8c5e786aceccb4374c84079d Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 15:19:53 +0200 Subject: [PATCH 15/23] Added tr caching to webpack --- package-lock.json | 6 ++ package.json | 14 ++-- shared/js/ConnectionHandler.ts | 2 +- shared/js/i18n/localize.ts | 2 +- shared/js/main.ts | 6 +- shared/js/profiles/identities/NameIdentity.ts | 1 - shared/js/ui/modal/ModalNewcomer.ts | 18 ++--- .../modal/permission/ModalPermissionEdit.ts | 3 +- tools/trgen/compiler.ts | 1 - tools/trgen/generator.ts | 1 - tools/trgen/index.ts | 2 - tools/trgen/ts_generator.ts | 48 +++++++------ tools/trgen/ts_transformer.ts | 67 ++++++++++++++++++ tools/trgen/ttsc_transformer.ts | 68 ++----------------- tools/trmanager/.gitignore | 2 - tools/trmanager/index.html | 10 --- webpack.config.js => webpack.config.ts | 38 ++++++++--- webpack/WatLoader.ts | 8 +-- 18 files changed, 160 insertions(+), 137 deletions(-) create mode 100644 tools/trgen/ts_transformer.ts delete mode 100644 tools/trmanager/.gitignore delete mode 100644 tools/trmanager/index.html rename webpack.config.js => webpack.config.ts (69%) diff --git a/package-lock.json b/package-lock.json index 947f5178..edf9fd4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9240,6 +9240,12 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, + "wabt": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.13.tgz", + "integrity": "sha512-nkWPyUjYt+SqPox2mjJF5jrpWv30awdXKdG0OmryzfhtahHBrPz/BGSbakPgcJU2SFjC1s7Mb8MadRhQ6lmqUg==", + "dev": true + }, "watchpack": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", diff --git a/package.json b/package.json index 61c8b1e8..7b3a1823 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "author": "TeaSpeak (WolverinDEV)", "license": "ISC", "devDependencies": { - "@types/fs-extra": "^8.0.1", "@types/emscripten": "^1.38.0", + "@types/fs-extra": "^8.0.1", "@types/jquery": "^3.3.34", "@types/lodash": "^4.14.149", "@types/moment": "^2.13.0", @@ -36,12 +36,15 @@ "@types/sha256": "^0.2.0", "@types/websocket": "0.0.40", "chunk-manifest-webpack-plugin": "^1.1.2", + "circular-dependency-plugin": "^5.2.0", "clean-css": "^4.2.1", "clean-webpack-plugin": "^3.0.0", + "css-loader": "^3.4.2", "csso-cli": "^2.0.2", "exports-loader": "^0.7.0", "fs-extra": "latest", "gulp": "^4.0.2", + "html-loader": "^1.0.0", "html-webpack-plugin": "^4.0.3", "mime-types": "^2.1.24", "mini-css-extract-plugin": "^0.9.0", @@ -49,20 +52,19 @@ "node-sass": "^4.13.1", "raw-loader": "^4.0.0", "sass": "1.22.10", + "sass-loader": "^8.0.2", "sha256": "^0.2.0", + "style-loader": "^1.1.3", "terser": "^4.2.1", "ts-loader": "^6.2.2", "ttypescript": "^1.5.10", "typescript": "3.6.5", + "wabt": "^1.0.13", "webpack": "^4.42.1", "webpack-bundle-analyzer": "^3.6.1", "webpack-cli": "^3.3.11", "worker-plugin": "^4.0.2", - "circular-dependency-plugin": "^5.2.0", - "css-loader": "^3.4.2", - "html-loader": "^1.0.0", - "sass-loader": "^8.0.2", - "style-loader": "^1.1.3" + "terser-webpack-plugin": "latest" }, "repository": { "type": "git", diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts index 8a0b6731..3cec06c1 100644 --- a/shared/js/ConnectionHandler.ts +++ b/shared/js/ConnectionHandler.ts @@ -477,7 +477,7 @@ export class ConnectionHandler { this._certificate_modal = createErrorModal( tr("Could not connect"), - formatMessage(tr(error_message_format), this.generate_ssl_certificate_accept()) + formatMessage(/* @tr-ignore */ tr(error_message_format), this.generate_ssl_certificate_accept()) ); this._certificate_modal.close_listener.push(() => this._certificate_modal = undefined); this._certificate_modal.open(); diff --git a/shared/js/i18n/localize.ts b/shared/js/i18n/localize.ts index 11ca2368..48602847 100644 --- a/shared/js/i18n/localize.ts +++ b/shared/js/i18n/localize.ts @@ -70,7 +70,7 @@ export function tr(message: string, key?: string) { } export function tra(message: string, ...args: any[]) { - message = tr(message); + message = /* @tr-ignore */ tr(message); return formatMessage(message, ...args); } diff --git a/shared/js/main.ts b/shared/js/main.ts index 2c0b1121..3c4956c5 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -113,7 +113,7 @@ function setup_jsrender() : boolean { }); js_render.views.tags("tr", (...args) => { - return tr(args[0]); + return /* @tr-ignore */ tr(args[0]); }); $(".jsrender-template").each((idx, _entry) => { @@ -481,7 +481,7 @@ const task_connect_handler: loader.Task = { "You could now close this page."; createInfoModal( tr("Connecting successfully within other instance"), - formatMessage(tr(message), connect_data.address), + formatMessage(/* @tr-ignore */ tr(message), connect_data.address), { closeable: false, footer: undefined @@ -548,7 +548,7 @@ const task_certificate_callback: loader.Task = { "This page will close in {0} seconds."; createInfoModal( tr("Certificate acccepted successfully"), - formatMessage(tr(message), seconds_tag), + formatMessage(/* @tr-ignore */ tr(message), seconds_tag), { closeable: false, footer: undefined diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts index c0a14aa2..c3d65831 100644 --- a/shared/js/profiles/identities/NameIdentity.ts +++ b/shared/js/profiles/identities/NameIdentity.ts @@ -10,7 +10,6 @@ import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase"; import {HandshakeIdentityHandler} from "tc-shared/connection/HandshakeHandler"; -console.error(AbstractHandshakeIdentityHandler); class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { readonly identity: NameIdentity; handler: HandshakeCommandHandler; diff --git a/shared/js/ui/modal/ModalNewcomer.ts b/shared/js/ui/modal/ModalNewcomer.ts index b537573c..2a93ac9d 100644 --- a/shared/js/ui/modal/ModalNewcomer.ts +++ b/shared/js/ui/modal/ModalNewcomer.ts @@ -163,7 +163,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry" + "press a key. The key could be selected " + "via the button right to the radio button." @@ -401,7 +401,7 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry { @@ -1358,7 +1359,7 @@ function apply_server_groups(connection: ConnectionHandler, editor: AbstractPerm console.log(tr("Failed to add client %o to server group %o: %o"), dbid, current_group.id, error); if(error instanceof CommandResult) error = error.extra_message || error.message; - createErrorModal(tr("Failed to add client"), tr("Failed to add client to server group\n" + error)).open(); + createErrorModal(tr("Failed to add client"), tra("Failed to add client to server group{:br:}", error)).open(); }); }).open(); }); diff --git a/tools/trgen/compiler.ts b/tools/trgen/compiler.ts index efafcc82..ef2f438f 100644 --- a/tools/trgen/compiler.ts +++ b/tools/trgen/compiler.ts @@ -49,7 +49,6 @@ function compile(fileNames: string[], options: ts.CompilerOptions): void { } const config = ts.parseCommandLine(process.argv.slice(2), file => readFileSync(file).toString()); -console.dir(config); if(config.errors && config.errors.length > 0) { for(const error of config.errors) console.log(error.messageText); diff --git a/tools/trgen/generator.ts b/tools/trgen/generator.ts index 99408f2a..71cab0b7 100644 --- a/tools/trgen/generator.ts +++ b/tools/trgen/generator.ts @@ -1,4 +1,3 @@ - export interface TranslationEntry { filename: string; line: number; diff --git a/tools/trgen/index.ts b/tools/trgen/index.ts index 22dd2784..db395c99 100644 --- a/tools/trgen/index.ts +++ b/tools/trgen/index.ts @@ -8,8 +8,6 @@ import {isArray} from "util"; import * as mkdirp from "mkdirp"; import {TranslationEntry} from "./generator"; -console.log("TR GEN!"); - /* const files = ["/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/build/trgen/test/test_01.ts"]; files.forEach(file => { diff --git a/tools/trgen/ts_generator.ts b/tools/trgen/ts_generator.ts index 02097924..4ee1f70e 100644 --- a/tools/trgen/ts_generator.ts +++ b/tools/trgen/ts_generator.ts @@ -11,10 +11,14 @@ export function generate(file: ts.SourceFile, config: Configuration) : Translati return result; } -function report(node: ts.Node, message: string) { +const source_location = (node: ts.Node) => { const sf = node.getSourceFile(); let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1}; - console.log(`${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1}): ${message}`); + return `${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1})`; +}; + +function report(node: ts.Node, message: string) { + console.log(`${source_location(node)}: ${message}`); } function _generate(config: Configuration, node: ts.Node, result: TranslationEntry[]) { @@ -27,7 +31,6 @@ function _generate(config: Configuration, node: ts.Node, result: TranslationEntr if(call_name != "tr") break call_analize; console.dir(call_name); - console.log("Parameters: %o", call.arguments.length); if(call.arguments.length > 1) { report(call, "Invalid argument count"); @@ -159,7 +162,7 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression return [...nodes, ts.createLabel(unique_check_label_name, ts.createBlock(blocked_nodes))]; } -export function transform(config: Configuration, context: ts.TransformationContext, node: ts.SourceFile) : TransformResult { +export function transform(config: Configuration, context: ts.TransformationContext, source_file: ts.SourceFile) : TransformResult { const cache: VolatileTransformConfig = {} as any; cache.translations = []; @@ -215,56 +218,59 @@ export function transform(config: Configuration, context: ts.TransformationConte function visit(node: ts.Node): ts.Node { node = ts.visitEachChild(node, visit, context); - return replace_processor(config, cache, node); + return replace_processor(config, cache, node, source_file); } - node = ts.visitNode(node, visit); - extra_nodes.push(...create_unique_check(node, cache.nodes.translation_map_init, generated_names)); + source_file = ts.visitNode(source_file, visit); + extra_nodes.push(...create_unique_check(source_file, cache.nodes.translation_map_init, generated_names)); - node = ts.updateSourceFileNode(node, [...(extra_nodes as any[]), ...node.statements], node.isDeclarationFile, node.referencedFiles, node.typeReferenceDirectives, node.hasNoDefaultLib, node.referencedFiles); + source_file = ts.updateSourceFileNode(source_file, [...(extra_nodes as any[]), ...source_file.statements], source_file.isDeclarationFile, source_file.referencedFiles, source_file.typeReferenceDirectives, source_file.hasNoDefaultLib, source_file.referencedFiles); const result: TransformResult = {} as any; - result.node = node; + result.node = source_file; result.translations = cache.translations; return result; } -export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node) : ts.Node { +export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node, source_file: ts.SourceFile) : ts.Node { if(config.verbose) console.log("Process %s", SyntaxKind[node.kind]); if(ts.isCallExpression(node)) { const call = node; const call_name = call.expression["escapedText"] as string; if(call_name != "tr") return node; - + if(!node.getSourceFile()) return node; if(config.verbose) { console.dir(call_name); console.log("Parameters: %o", call.arguments.length); } - if(call.arguments.length > 1) { - report(call, "Invalid argument count"); + + if(call.arguments.length > 1) + throw new Error(source_location(call) + ": tr(...) has been called with an invalid arguments (" + (call.arguments.length === 0 ? "too few" : "too many") + ")"); + + const fullText = call.getFullText(source_file); + if(fullText && fullText.indexOf("@tr-ignore") !== -1) return node; - } const object = call.arguments[0]; if(object.kind != SyntaxKind.StringLiteral) { - report(call, "Invalid argument: " + SyntaxKind[object.kind]); - return node; + if(call.getSourceFile()) + throw new Error(source_location(call) + ": Ignoring tr call because given argument isn't of type string literal. (" + SyntaxKind[object.kind] + ")"); + report(call, "Ignoring tr call because given argument isn't of type string literal. (" + SyntaxKind[object.kind] + ")"); } if(config.verbose) - console.log("Message: %o", object.text || object.getText()); + console.log("Message: %o", object.text || object.getText(source_file)); - const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText())); + const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText(source_file))); const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name); const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name); const new_variable = ts.createAssignment(variable, call); - const source_file = node.getSourceFile(); - let { line, character } = source_file ? source_file.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1}; + let { line, character } = source_file.getLineAndCharacterOfPosition(node.getStart()); cache.translations.push({ - message: object.text || object.getText(), + message: object.text || object.getText(source_file), line: line, character: character, filename: (source_file || {fileName: "unknown"}).fileName diff --git a/tools/trgen/ts_transformer.ts b/tools/trgen/ts_transformer.ts new file mode 100644 index 00000000..d5a7bae6 --- /dev/null +++ b/tools/trgen/ts_transformer.ts @@ -0,0 +1,67 @@ +import * as ts from "typescript"; +import * as ts_generator from "./ts_generator"; +import * as path from "path"; +import * as mkdirp from "mkdirp"; + +import {writeFileSync} from "fs"; +import {TranslationEntry} from "./generator"; + +export interface Config { + target_file?: string; + verbose?: boolean; +} + +let process_config: Config; +export default function(program: ts.Program, config?: Config) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile { + process_config = config as any || {}; + + const base_path = path.dirname(program.getCompilerOptions().project || program.getCurrentDirectory()); + if(process_config.verbose) { + console.log("TRGen transformer called"); + console.log("Base path: %s", base_path); + } + + process.on('exit', function () { + if(!process_config.target_file) return; + + const target = path.isAbsolute(process_config.target_file) ? process_config.target_file : path.join(base_path, process_config.target_file); + if(process_config.target_file) { + if(process_config.verbose) + console.log("Writing translation file to " + target); + + mkdirp.sync(path.dirname(target)); + writeFileSync(target, JSON.stringify(translations, null, 2)); + } + }); + + return ctx => transformer(ctx) as any; +} + +const translations: TranslationEntry[] = []; +const transformer = (context: ts.TransformationContext) => +(rootNode: ts.Node) => { + const handler = (rootNode: ts.Node) => { + if(rootNode.kind == ts.SyntaxKind.Bundle) { + const bundle = rootNode as ts.Bundle; + const result = []; + for(const file of bundle.sourceFiles) + result.push(handler(file)); + return ts.updateBundle(bundle, result as any, bundle.prepends as any); + + } else if(rootNode.kind == ts.SyntaxKind.SourceFile) { + const file = rootNode as ts.SourceFile; + + console.log("Processing " + file.fileName); + const result = ts_generator.transform({ + use_window: false, + replace_cache: true + }, context, file); + translations.push(...result.translations); + return result.node; + } else { + console.warn("Invalid transform input: %s", ts.SyntaxKind[rootNode.kind]); + } + }; + + return handler(rootNode); +}; \ No newline at end of file diff --git a/tools/trgen/ttsc_transformer.ts b/tools/trgen/ttsc_transformer.ts index 7bc58503..4885dd05 100644 --- a/tools/trgen/ttsc_transformer.ts +++ b/tools/trgen/ttsc_transformer.ts @@ -1,67 +1,9 @@ -import * as ts from "typescript"; -import * as ts_generator from "./ts_generator"; -import * as path from "path"; -import * as mkdirp from "mkdirp"; - +import transform, {Config} from "./ts_transformer"; import {PluginConfig} from "ttypescript/lib/PluginCreator"; -import {writeFileSync} from "fs"; -import {TranslationEntry} from "./generator"; +import * as ts from "typescript"; -interface Config { - target_file?: string; - verbose?: boolean; -} - -//(program: ts.Program, config?: PluginConfig) => ts.TransformerFactory -let process_config: Config; export default function(program: ts.Program, config?: PluginConfig) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile { - process_config = config as any || {}; + const process_config: Config = config as any || {}; - const base_path = path.dirname(program.getCompilerOptions().project || program.getCurrentDirectory()); - if(process_config.verbose) { - console.log("TRGen transformer called"); - console.log("Base path: %s", base_path); - } - - process.on('exit', function () { - const target = path.isAbsolute(process_config.target_file) ? process_config.target_file : path.join(base_path, process_config.target_file); - if(process_config.target_file) { - if(process_config.verbose) - console.log("Writing translation file to " + target); - - mkdirp.sync(path.dirname(target)); - writeFileSync(target, JSON.stringify(translations, null, 2)); - } - }); - - return ctx => transformer(ctx) as any; -} - -const translations: TranslationEntry[] = []; -const transformer = (context: ts.TransformationContext) => -(rootNode: ts.Node) => { - const handler = (rootNode: ts.Node) => { - if(rootNode.kind == ts.SyntaxKind.Bundle) { - const bundle = rootNode as ts.Bundle; - const result = []; - for(const file of bundle.sourceFiles) - result.push(handler(file)); - return ts.updateBundle(bundle, result as any, bundle.prepends as any); - - } else if(rootNode.kind == ts.SyntaxKind.SourceFile) { - const file = rootNode as ts.SourceFile; - - console.log("Processing " + file.fileName); - const result = ts_generator.transform({ - use_window: false, - replace_cache: true - }, context, file); - translations.push(...result.translations); - return result.node; - } else { - console.warn("Invalid transform input: %s", ts.SyntaxKind[rootNode.kind]); - } - }; - - return handler(rootNode); -}; \ No newline at end of file + return transform(program, process_config); +} \ No newline at end of file diff --git a/tools/trmanager/.gitignore b/tools/trmanager/.gitignore deleted file mode 100644 index 20efd1ec..00000000 --- a/tools/trmanager/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/*.js -**/*.js.map \ No newline at end of file diff --git a/tools/trmanager/index.html b/tools/trmanager/index.html deleted file mode 100644 index ebc0c722..00000000 --- a/tools/trmanager/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Translation Manager - - -
This needs some improvements
- - \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.ts similarity index 69% rename from webpack.config.js rename to webpack.config.ts index 60c2e8d9..412ca812 100644 --- a/webpack.config.js +++ b/webpack.config.ts @@ -1,19 +1,23 @@ +import * as ts from "typescript"; +import trtransformer, {Config} from "./tools/trgen/ts_transformer"; + const path = require('path'); const webpack = require("webpack"); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const ManifestGenerator = require("./webpack/ManifestPlugin"); const WorkerPlugin = require('worker-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -const isDevelopment = process.env.NODE_ENV === 'development'; +let isDevelopment = process.env.NODE_ENV === 'development'; +isDevelopment = true; module.exports = { entry: { - //"shared-app": "./shared/js/main.ts" "shared-app": "./web/js/index.ts" }, - devtool: 'inline-source-map', - mode: "development", + devtool: isDevelopment ? "inline-source-map" : undefined, + mode: isDevelopment ? "development" : "production", plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ @@ -34,8 +38,8 @@ module.exports = { }) */ new webpack.optimize.AggressiveSplittingPlugin({ - minSize: 1024 * 128, - maxSize: 1024 * 1024 + minSize: 1024 * 8, + maxSize: 1024 * 128 }) ], module: { @@ -43,8 +47,7 @@ module.exports = { { test: /\.s[ac]ss$/, loader: [ - //isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, - 'style-loader', + isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { @@ -68,8 +71,21 @@ module.exports = { { loader: 'ts-loader', options: { - transpileOnly: true + transpileOnly: true, + getCustomTransformers: (prog: ts.Program) => { + return { + before: [trtransformer(prog, {})] + }; + } } + /* + { + "transform": "../../tools/trgen/ttsc_transformer.js", + "type": "program", + "target_file": "../generated/messages_script.json", + "verbose": true + } + */ } ] }, @@ -100,6 +116,8 @@ module.exports = { publicPath: "js/" }, optimization: { - splitChunks: { } + splitChunks: { }, + minimize: !isDevelopment, + minimizer: [new TerserPlugin()] } }; \ No newline at end of file diff --git a/webpack/WatLoader.ts b/webpack/WatLoader.ts index f1c8b982..7597d523 100644 --- a/webpack/WatLoader.ts +++ b/webpack/WatLoader.ts @@ -2,7 +2,7 @@ import * as webpack from "webpack"; import {RawSourceMap} from "source-map"; import LoaderContext = webpack.loader.LoaderContext; -const wabt = require("wabt"); +const wabt = require("wabt")(); const filename = "module.wast"; @@ -10,9 +10,7 @@ export default function loader(this: LoaderContext, source: string | Buffer, sou this.cacheable(); const module = wabt.parseWat(filename, source); - const { buffer } = module.toBinary({ write_debug_names: true, relocatable: true, canonicalize_lebs: true, log: true }); + const { buffer } = module.toBinary({ write_debug_names: false }); - this.emitFile("test.wasm", buffer, null); - const result = `module.exports = new Uint8Array([${buffer.join(",")}]);`; - this.callback(null, result); + this.callback(null, `module.exports = new Uint8Array([${buffer.join(",")}]);`); } \ No newline at end of file From 3ec30c07f914dac308a24637b007f8ebd9608b88 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 15:47:25 +0200 Subject: [PATCH 16/23] Removed moment and webrtc adapter and bundle it with webpack --- file.ts | 44 - loader/app/app.ts | 2 - package-lock.json | 30 +- package.json | 5 +- shared/adapter/adapter-latest.js | 5283 --------- shared/js/main.ts | 2 +- shared/js/ui/modal/ModalAvatarList.ts | 2 +- shared/js/ui/modal/ModalBanList.ts | 2 +- shared/js/ui/modal/ModalClientInfo.ts | 2 +- shared/js/ui/modal/ModalPoke.ts | 2 +- shared/js/ui/modal/ModalServerInfo.ts | 2 +- vendor/moment/moment.js | 14380 ------------------------ web/js/index.ts | 5 +- 13 files changed, 42 insertions(+), 19719 deletions(-) delete mode 100644 shared/adapter/adapter-latest.js delete mode 100644 vendor/moment/moment.js diff --git a/file.ts b/file.ts index 4086a735..376a4547 100644 --- a/file.ts +++ b/file.ts @@ -479,23 +479,6 @@ const WEB_APP_FILE_LIST = [ "path": "js/", "local-path": "./loader/dist/" }, - { /* shared javascript files (WebRTC adapter) */ - "type": "js", - "search-pattern": /.*\.js$/, - "build-target": "dev|rel", - - "path": "adapter/", - "local-path": "./shared/adapter/" - }, - - { /* shared generated worker codec */ - "type": "js", - "search-pattern": /(WorkerPOW.js)$/, - "build-target": "dev|rel", - - "path": "js/workers/", - "local-path": "./shared/js/workers/" - }, { /* shared developer single css files */ "type": "css", "search-pattern": /.*\.css$/, @@ -572,33 +555,6 @@ const WEB_APP_FILE_LIST = [ /* web specific */ - { /* generated assembly files */ - "web-only": true, - "type": "wasm", - "search-pattern": /.*\.(wasm)/, - "build-target": "dev|rel", - - "path": "wasm/", - "local-path": "./asm/generated/" - }, - { /* generated assembly javascript files */ - "web-only": true, - "type": "js", - "search-pattern": /.*\.(js)/, - "build-target": "dev|rel", - - "path": "wasm/", - "local-path": "./asm/generated/" - }, - { /* web generated worker codec */ - "web-only": true, - "type": "js", - "search-pattern": /(WorkerCodec.js)$/, - "build-target": "dev|rel", - - "path": "js/workers/", - "local-path": "./web/js/workers/" - }, { /* web css files */ "web-only": true, "type": "css", diff --git a/loader/app/app.ts b/loader/app/app.ts index 4847410a..f5cb23f4 100644 --- a/loader/app/app.ts +++ b/loader/app/app.ts @@ -103,11 +103,9 @@ const loader_javascript = { await loader.scripts.load("vendor/jsrender/jsrender.min.js", { cache_tag: cache_tag() }); await loader.scripts.load_multiple([ ["vendor/xbbcode/src/parser.js"], - ["vendor/moment/moment.js"], ["vendor/twemoji/twemoji.min.js", ""], /* empty string means not required */ ["vendor/highlight/highlight.pack.js", ""], /* empty string means not required */ ["vendor/remarkable/remarkable.min.js", ""], /* empty string means not required */ - ["adapter/adapter-latest.js", "https://webrtc.github.io/adapter/adapter-latest.js"] ], { cache_tag: cache_tag(), max_parallel_requests: -1 diff --git a/package-lock.json b/package-lock.json index edf9fd4b..09bf9591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2059,6 +2059,11 @@ "domelementtype": "^2.0.1" } }, + "dompurify": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.8.tgz", + "integrity": "sha512-vIOSyOXkMx81ghEalh4MLBtDHMx1bhKlaqHDMqM2yeitJ996SLOk5mGdDpI9ifJAgokred8Rmu219fX4OltqXw==" + }, "domutils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz", @@ -5875,8 +5880,7 @@ "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", - "dev": true + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, "move-concurrently": { "version": "1.0.1", @@ -7703,6 +7707,14 @@ "inherits": "^2.0.1" } }, + "rtcpeerconnection-shim": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz", + "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==", + "requires": { + "sdp": "^2.6.0" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -7937,6 +7949,11 @@ } } }, + "sdp": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz", + "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -10340,6 +10357,15 @@ "source-map": "~0.6.1" } }, + "webrtc-adapter": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.5.1.tgz", + "integrity": "sha512-R5LkIR/APjODkstSXFOztOmINXQ0nqIGfUoKTtCzjyiDXHNgwhkqZ9vi8UzGyjfUBibuZ0ZzVyV10qtuLGW3CQ==", + "requires": { + "rtcpeerconnection-shim": "^1.2.15", + "sdp": "^2.12.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 7b3a1823..30177f40 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,10 @@ }, "homepage": "https://www.teaspeak.de", "dependencies": { + "dompurify": "^2.0.8", + "moment": "^2.24.0", "react": "^16.13.1", - "react-dom": "^16.13.1" + "react-dom": "^16.13.1", + "webrtc-adapter": "^7.5.1" } } diff --git a/shared/adapter/adapter-latest.js b/shared/adapter/adapter-latest.js deleted file mode 100644 index 840af398..00000000 --- a/shared/adapter/adapter-latest.js +++ /dev/null @@ -1,5283 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}, - window = _ref.window; - - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { - shimChrome: true, - shimFirefox: true, - shimEdge: true, - shimSafari: true - }; - - // Utils. - var logging = utils.log; - var browserDetails = utils.detectBrowser(window); - - var adapter = { - browserDetails: browserDetails, - commonShim: commonShim, - extractVersion: utils.extractVersion, - disableLog: utils.disableLog, - disableWarnings: utils.disableWarnings - }; - - // Shim browser if found. - switch (browserDetails.browser) { - case 'chrome': - if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) { - logging('Chrome shim is not included in this adapter release.'); - return adapter; - } - logging('adapter.js shimming chrome.'); - // Export to the adapter global object visible in the browser. - adapter.browserShim = chromeShim; - - chromeShim.shimGetUserMedia(window); - chromeShim.shimMediaStream(window); - chromeShim.shimPeerConnection(window); - chromeShim.shimOnTrack(window); - chromeShim.shimAddTrackRemoveTrack(window); - chromeShim.shimGetSendersWithDtmf(window); - chromeShim.shimGetStats(window); - chromeShim.shimSenderReceiverGetStats(window); - chromeShim.fixNegotiationNeeded(window); - - commonShim.shimRTCIceCandidate(window); - commonShim.shimConnectionState(window); - commonShim.shimMaxMessageSize(window); - commonShim.shimSendThrowTypeError(window); - commonShim.removeAllowExtmapMixed(window); - break; - case 'firefox': - if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) { - logging('Firefox shim is not included in this adapter release.'); - return adapter; - } - logging('adapter.js shimming firefox.'); - // Export to the adapter global object visible in the browser. - adapter.browserShim = firefoxShim; - - firefoxShim.shimGetUserMedia(window); - firefoxShim.shimPeerConnection(window); - firefoxShim.shimOnTrack(window); - firefoxShim.shimRemoveStream(window); - firefoxShim.shimSenderGetStats(window); - firefoxShim.shimReceiverGetStats(window); - firefoxShim.shimRTCDataChannel(window); - - commonShim.shimRTCIceCandidate(window); - commonShim.shimConnectionState(window); - commonShim.shimMaxMessageSize(window); - commonShim.shimSendThrowTypeError(window); - break; - case 'edge': - if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) { - logging('MS edge shim is not included in this adapter release.'); - return adapter; - } - logging('adapter.js shimming edge.'); - // Export to the adapter global object visible in the browser. - adapter.browserShim = edgeShim; - - edgeShim.shimGetUserMedia(window); - edgeShim.shimGetDisplayMedia(window); - edgeShim.shimPeerConnection(window); - edgeShim.shimReplaceTrack(window); - - // the edge shim implements the full RTCIceCandidate object. - - commonShim.shimMaxMessageSize(window); - commonShim.shimSendThrowTypeError(window); - break; - case 'safari': - if (!safariShim || !options.shimSafari) { - logging('Safari shim is not included in this adapter release.'); - return adapter; - } - logging('adapter.js shimming safari.'); - // Export to the adapter global object visible in the browser. - adapter.browserShim = safariShim; - - safariShim.shimRTCIceServerUrls(window); - safariShim.shimCreateOfferLegacy(window); - safariShim.shimCallbacksAPI(window); - safariShim.shimLocalStreamsAPI(window); - safariShim.shimRemoteStreamsAPI(window); - safariShim.shimTrackEventTransceiver(window); - safariShim.shimGetUserMedia(window); - - commonShim.shimRTCIceCandidate(window); - commonShim.shimMaxMessageSize(window); - commonShim.shimSendThrowTypeError(window); - commonShim.removeAllowExtmapMixed(window); - break; - default: - logging('Unsupported browser!'); - break; - } - - return adapter; - } - -// Browser shims. - - },{"./chrome/chrome_shim":3,"./common_shim":6,"./edge/edge_shim":7,"./firefox/firefox_shim":11,"./safari/safari_shim":14,"./utils":15}],3:[function(require,module,exports){ - - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - var _getusermedia = require('./getusermedia'); - - Object.defineProperty(exports, 'shimGetUserMedia', { - enumerable: true, - get: function get() { - return _getusermedia.shimGetUserMedia; - } - }); - - var _getdisplaymedia = require('./getdisplaymedia'); - - Object.defineProperty(exports, 'shimGetDisplayMedia', { - enumerable: true, - get: function get() { - return _getdisplaymedia.shimGetDisplayMedia; - } - }); - exports.shimMediaStream = shimMediaStream; - exports.shimOnTrack = shimOnTrack; - exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf; - exports.shimGetStats = shimGetStats; - exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats; - exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative; - exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack; - exports.shimPeerConnection = shimPeerConnection; - exports.fixNegotiationNeeded = fixNegotiationNeeded; - - var _utils = require('../utils.js'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function shimMediaStream(window) { - window.MediaStream = window.MediaStream || window.webkitMediaStream; - } - - function shimOnTrack(window) { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) { - Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { - get: function get() { - return this._ontrack; - }, - set: function set(f) { - if (this._ontrack) { - this.removeEventListener('track', this._ontrack); - } - this.addEventListener('track', this._ontrack = f); - }, - - enumerable: true, - configurable: true - }); - var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; - window.RTCPeerConnection.prototype.setRemoteDescription = function () { - var _this = this; - - if (!this._ontrackpoly) { - this._ontrackpoly = function (e) { - // onaddstream does not fire when a track is added to an existing - // stream. But stream.onaddtrack is implemented so we use that. - e.stream.addEventListener('addtrack', function (te) { - var receiver = void 0; - if (window.RTCPeerConnection.prototype.getReceivers) { - receiver = _this.getReceivers().find(function (r) { - return r.track && r.track.id === te.track.id; - }); - } else { - receiver = { track: te.track }; - } - - var event = new Event('track'); - event.track = te.track; - event.receiver = receiver; - event.transceiver = { receiver: receiver }; - event.streams = [e.stream]; - _this.dispatchEvent(event); - }); - e.stream.getTracks().forEach(function (track) { - var receiver = void 0; - if (window.RTCPeerConnection.prototype.getReceivers) { - receiver = _this.getReceivers().find(function (r) { - return r.track && r.track.id === track.id; - }); - } else { - receiver = { track: track }; - } - var event = new Event('track'); - event.track = track; - event.receiver = receiver; - event.transceiver = { receiver: receiver }; - event.streams = [e.stream]; - _this.dispatchEvent(event); - }); - }; - this.addEventListener('addstream', this._ontrackpoly); - } - return origSetRemoteDescription.apply(this, arguments); - }; - } else { - // even if RTCRtpTransceiver is in window, it is only used and - // emitted in unified-plan. Unfortunately this means we need - // to unconditionally wrap the event. - utils.wrapPeerConnectionEvent(window, 'track', function (e) { - if (!e.transceiver) { - Object.defineProperty(e, 'transceiver', { value: { receiver: e.receiver } }); - } - return e; - }); - } - } - - function shimGetSendersWithDtmf(window) { - // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack. - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) { - var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) { - return { - track: track, - get dtmf() { - if (this._dtmf === undefined) { - if (track.kind === 'audio') { - this._dtmf = pc.createDTMFSender(track); - } else { - this._dtmf = null; - } - } - return this._dtmf; - }, - _pc: pc - }; - }; - - // augment addTrack when getSenders is not available. - if (!window.RTCPeerConnection.prototype.getSenders) { - window.RTCPeerConnection.prototype.getSenders = function () { - this._senders = this._senders || []; - return this._senders.slice(); // return a copy of the internal state. - }; - var origAddTrack = window.RTCPeerConnection.prototype.addTrack; - window.RTCPeerConnection.prototype.addTrack = function (track, stream) { - var sender = origAddTrack.apply(this, arguments); - if (!sender) { - sender = shimSenderWithDtmf(this, track); - this._senders.push(sender); - } - return sender; - }; - - var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; - window.RTCPeerConnection.prototype.removeTrack = function (sender) { - origRemoveTrack.apply(this, arguments); - var idx = this._senders.indexOf(sender); - if (idx !== -1) { - this._senders.splice(idx, 1); - } - }; - } - var origAddStream = window.RTCPeerConnection.prototype.addStream; - window.RTCPeerConnection.prototype.addStream = function (stream) { - var _this2 = this; - - this._senders = this._senders || []; - origAddStream.apply(this, [stream]); - stream.getTracks().forEach(function (track) { - _this2._senders.push(shimSenderWithDtmf(_this2, track)); - }); - }; - - var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; - window.RTCPeerConnection.prototype.removeStream = function (stream) { - var _this3 = this; - - this._senders = this._senders || []; - origRemoveStream.apply(this, [stream]); - - stream.getTracks().forEach(function (track) { - var sender = _this3._senders.find(function (s) { - return s.track === track; - }); - if (sender) { - // remove sender - _this3._senders.splice(_this3._senders.indexOf(sender), 1); - } - }); - }; - } else if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) { - var origGetSenders = window.RTCPeerConnection.prototype.getSenders; - window.RTCPeerConnection.prototype.getSenders = function () { - var _this4 = this; - - var senders = origGetSenders.apply(this, []); - senders.forEach(function (sender) { - return sender._pc = _this4; - }); - return senders; - }; - - Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { - get: function get() { - if (this._dtmf === undefined) { - if (this.track.kind === 'audio') { - this._dtmf = this._pc.createDTMFSender(this.track); - } else { - this._dtmf = null; - } - } - return this._dtmf; - } - }); - } - } - - function shimGetStats(window) { - if (!window.RTCPeerConnection) { - return; - } - - var origGetStats = window.RTCPeerConnection.prototype.getStats; - window.RTCPeerConnection.prototype.getStats = function (selector, successCallback, errorCallback) { - var _this5 = this; - - var args = arguments; - - // If selector is a function then we are in the old style stats so just - // pass back the original getStats format to avoid breaking old users. - if (arguments.length > 0 && typeof selector === 'function') { - return origGetStats.apply(this, arguments); - } - - // When spec-style getStats is supported, return those when called with - // either no arguments or the selector argument is null. - if (origGetStats.length === 0 && (arguments.length === 0 || typeof arguments[0] !== 'function')) { - return origGetStats.apply(this, []); - } - - var fixChromeStats_ = function fixChromeStats_(response) { - var standardReport = {}; - var reports = response.result(); - reports.forEach(function (report) { - var standardStats = { - id: report.id, - timestamp: report.timestamp, - type: { - localcandidate: 'local-candidate', - remotecandidate: 'remote-candidate' - }[report.type] || report.type - }; - report.names().forEach(function (name) { - standardStats[name] = report.stat(name); - }); - standardReport[standardStats.id] = standardStats; - }); - - return standardReport; - }; - - // shim getStats with maplike support - var makeMapStats = function makeMapStats(stats) { - return new Map(Object.keys(stats).map(function (key) { - return [key, stats[key]]; - })); - }; - - if (arguments.length >= 2) { - var successCallbackWrapper_ = function successCallbackWrapper_(response) { - args[1](makeMapStats(fixChromeStats_(response))); - }; - - return origGetStats.apply(this, [successCallbackWrapper_, arguments[0]]); - } - - // promise-support - return new Promise(function (resolve, reject) { - origGetStats.apply(_this5, [function (response) { - resolve(makeMapStats(fixChromeStats_(response))); - }, reject]); - }).then(successCallback, errorCallback); - }; - } - - function shimSenderReceiverGetStats(window) { - if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) { - return; - } - - // shim sender stats. - if (!('getStats' in window.RTCRtpSender.prototype)) { - var origGetSenders = window.RTCPeerConnection.prototype.getSenders; - if (origGetSenders) { - window.RTCPeerConnection.prototype.getSenders = function () { - var _this6 = this; - - var senders = origGetSenders.apply(this, []); - senders.forEach(function (sender) { - return sender._pc = _this6; - }); - return senders; - }; - } - - var origAddTrack = window.RTCPeerConnection.prototype.addTrack; - if (origAddTrack) { - window.RTCPeerConnection.prototype.addTrack = function () { - var sender = origAddTrack.apply(this, arguments); - sender._pc = this; - return sender; - }; - } - window.RTCRtpSender.prototype.getStats = function () { - var sender = this; - return this._pc.getStats().then(function (result) { - return ( - /* Note: this will include stats of all senders that - * send a track with the same id as sender.track as - * it is not possible to identify the RTCRtpSender. - */ - utils.filterStats(result, sender.track, true) - ); - }); - }; - } - - // shim receiver stats. - if (!('getStats' in window.RTCRtpReceiver.prototype)) { - var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; - if (origGetReceivers) { - window.RTCPeerConnection.prototype.getReceivers = function () { - var _this7 = this; - - var receivers = origGetReceivers.apply(this, []); - receivers.forEach(function (receiver) { - return receiver._pc = _this7; - }); - return receivers; - }; - } - utils.wrapPeerConnectionEvent(window, 'track', function (e) { - e.receiver._pc = e.srcElement; - return e; - }); - window.RTCRtpReceiver.prototype.getStats = function () { - var receiver = this; - return this._pc.getStats().then(function (result) { - return utils.filterStats(result, receiver.track, false); - }); - }; - } - - if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) { - return; - } - - // shim RTCPeerConnection.getStats(track). - var origGetStats = window.RTCPeerConnection.prototype.getStats; - window.RTCPeerConnection.prototype.getStats = function () { - if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) { - var track = arguments[0]; - var sender = void 0; - var receiver = void 0; - var err = void 0; - this.getSenders().forEach(function (s) { - if (s.track === track) { - if (sender) { - err = true; - } else { - sender = s; - } - } - }); - this.getReceivers().forEach(function (r) { - if (r.track === track) { - if (receiver) { - err = true; - } else { - receiver = r; - } - } - return r.track === track; - }); - if (err || sender && receiver) { - return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError')); - } else if (sender) { - return sender.getStats(); - } else if (receiver) { - return receiver.getStats(); - } - return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError')); - } - return origGetStats.apply(this, arguments); - }; - } - - function shimAddTrackRemoveTrackWithNative(window) { - // shim addTrack/removeTrack with native variants in order to make - // the interactions with legacy getLocalStreams behave as in other browsers. - // Keeps a mapping stream.id => [stream, rtpsenders...] - window.RTCPeerConnection.prototype.getLocalStreams = function () { - var _this8 = this; - - this._shimmedLocalStreams = this._shimmedLocalStreams || {}; - return Object.keys(this._shimmedLocalStreams).map(function (streamId) { - return _this8._shimmedLocalStreams[streamId][0]; - }); - }; - - var origAddTrack = window.RTCPeerConnection.prototype.addTrack; - window.RTCPeerConnection.prototype.addTrack = function (track, stream) { - if (!stream) { - return origAddTrack.apply(this, arguments); - } - this._shimmedLocalStreams = this._shimmedLocalStreams || {}; - - var sender = origAddTrack.apply(this, arguments); - if (!this._shimmedLocalStreams[stream.id]) { - this._shimmedLocalStreams[stream.id] = [stream, sender]; - } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) { - this._shimmedLocalStreams[stream.id].push(sender); - } - return sender; - }; - - var origAddStream = window.RTCPeerConnection.prototype.addStream; - window.RTCPeerConnection.prototype.addStream = function (stream) { - var _this9 = this; - - this._shimmedLocalStreams = this._shimmedLocalStreams || {}; - - stream.getTracks().forEach(function (track) { - var alreadyExists = _this9.getSenders().find(function (s) { - return s.track === track; - }); - if (alreadyExists) { - throw new DOMException('Track already exists.', 'InvalidAccessError'); - } - }); - var existingSenders = this.getSenders(); - origAddStream.apply(this, arguments); - var newSenders = this.getSenders().filter(function (newSender) { - return existingSenders.indexOf(newSender) === -1; - }); - this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders); - }; - - var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; - window.RTCPeerConnection.prototype.removeStream = function (stream) { - this._shimmedLocalStreams = this._shimmedLocalStreams || {}; - delete this._shimmedLocalStreams[stream.id]; - return origRemoveStream.apply(this, arguments); - }; - - var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; - window.RTCPeerConnection.prototype.removeTrack = function (sender) { - var _this10 = this; - - this._shimmedLocalStreams = this._shimmedLocalStreams || {}; - if (sender) { - Object.keys(this._shimmedLocalStreams).forEach(function (streamId) { - var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender); - if (idx !== -1) { - _this10._shimmedLocalStreams[streamId].splice(idx, 1); - } - if (_this10._shimmedLocalStreams[streamId].length === 1) { - delete _this10._shimmedLocalStreams[streamId]; - } - }); - } - return origRemoveTrack.apply(this, arguments); - }; - } - - function shimAddTrackRemoveTrack(window) { - if (!window.RTCPeerConnection) { - return; - } - var browserDetails = utils.detectBrowser(window); - // shim addTrack and removeTrack. - if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) { - return shimAddTrackRemoveTrackWithNative(window); - } - - // also shim pc.getLocalStreams when addTrack is shimmed - // to return the original streams. - var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams; - window.RTCPeerConnection.prototype.getLocalStreams = function () { - var _this11 = this; - - var nativeStreams = origGetLocalStreams.apply(this); - this._reverseStreams = this._reverseStreams || {}; - return nativeStreams.map(function (stream) { - return _this11._reverseStreams[stream.id]; - }); - }; - - var origAddStream = window.RTCPeerConnection.prototype.addStream; - window.RTCPeerConnection.prototype.addStream = function (stream) { - var _this12 = this; - - this._streams = this._streams || {}; - this._reverseStreams = this._reverseStreams || {}; - - stream.getTracks().forEach(function (track) { - var alreadyExists = _this12.getSenders().find(function (s) { - return s.track === track; - }); - if (alreadyExists) { - throw new DOMException('Track already exists.', 'InvalidAccessError'); - } - }); - // Add identity mapping for consistency with addTrack. - // Unless this is being used with a stream from addTrack. - if (!this._reverseStreams[stream.id]) { - var newStream = new window.MediaStream(stream.getTracks()); - this._streams[stream.id] = newStream; - this._reverseStreams[newStream.id] = stream; - stream = newStream; - } - origAddStream.apply(this, [stream]); - }; - - var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; - window.RTCPeerConnection.prototype.removeStream = function (stream) { - this._streams = this._streams || {}; - this._reverseStreams = this._reverseStreams || {}; - - origRemoveStream.apply(this, [this._streams[stream.id] || stream]); - delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id]; - delete this._streams[stream.id]; - }; - - window.RTCPeerConnection.prototype.addTrack = function (track, stream) { - var _this13 = this; - - if (this.signalingState === 'closed') { - throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); - } - var streams = [].slice.call(arguments, 1); - if (streams.length !== 1 || !streams[0].getTracks().find(function (t) { - return t === track; - })) { - // this is not fully correct but all we can manage without - // [[associated MediaStreams]] internal slot. - throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError'); - } - - var alreadyExists = this.getSenders().find(function (s) { - return s.track === track; - }); - if (alreadyExists) { - throw new DOMException('Track already exists.', 'InvalidAccessError'); - } - - this._streams = this._streams || {}; - this._reverseStreams = this._reverseStreams || {}; - var oldStream = this._streams[stream.id]; - if (oldStream) { - // this is using odd Chrome behaviour, use with caution: - // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815 - // Note: we rely on the high-level addTrack/dtmf shim to - // create the sender with a dtmf sender. - oldStream.addTrack(track); - - // Trigger ONN async. - Promise.resolve().then(function () { - _this13.dispatchEvent(new Event('negotiationneeded')); - }); - } else { - var newStream = new window.MediaStream([track]); - this._streams[stream.id] = newStream; - this._reverseStreams[newStream.id] = stream; - this.addStream(newStream); - } - return this.getSenders().find(function (s) { - return s.track === track; - }); - }; - - // replace the internal stream id with the external one and - // vice versa. - function replaceInternalStreamId(pc, description) { - var sdp = description.sdp; - Object.keys(pc._reverseStreams || []).forEach(function (internalId) { - var externalStream = pc._reverseStreams[internalId]; - var internalStream = pc._streams[externalStream.id]; - sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id); - }); - return new RTCSessionDescription({ - type: description.type, - sdp: sdp - }); - } - function replaceExternalStreamId(pc, description) { - var sdp = description.sdp; - Object.keys(pc._reverseStreams || []).forEach(function (internalId) { - var externalStream = pc._reverseStreams[internalId]; - var internalStream = pc._streams[externalStream.id]; - sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id); - }); - return new RTCSessionDescription({ - type: description.type, - sdp: sdp - }); - } - ['createOffer', 'createAnswer'].forEach(function (method) { - var nativeMethod = window.RTCPeerConnection.prototype[method]; - window.RTCPeerConnection.prototype[method] = function () { - var _this14 = this; - - var args = arguments; - var isLegacyCall = arguments.length && typeof arguments[0] === 'function'; - if (isLegacyCall) { - return nativeMethod.apply(this, [function (description) { - var desc = replaceInternalStreamId(_this14, description); - args[0].apply(null, [desc]); - }, function (err) { - if (args[1]) { - args[1].apply(null, err); - } - }, arguments[2]]); - } - return nativeMethod.apply(this, arguments).then(function (description) { - return replaceInternalStreamId(_this14, description); - }); - }; - }); - - var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription; - window.RTCPeerConnection.prototype.setLocalDescription = function () { - if (!arguments.length || !arguments[0].type) { - return origSetLocalDescription.apply(this, arguments); - } - arguments[0] = replaceExternalStreamId(this, arguments[0]); - return origSetLocalDescription.apply(this, arguments); - }; - - // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier - - var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription'); - Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', { - get: function get() { - var description = origLocalDescription.get.apply(this); - if (description.type === '') { - return description; - } - return replaceInternalStreamId(this, description); - } - }); - - window.RTCPeerConnection.prototype.removeTrack = function (sender) { - var _this15 = this; - - if (this.signalingState === 'closed') { - throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); - } - // We can not yet check for sender instanceof RTCRtpSender - // since we shim RTPSender. So we check if sender._pc is set. - if (!sender._pc) { - throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError'); - } - var isLocal = sender._pc === this; - if (!isLocal) { - throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError'); - } - - // Search for the native stream the senders track belongs to. - this._streams = this._streams || {}; - var stream = void 0; - Object.keys(this._streams).forEach(function (streamid) { - var hasTrack = _this15._streams[streamid].getTracks().find(function (track) { - return sender.track === track; - }); - if (hasTrack) { - stream = _this15._streams[streamid]; - } - }); - - if (stream) { - if (stream.getTracks().length === 1) { - // if this is the last track of the stream, remove the stream. This - // takes care of any shimmed _senders. - this.removeStream(this._reverseStreams[stream.id]); - } else { - // relying on the same odd chrome behaviour as above. - stream.removeTrack(sender.track); - } - this.dispatchEvent(new Event('negotiationneeded')); - } - }; - } - - function shimPeerConnection(window) { - var browserDetails = utils.detectBrowser(window); - - if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) { - // very basic support for old versions. - window.RTCPeerConnection = window.webkitRTCPeerConnection; - } - if (!window.RTCPeerConnection) { - return; - } - - // shim implicit creation of RTCSessionDescription/RTCIceCandidate - if (browserDetails.version < 53) { - ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { - var nativeMethod = window.RTCPeerConnection.prototype[method]; - window.RTCPeerConnection.prototype[method] = function () { - arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); - return nativeMethod.apply(this, arguments); - }; - }); - } - - // support for addIceCandidate(null or undefined) - var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate; - window.RTCPeerConnection.prototype.addIceCandidate = function () { - if (!arguments[0]) { - if (arguments[1]) { - arguments[1].apply(null); - } - return Promise.resolve(); - } - return nativeAddIceCandidate.apply(this, arguments); - }; - } - - function fixNegotiationNeeded(window) { - utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) { - var pc = e.target; - if (pc.signalingState !== 'stable') { - return; - } - return e; - }); - } - - },{"../utils.js":15,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){ - /* - * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = shimGetDisplayMedia; - function shimGetDisplayMedia(window, getSourceId) { - if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { - return; - } - if (!window.navigator.mediaDevices) { - return; - } - // getSourceId is a function that returns a promise resolving with - // the sourceId of the screen/window/tab to be shared. - if (typeof getSourceId !== 'function') { - console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function'); - return; - } - window.navigator.mediaDevices.getDisplayMedia = function (constraints) { - return getSourceId(constraints).then(function (sourceId) { - var widthSpecified = constraints.video && constraints.video.width; - var heightSpecified = constraints.video && constraints.video.height; - var frameRateSpecified = constraints.video && constraints.video.frameRate; - constraints.video = { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: sourceId, - maxFrameRate: frameRateSpecified || 3 - } - }; - if (widthSpecified) { - constraints.video.mandatory.maxWidth = widthSpecified; - } - if (heightSpecified) { - constraints.video.mandatory.maxHeight = heightSpecified; - } - return window.navigator.mediaDevices.getUserMedia(constraints); - }); - }; - } - - },{}],5:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports.shimGetUserMedia = shimGetUserMedia; - - var _utils = require('../utils.js'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - var logging = utils.log; - - function shimGetUserMedia(window) { - var navigator = window && window.navigator; - - if (!navigator.mediaDevices) { - return; - } - - var browserDetails = utils.detectBrowser(window); - - var constraintsToChrome_ = function constraintsToChrome_(c) { - if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) { - return c; - } - var cc = {}; - Object.keys(c).forEach(function (key) { - if (key === 'require' || key === 'advanced' || key === 'mediaSource') { - return; - } - var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] }; - if (r.exact !== undefined && typeof r.exact === 'number') { - r.min = r.max = r.exact; - } - var oldname_ = function oldname_(prefix, name) { - if (prefix) { - return prefix + name.charAt(0).toUpperCase() + name.slice(1); - } - return name === 'deviceId' ? 'sourceId' : name; - }; - if (r.ideal !== undefined) { - cc.optional = cc.optional || []; - var oc = {}; - if (typeof r.ideal === 'number') { - oc[oldname_('min', key)] = r.ideal; - cc.optional.push(oc); - oc = {}; - oc[oldname_('max', key)] = r.ideal; - cc.optional.push(oc); - } else { - oc[oldname_('', key)] = r.ideal; - cc.optional.push(oc); - } - } - if (r.exact !== undefined && typeof r.exact !== 'number') { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname_('', key)] = r.exact; - } else { - ['min', 'max'].forEach(function (mix) { - if (r[mix] !== undefined) { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname_(mix, key)] = r[mix]; - } - }); - } - }); - if (c.advanced) { - cc.optional = (cc.optional || []).concat(c.advanced); - } - return cc; - }; - - var shimConstraints_ = function shimConstraints_(constraints, func) { - if (browserDetails.version >= 61) { - return func(constraints); - } - constraints = JSON.parse(JSON.stringify(constraints)); - if (constraints && _typeof(constraints.audio) === 'object') { - var remap = function remap(obj, a, b) { - if (a in obj && !(b in obj)) { - obj[b] = obj[a]; - delete obj[a]; - } - }; - constraints = JSON.parse(JSON.stringify(constraints)); - remap(constraints.audio, 'autoGainControl', 'googAutoGainControl'); - remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression'); - constraints.audio = constraintsToChrome_(constraints.audio); - } - if (constraints && _typeof(constraints.video) === 'object') { - // Shim facingMode for mobile & surface pro. - var face = constraints.video.facingMode; - face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : { ideal: face }); - var getSupportedFacingModeLies = browserDetails.version < 66; - - if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) { - delete constraints.video.facingMode; - var matches = void 0; - if (face.exact === 'environment' || face.ideal === 'environment') { - matches = ['back', 'rear']; - } else if (face.exact === 'user' || face.ideal === 'user') { - matches = ['front']; - } - if (matches) { - // Look for matches in label, or use last cam for back (typical). - return navigator.mediaDevices.enumerateDevices().then(function (devices) { - devices = devices.filter(function (d) { - return d.kind === 'videoinput'; - }); - var dev = devices.find(function (d) { - return matches.some(function (match) { - return d.label.toLowerCase().includes(match); - }); - }); - if (!dev && devices.length && matches.includes('back')) { - dev = devices[devices.length - 1]; // more likely the back cam - } - if (dev) { - constraints.video.deviceId = face.exact ? { exact: dev.deviceId } : { ideal: dev.deviceId }; - } - constraints.video = constraintsToChrome_(constraints.video); - logging('chrome: ' + JSON.stringify(constraints)); - return func(constraints); - }); - } - } - constraints.video = constraintsToChrome_(constraints.video); - } - logging('chrome: ' + JSON.stringify(constraints)); - return func(constraints); - }; - - var shimError_ = function shimError_(e) { - if (browserDetails.version >= 64) { - return e; - } - return { - name: { - PermissionDeniedError: 'NotAllowedError', - PermissionDismissedError: 'NotAllowedError', - InvalidStateError: 'NotAllowedError', - DevicesNotFoundError: 'NotFoundError', - ConstraintNotSatisfiedError: 'OverconstrainedError', - TrackStartError: 'NotReadableError', - MediaDeviceFailedDueToShutdown: 'NotAllowedError', - MediaDeviceKillSwitchOn: 'NotAllowedError', - TabCaptureError: 'AbortError', - ScreenCaptureError: 'AbortError', - DeviceCaptureError: 'AbortError' - }[e.name] || e.name, - message: e.message, - constraint: e.constraint || e.constraintName, - toString: function toString() { - return this.name + (this.message && ': ') + this.message; - } - }; - }; - - var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) { - shimConstraints_(constraints, function (c) { - navigator.webkitGetUserMedia(c, onSuccess, function (e) { - if (onError) { - onError(shimError_(e)); - } - }); - }); - }; - navigator.getUserMedia = getUserMedia_.bind(navigator); - - // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia - // function which returns a Promise, it does not accept spec-style - // constraints. - if (navigator.mediaDevices.getUserMedia) { - var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); - navigator.mediaDevices.getUserMedia = function (cs) { - return shimConstraints_(cs, function (c) { - return origGetUserMedia(c).then(function (stream) { - if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) { - stream.getTracks().forEach(function (track) { - track.stop(); - }); - throw new DOMException('', 'NotFoundError'); - } - return stream; - }, function (e) { - return Promise.reject(shimError_(e)); - }); - }); - }; - } - } - - },{"../utils.js":15}],6:[function(require,module,exports){ - /* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports.shimRTCIceCandidate = shimRTCIceCandidate; - exports.shimMaxMessageSize = shimMaxMessageSize; - exports.shimSendThrowTypeError = shimSendThrowTypeError; - exports.shimConnectionState = shimConnectionState; - exports.removeAllowExtmapMixed = removeAllowExtmapMixed; - - var _sdp = require('sdp'); - - var _sdp2 = _interopRequireDefault(_sdp); - - var _utils = require('./utils'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - function shimRTCIceCandidate(window) { - // foundation is arbitrarily chosen as an indicator for full support for - // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface - if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) { - return; - } - - var NativeRTCIceCandidate = window.RTCIceCandidate; - window.RTCIceCandidate = function (args) { - // Remove the a= which shouldn't be part of the candidate string. - if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) { - args = JSON.parse(JSON.stringify(args)); - args.candidate = args.candidate.substr(2); - } - - if (args.candidate && args.candidate.length) { - // Augment the native candidate with the parsed fields. - var nativeCandidate = new NativeRTCIceCandidate(args); - var parsedCandidate = _sdp2.default.parseCandidate(args.candidate); - var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate); - - // Add a serializer that does not serialize the extra attributes. - augmentedCandidate.toJSON = function () { - return { - candidate: augmentedCandidate.candidate, - sdpMid: augmentedCandidate.sdpMid, - sdpMLineIndex: augmentedCandidate.sdpMLineIndex, - usernameFragment: augmentedCandidate.usernameFragment - }; - }; - return augmentedCandidate; - } - return new NativeRTCIceCandidate(args); - }; - window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype; - - // Hook up the augmented candidate in onicecandidate and - // addEventListener('icecandidate', ...) - utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) { - if (e.candidate) { - Object.defineProperty(e, 'candidate', { - value: new window.RTCIceCandidate(e.candidate), - writable: 'false' - }); - } - return e; - }); - } - - function shimMaxMessageSize(window) { - if (window.RTCSctpTransport || !window.RTCPeerConnection) { - return; - } - var browserDetails = utils.detectBrowser(window); - - if (!('sctp' in window.RTCPeerConnection.prototype)) { - Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', { - get: function get() { - return typeof this._sctp === 'undefined' ? null : this._sctp; - } - }); - } - - var sctpInDescription = function sctpInDescription(description) { - if (!description || !description.sdp) { - return false; - } - var sections = _sdp2.default.splitSections(description.sdp); - sections.shift(); - return sections.some(function (mediaSection) { - var mLine = _sdp2.default.parseMLine(mediaSection); - return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1; - }); - }; - - var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) { - // TODO: Is there a better solution for detecting Firefox? - var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/); - if (match === null || match.length < 2) { - return -1; - } - var version = parseInt(match[1], 10); - // Test for NaN (yes, this is ugly) - return version !== version ? -1 : version; - }; - - var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) { - // Every implementation we know can send at least 64 KiB. - // Note: Although Chrome is technically able to send up to 256 KiB, the - // data does not reach the other peer reliably. - // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419 - var canSendMaxMessageSize = 65536; - if (browserDetails.browser === 'firefox') { - if (browserDetails.version < 57) { - if (remoteIsFirefox === -1) { - // FF < 57 will send in 16 KiB chunks using the deprecated PPID - // fragmentation. - canSendMaxMessageSize = 16384; - } else { - // However, other FF (and RAWRTC) can reassemble PPID-fragmented - // messages. Thus, supporting ~2 GiB when sending. - canSendMaxMessageSize = 2147483637; - } - } else if (browserDetails.version < 60) { - // Currently, all FF >= 57 will reset the remote maximum message size - // to the default value when a data channel is created at a later - // stage. :( - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 - canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536; - } else { - // FF >= 60 supports sending ~2 GiB - canSendMaxMessageSize = 2147483637; - } - } - return canSendMaxMessageSize; - }; - - var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) { - // Note: 65536 bytes is the default value from the SDP spec. Also, - // every implementation we know supports receiving 65536 bytes. - var maxMessageSize = 65536; - - // FF 57 has a slightly incorrect default remote max message size, so - // we need to adjust it here to avoid a failure when sending. - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697 - if (browserDetails.browser === 'firefox' && browserDetails.version === 57) { - maxMessageSize = 65535; - } - - var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:'); - if (match.length > 0) { - maxMessageSize = parseInt(match[0].substr(19), 10); - } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) { - // If the maximum message size is not present in the remote SDP and - // both local and remote are Firefox, the remote peer can receive - // ~2 GiB. - maxMessageSize = 2147483637; - } - return maxMessageSize; - }; - - var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; - window.RTCPeerConnection.prototype.setRemoteDescription = function () { - this._sctp = null; - - if (sctpInDescription(arguments[0])) { - // Check if the remote is FF. - var isFirefox = getRemoteFirefoxVersion(arguments[0]); - - // Get the maximum message size the local peer is capable of sending - var canSendMMS = getCanSendMaxMessageSize(isFirefox); - - // Get the maximum message size of the remote peer. - var remoteMMS = getMaxMessageSize(arguments[0], isFirefox); - - // Determine final maximum message size - var maxMessageSize = void 0; - if (canSendMMS === 0 && remoteMMS === 0) { - maxMessageSize = Number.POSITIVE_INFINITY; - } else if (canSendMMS === 0 || remoteMMS === 0) { - maxMessageSize = Math.max(canSendMMS, remoteMMS); - } else { - maxMessageSize = Math.min(canSendMMS, remoteMMS); - } - - // Create a dummy RTCSctpTransport object and the 'maxMessageSize' - // attribute. - var sctp = {}; - Object.defineProperty(sctp, 'maxMessageSize', { - get: function get() { - return maxMessageSize; - } - }); - this._sctp = sctp; - } - - return origSetRemoteDescription.apply(this, arguments); - }; - } - - function shimSendThrowTypeError(window) { - if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) { - return; - } - - // Note: Although Firefox >= 57 has a native implementation, the maximum - // message size can be reset for all data channels at a later stage. - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 - - function wrapDcSend(dc, pc) { - var origDataChannelSend = dc.send; - dc.send = function () { - var data = arguments[0]; - var length = data.length || data.size || data.byteLength; - if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) { - throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)'); - } - return origDataChannelSend.apply(dc, arguments); - }; - } - var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel; - window.RTCPeerConnection.prototype.createDataChannel = function () { - var dataChannel = origCreateDataChannel.apply(this, arguments); - wrapDcSend(dataChannel, this); - return dataChannel; - }; - utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) { - wrapDcSend(e.channel, e.target); - return e; - }); - } - - /* shims RTCConnectionState by pretending it is the same as iceConnectionState. - * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12 - * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect - * since DTLS failures would be hidden. See - * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827 - * for the Firefox tracking bug. - */ - function shimConnectionState(window) { - if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) { - return; - } - var proto = window.RTCPeerConnection.prototype; - Object.defineProperty(proto, 'connectionState', { - get: function get() { - return { - completed: 'connected', - checking: 'connecting' - }[this.iceConnectionState] || this.iceConnectionState; - }, - - enumerable: true, - configurable: true - }); - Object.defineProperty(proto, 'onconnectionstatechange', { - get: function get() { - return this._onconnectionstatechange || null; - }, - set: function set(cb) { - if (this._onconnectionstatechange) { - this.removeEventListener('connectionstatechange', this._onconnectionstatechange); - delete this._onconnectionstatechange; - } - if (cb) { - this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb); - } - }, - - enumerable: true, - configurable: true - }); - - ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) { - var origMethod = proto[method]; - proto[method] = function () { - if (!this._connectionstatechangepoly) { - this._connectionstatechangepoly = function (e) { - var pc = e.target; - if (pc._lastConnectionState !== pc.connectionState) { - pc._lastConnectionState = pc.connectionState; - var newEvent = new Event('connectionstatechange', e); - pc.dispatchEvent(newEvent); - } - return e; - }; - this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly); - } - return origMethod.apply(this, arguments); - }; - }); - } - - function removeAllowExtmapMixed(window) { - /* remove a=extmap-allow-mixed for Chrome < M71 */ - if (!window.RTCPeerConnection) { - return; - } - var browserDetails = utils.detectBrowser(window); - if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) { - return; - } - var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription; - window.RTCPeerConnection.prototype.setRemoteDescription = function (desc) { - if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) { - desc.sdp = desc.sdp.split('\n').filter(function (line) { - return line.trim() !== 'a=extmap-allow-mixed'; - }).join('\n'); - } - return nativeSRD.apply(this, arguments); - }; - } - - },{"./utils":15,"sdp":17}],7:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined; - - var _getusermedia = require('./getusermedia'); - - Object.defineProperty(exports, 'shimGetUserMedia', { - enumerable: true, - get: function get() { - return _getusermedia.shimGetUserMedia; - } - }); - - var _getdisplaymedia = require('./getdisplaymedia'); - - Object.defineProperty(exports, 'shimGetDisplayMedia', { - enumerable: true, - get: function get() { - return _getdisplaymedia.shimGetDisplayMedia; - } - }); - exports.shimPeerConnection = shimPeerConnection; - exports.shimReplaceTrack = shimReplaceTrack; - - var _utils = require('../utils'); - - var utils = _interopRequireWildcard(_utils); - - var _filtericeservers = require('./filtericeservers'); - - var _rtcpeerconnectionShim = require('rtcpeerconnection-shim'); - - var _rtcpeerconnectionShim2 = _interopRequireDefault(_rtcpeerconnectionShim); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function shimPeerConnection(window) { - var browserDetails = utils.detectBrowser(window); - - if (window.RTCIceGatherer) { - if (!window.RTCIceCandidate) { - window.RTCIceCandidate = function (args) { - return args; - }; - } - if (!window.RTCSessionDescription) { - window.RTCSessionDescription = function (args) { - return args; - }; - } - // this adds an additional event listener to MediaStrackTrack that signals - // when a tracks enabled property was changed. Workaround for a bug in - // addStream, see below. No longer required in 15025+ - if (browserDetails.version < 15025) { - var origMSTEnabled = Object.getOwnPropertyDescriptor(window.MediaStreamTrack.prototype, 'enabled'); - Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', { - set: function set(value) { - origMSTEnabled.set.call(this, value); - var ev = new Event('enabled'); - ev.enabled = value; - this.dispatchEvent(ev); - } - }); - } - } - - // ORTC defines the DTMF sender a bit different. - // https://github.com/w3c/ortc/issues/714 - if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) { - Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { - get: function get() { - if (this._dtmf === undefined) { - if (this.track.kind === 'audio') { - this._dtmf = new window.RTCDtmfSender(this); - } else if (this.track.kind === 'video') { - this._dtmf = null; - } - } - return this._dtmf; - } - }); - } - // Edge currently only implements the RTCDtmfSender, not the - // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2* - if (window.RTCDtmfSender && !window.RTCDTMFSender) { - window.RTCDTMFSender = window.RTCDtmfSender; - } - - var RTCPeerConnectionShim = (0, _rtcpeerconnectionShim2.default)(window, browserDetails.version); - window.RTCPeerConnection = function (config) { - if (config && config.iceServers) { - config.iceServers = (0, _filtericeservers.filterIceServers)(config.iceServers, browserDetails.version); - utils.log('ICE servers after filtering:', config.iceServers); - } - return new RTCPeerConnectionShim(config); - }; - window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype; - } - - function shimReplaceTrack(window) { - // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614 - if (window.RTCRtpSender && !('replaceTrack' in window.RTCRtpSender.prototype)) { - window.RTCRtpSender.prototype.replaceTrack = window.RTCRtpSender.prototype.setTrack; - } - } - - },{"../utils":15,"./filtericeservers":8,"./getdisplaymedia":9,"./getusermedia":10,"rtcpeerconnection-shim":16}],8:[function(require,module,exports){ - /* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.filterIceServers = filterIceServers; - - var _utils = require('../utils'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -// Edge does not like -// 1) stun: filtered after 14393 unless ?transport=udp is present -// 2) turn: that does not have all of turn:host:port?transport=udp -// 3) turn: with ipv6 addresses -// 4) turn: occurring muliple times - function filterIceServers(iceServers, edgeVersion) { - var hasTurn = false; - iceServers = JSON.parse(JSON.stringify(iceServers)); - return iceServers.filter(function (server) { - if (server && (server.urls || server.url)) { - var urls = server.urls || server.url; - if (server.url && !server.urls) { - utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls'); - } - var isString = typeof urls === 'string'; - if (isString) { - urls = [urls]; - } - urls = urls.filter(function (url) { - // filter STUN unconditionally. - if (url.indexOf('stun:') === 0) { - return false; - } - - var validTurn = url.startsWith('turn') && !url.startsWith('turn:[') && url.includes('transport=udp'); - if (validTurn && !hasTurn) { - hasTurn = true; - return true; - } - return validTurn && !hasTurn; - }); - - delete server.url; - server.urls = isString ? urls[0] : urls; - return !!urls.length; - } - }); - } - - },{"../utils":15}],9:[function(require,module,exports){ - /* - * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = shimGetDisplayMedia; - function shimGetDisplayMedia(window) { - if (!('getDisplayMedia' in window.navigator)) { - return; - } - if (!window.navigator.mediaDevices) { - return; - } - if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { - return; - } - window.navigator.mediaDevices.getDisplayMedia = window.navigator.getDisplayMedia.bind(window.navigator); - } - - },{}],10:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetUserMedia = shimGetUserMedia; - function shimGetUserMedia(window) { - var navigator = window && window.navigator; - - var shimError_ = function shimError_(e) { - return { - name: { PermissionDeniedError: 'NotAllowedError' }[e.name] || e.name, - message: e.message, - constraint: e.constraint, - toString: function toString() { - return this.name; - } - }; - }; - - // getUserMedia error shim. - var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); - navigator.mediaDevices.getUserMedia = function (c) { - return origGetUserMedia(c).catch(function (e) { - return Promise.reject(shimError_(e)); - }); - }; - } - - },{}],11:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined; - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - var _getusermedia = require('./getusermedia'); - - Object.defineProperty(exports, 'shimGetUserMedia', { - enumerable: true, - get: function get() { - return _getusermedia.shimGetUserMedia; - } - }); - - var _getdisplaymedia = require('./getdisplaymedia'); - - Object.defineProperty(exports, 'shimGetDisplayMedia', { - enumerable: true, - get: function get() { - return _getdisplaymedia.shimGetDisplayMedia; - } - }); - exports.shimOnTrack = shimOnTrack; - exports.shimPeerConnection = shimPeerConnection; - exports.shimSenderGetStats = shimSenderGetStats; - exports.shimReceiverGetStats = shimReceiverGetStats; - exports.shimRemoveStream = shimRemoveStream; - exports.shimRTCDataChannel = shimRTCDataChannel; - - var _utils = require('../utils'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function shimOnTrack(window) { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) { - Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { - get: function get() { - return { receiver: this.receiver }; - } - }); - } - } - - function shimPeerConnection(window) { - var browserDetails = utils.detectBrowser(window); - - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) { - return; // probably media.peerconnection.enabled=false in about:config - } - if (!window.RTCPeerConnection && window.mozRTCPeerConnection) { - // very basic support for old versions. - window.RTCPeerConnection = window.mozRTCPeerConnection; - } - - if (browserDetails.version < 53) { - // shim away need for obsolete RTCIceCandidate/RTCSessionDescription. - ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { - var nativeMethod = window.RTCPeerConnection.prototype[method]; - window.RTCPeerConnection.prototype[method] = function () { - arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); - return nativeMethod.apply(this, arguments); - }; - }); - } - - // support for addIceCandidate(null or undefined) - var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate; - window.RTCPeerConnection.prototype.addIceCandidate = function () { - if (!arguments[0]) { - if (arguments[1]) { - arguments[1].apply(null); - } - return Promise.resolve(); - } - return nativeAddIceCandidate.apply(this, arguments); - }; - - var modernStatsTypes = { - inboundrtp: 'inbound-rtp', - outboundrtp: 'outbound-rtp', - candidatepair: 'candidate-pair', - localcandidate: 'local-candidate', - remotecandidate: 'remote-candidate' - }; - - var nativeGetStats = window.RTCPeerConnection.prototype.getStats; - window.RTCPeerConnection.prototype.getStats = function (selector, onSucc, onErr) { - return nativeGetStats.apply(this, [selector || null]).then(function (stats) { - if (browserDetails.version < 53 && !onSucc) { - // Shim only promise getStats with spec-hyphens in type names - // Leave callback version alone; misc old uses of forEach before Map - try { - stats.forEach(function (stat) { - stat.type = modernStatsTypes[stat.type] || stat.type; - }); - } catch (e) { - if (e.name !== 'TypeError') { - throw e; - } - // Avoid TypeError: "type" is read-only, in old versions. 34-43ish - stats.forEach(function (stat, i) { - stats.set(i, Object.assign({}, stat, { - type: modernStatsTypes[stat.type] || stat.type - })); - }); - } - } - return stats; - }).then(onSucc, onErr); - }; - } - - function shimSenderGetStats(window) { - if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { - return; - } - if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) { - return; - } - var origGetSenders = window.RTCPeerConnection.prototype.getSenders; - if (origGetSenders) { - window.RTCPeerConnection.prototype.getSenders = function () { - var _this = this; - - var senders = origGetSenders.apply(this, []); - senders.forEach(function (sender) { - return sender._pc = _this; - }); - return senders; - }; - } - - var origAddTrack = window.RTCPeerConnection.prototype.addTrack; - if (origAddTrack) { - window.RTCPeerConnection.prototype.addTrack = function () { - var sender = origAddTrack.apply(this, arguments); - sender._pc = this; - return sender; - }; - } - window.RTCRtpSender.prototype.getStats = function () { - return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map()); - }; - } - - function shimReceiverGetStats(window) { - if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { - return; - } - if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) { - return; - } - var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; - if (origGetReceivers) { - window.RTCPeerConnection.prototype.getReceivers = function () { - var _this2 = this; - - var receivers = origGetReceivers.apply(this, []); - receivers.forEach(function (receiver) { - return receiver._pc = _this2; - }); - return receivers; - }; - } - utils.wrapPeerConnectionEvent(window, 'track', function (e) { - e.receiver._pc = e.srcElement; - return e; - }); - window.RTCRtpReceiver.prototype.getStats = function () { - return this._pc.getStats(this.track); - }; - } - - function shimRemoveStream(window) { - if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) { - return; - } - window.RTCPeerConnection.prototype.removeStream = function (stream) { - var _this3 = this; - - utils.deprecated('removeStream', 'removeTrack'); - this.getSenders().forEach(function (sender) { - if (sender.track && stream.getTracks().includes(sender.track)) { - _this3.removeTrack(sender); - } - }); - }; - } - - function shimRTCDataChannel(window) { - // rename DataChannel to RTCDataChannel (native fix in FF60): - // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851 - if (window.DataChannel && !window.RTCDataChannel) { - window.RTCDataChannel = window.DataChannel; - } - } - - },{"../utils":15,"./getdisplaymedia":12,"./getusermedia":13}],12:[function(require,module,exports){ - /* - * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.shimGetDisplayMedia = shimGetDisplayMedia; - function shimGetDisplayMedia(window, preferredMediaSource) { - if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { - return; - } - if (!window.navigator.mediaDevices) { - return; - } - window.navigator.mediaDevices.getDisplayMedia = function (constraints) { - if (!(constraints && constraints.video)) { - var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined'); - err.name = 'NotFoundError'; - // from https://heycam.github.io/webidl/#idl-DOMException-error-names - err.code = 8; - return Promise.reject(err); - } - if (constraints.video === true) { - constraints.video = { mediaSource: preferredMediaSource }; - } else { - constraints.video.mediaSource = preferredMediaSource; - } - return window.navigator.mediaDevices.getUserMedia(constraints); - }; - } - - },{}],13:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports.shimGetUserMedia = shimGetUserMedia; - - var _utils = require('../utils'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function shimGetUserMedia(window) { - var browserDetails = utils.detectBrowser(window); - var navigator = window && window.navigator; - var MediaStreamTrack = window && window.MediaStreamTrack; - - navigator.getUserMedia = function (constraints, onSuccess, onError) { - // Replace Firefox 44+'s deprecation warning with unprefixed version. - utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia'); - navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError); - }; - - if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) { - var remap = function remap(obj, a, b) { - if (a in obj && !(b in obj)) { - obj[b] = obj[a]; - delete obj[a]; - } - }; - - var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); - navigator.mediaDevices.getUserMedia = function (c) { - if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') { - c = JSON.parse(JSON.stringify(c)); - remap(c.audio, 'autoGainControl', 'mozAutoGainControl'); - remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression'); - } - return nativeGetUserMedia(c); - }; - - if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) { - var nativeGetSettings = MediaStreamTrack.prototype.getSettings; - MediaStreamTrack.prototype.getSettings = function () { - var obj = nativeGetSettings.apply(this, arguments); - remap(obj, 'mozAutoGainControl', 'autoGainControl'); - remap(obj, 'mozNoiseSuppression', 'noiseSuppression'); - return obj; - }; - } - - if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) { - var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints; - MediaStreamTrack.prototype.applyConstraints = function (c) { - if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') { - c = JSON.parse(JSON.stringify(c)); - remap(c, 'autoGainControl', 'mozAutoGainControl'); - remap(c, 'noiseSuppression', 'mozNoiseSuppression'); - } - return nativeApplyConstraints.apply(this, [c]); - }; - } - } - } - - },{"../utils":15}],14:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports.shimLocalStreamsAPI = shimLocalStreamsAPI; - exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI; - exports.shimCallbacksAPI = shimCallbacksAPI; - exports.shimGetUserMedia = shimGetUserMedia; - exports.shimConstraints = shimConstraints; - exports.shimRTCIceServerUrls = shimRTCIceServerUrls; - exports.shimTrackEventTransceiver = shimTrackEventTransceiver; - exports.shimCreateOfferLegacy = shimCreateOfferLegacy; - - var _utils = require('../utils'); - - var utils = _interopRequireWildcard(_utils); - - function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - - function shimLocalStreamsAPI(window) { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { - return; - } - if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) { - window.RTCPeerConnection.prototype.getLocalStreams = function () { - if (!this._localStreams) { - this._localStreams = []; - } - return this._localStreams; - }; - } - if (!('addStream' in window.RTCPeerConnection.prototype)) { - var _addTrack = window.RTCPeerConnection.prototype.addTrack; - window.RTCPeerConnection.prototype.addStream = function (stream) { - var _this = this; - - if (!this._localStreams) { - this._localStreams = []; - } - if (!this._localStreams.includes(stream)) { - this._localStreams.push(stream); - } - // Try to emulate Chrome's behaviour of adding in audio-video order. - // Safari orders by track id. - stream.getAudioTracks().forEach(function (track) { - return _addTrack.call(_this, track, stream); - }); - stream.getVideoTracks().forEach(function (track) { - return _addTrack.call(_this, track, stream); - }); - }; - - window.RTCPeerConnection.prototype.addTrack = function (track, stream) { - if (stream) { - if (!this._localStreams) { - this._localStreams = [stream]; - } else if (!this._localStreams.includes(stream)) { - this._localStreams.push(stream); - } - } - return _addTrack.call(this, track, stream); - }; - } - if (!('removeStream' in window.RTCPeerConnection.prototype)) { - window.RTCPeerConnection.prototype.removeStream = function (stream) { - var _this2 = this; - - if (!this._localStreams) { - this._localStreams = []; - } - var index = this._localStreams.indexOf(stream); - if (index === -1) { - return; - } - this._localStreams.splice(index, 1); - var tracks = stream.getTracks(); - this.getSenders().forEach(function (sender) { - if (tracks.includes(sender.track)) { - _this2.removeTrack(sender); - } - }); - }; - } - } - - function shimRemoteStreamsAPI(window) { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { - return; - } - if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) { - window.RTCPeerConnection.prototype.getRemoteStreams = function () { - return this._remoteStreams ? this._remoteStreams : []; - }; - } - if (!('onaddstream' in window.RTCPeerConnection.prototype)) { - Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', { - get: function get() { - return this._onaddstream; - }, - set: function set(f) { - var _this3 = this; - - if (this._onaddstream) { - this.removeEventListener('addstream', this._onaddstream); - this.removeEventListener('track', this._onaddstreampoly); - } - this.addEventListener('addstream', this._onaddstream = f); - this.addEventListener('track', this._onaddstreampoly = function (e) { - e.streams.forEach(function (stream) { - if (!_this3._remoteStreams) { - _this3._remoteStreams = []; - } - if (_this3._remoteStreams.includes(stream)) { - return; - } - _this3._remoteStreams.push(stream); - var event = new Event('addstream'); - event.stream = stream; - _this3.dispatchEvent(event); - }); - }); - } - }); - var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; - window.RTCPeerConnection.prototype.setRemoteDescription = function () { - var pc = this; - if (!this._onaddstreampoly) { - this.addEventListener('track', this._onaddstreampoly = function (e) { - e.streams.forEach(function (stream) { - if (!pc._remoteStreams) { - pc._remoteStreams = []; - } - if (pc._remoteStreams.indexOf(stream) >= 0) { - return; - } - pc._remoteStreams.push(stream); - var event = new Event('addstream'); - event.stream = stream; - pc.dispatchEvent(event); - }); - }); - } - return origSetRemoteDescription.apply(pc, arguments); - }; - } - } - - function shimCallbacksAPI(window) { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) { - return; - } - var prototype = window.RTCPeerConnection.prototype; - var createOffer = prototype.createOffer; - var createAnswer = prototype.createAnswer; - var setLocalDescription = prototype.setLocalDescription; - var setRemoteDescription = prototype.setRemoteDescription; - var addIceCandidate = prototype.addIceCandidate; - - prototype.createOffer = function (successCallback, failureCallback) { - var options = arguments.length >= 2 ? arguments[2] : arguments[0]; - var promise = createOffer.apply(this, [options]); - if (!failureCallback) { - return promise; - } - promise.then(successCallback, failureCallback); - return Promise.resolve(); - }; - - prototype.createAnswer = function (successCallback, failureCallback) { - var options = arguments.length >= 2 ? arguments[2] : arguments[0]; - var promise = createAnswer.apply(this, [options]); - if (!failureCallback) { - return promise; - } - promise.then(successCallback, failureCallback); - return Promise.resolve(); - }; - - var withCallback = function withCallback(description, successCallback, failureCallback) { - var promise = setLocalDescription.apply(this, [description]); - if (!failureCallback) { - return promise; - } - promise.then(successCallback, failureCallback); - return Promise.resolve(); - }; - prototype.setLocalDescription = withCallback; - - withCallback = function withCallback(description, successCallback, failureCallback) { - var promise = setRemoteDescription.apply(this, [description]); - if (!failureCallback) { - return promise; - } - promise.then(successCallback, failureCallback); - return Promise.resolve(); - }; - prototype.setRemoteDescription = withCallback; - - withCallback = function withCallback(candidate, successCallback, failureCallback) { - var promise = addIceCandidate.apply(this, [candidate]); - if (!failureCallback) { - return promise; - } - promise.then(successCallback, failureCallback); - return Promise.resolve(); - }; - prototype.addIceCandidate = withCallback; - } - - function shimGetUserMedia(window) { - var navigator = window && window.navigator; - - if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - // shim not needed in Safari 12.1 - var mediaDevices = navigator.mediaDevices; - var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices); - navigator.mediaDevices.getUserMedia = function (constraints) { - return _getUserMedia(shimConstraints(constraints)); - }; - } - - if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - navigator.getUserMedia = function (constraints, cb, errcb) { - navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb); - }.bind(navigator); - } - } - - function shimConstraints(constraints) { - if (constraints && constraints.video !== undefined) { - return Object.assign({}, constraints, { video: utils.compactObject(constraints.video) }); - } - - return constraints; - } - - function shimRTCIceServerUrls(window) { - // migrate from non-spec RTCIceServer.url to RTCIceServer.urls - var OrigPeerConnection = window.RTCPeerConnection; - window.RTCPeerConnection = function (pcConfig, pcConstraints) { - if (pcConfig && pcConfig.iceServers) { - var newIceServers = []; - for (var i = 0; i < pcConfig.iceServers.length; i++) { - var server = pcConfig.iceServers[i]; - if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) { - utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls'); - server = JSON.parse(JSON.stringify(server)); - server.urls = server.url; - delete server.url; - newIceServers.push(server); - } else { - newIceServers.push(pcConfig.iceServers[i]); - } - } - pcConfig.iceServers = newIceServers; - } - return new OrigPeerConnection(pcConfig, pcConstraints); - }; - window.RTCPeerConnection.prototype = OrigPeerConnection.prototype; - // wrap static methods. Currently just generateCertificate. - if ('generateCertificate' in window.RTCPeerConnection) { - Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { - get: function get() { - return OrigPeerConnection.generateCertificate; - } - }); - } - } - - function shimTrackEventTransceiver(window) { - // Add event.transceiver member over deprecated event.receiver - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'receiver' in window.RTCTrackEvent.prototype && - // can't check 'transceiver' in window.RTCTrackEvent.prototype, as it is - // defined for some reason even when window.RTCTransceiver is not. - !window.RTCTransceiver) { - Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { - get: function get() { - return { receiver: this.receiver }; - } - }); - } - } - - function shimCreateOfferLegacy(window) { - var origCreateOffer = window.RTCPeerConnection.prototype.createOffer; - window.RTCPeerConnection.prototype.createOffer = function (offerOptions) { - if (offerOptions) { - if (typeof offerOptions.offerToReceiveAudio !== 'undefined') { - // support bit values - offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio; - } - var audioTransceiver = this.getTransceivers().find(function (transceiver) { - return transceiver.receiver.track.kind === 'audio'; - }); - if (offerOptions.offerToReceiveAudio === false && audioTransceiver) { - if (audioTransceiver.direction === 'sendrecv') { - if (audioTransceiver.setDirection) { - audioTransceiver.setDirection('sendonly'); - } else { - audioTransceiver.direction = 'sendonly'; - } - } else if (audioTransceiver.direction === 'recvonly') { - if (audioTransceiver.setDirection) { - audioTransceiver.setDirection('inactive'); - } else { - audioTransceiver.direction = 'inactive'; - } - } - } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) { - this.addTransceiver('audio'); - } - - if (typeof offerOptions.offerToReceiveVideo !== 'undefined') { - // support bit values - offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo; - } - var videoTransceiver = this.getTransceivers().find(function (transceiver) { - return transceiver.receiver.track.kind === 'video'; - }); - if (offerOptions.offerToReceiveVideo === false && videoTransceiver) { - if (videoTransceiver.direction === 'sendrecv') { - if (videoTransceiver.setDirection) { - videoTransceiver.setDirection('sendonly'); - } else { - videoTransceiver.direction = 'sendonly'; - } - } else if (videoTransceiver.direction === 'recvonly') { - if (videoTransceiver.setDirection) { - videoTransceiver.setDirection('inactive'); - } else { - videoTransceiver.direction = 'inactive'; - } - } - } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) { - this.addTransceiver('video'); - } - } - return origCreateOffer.apply(this, arguments); - }; - } - - },{"../utils":15}],15:[function(require,module,exports){ - /* - * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - - exports.extractVersion = extractVersion; - exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent; - exports.disableLog = disableLog; - exports.disableWarnings = disableWarnings; - exports.log = log; - exports.deprecated = deprecated; - exports.detectBrowser = detectBrowser; - exports.compactObject = compactObject; - exports.walkStats = walkStats; - exports.filterStats = filterStats; - - function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - var logDisabled_ = true; - var deprecationWarnings_ = true; - - /** - * Extract browser version out of the provided user agent string. - * - * @param {!string} uastring userAgent string. - * @param {!string} expr Regular expression used as match criteria. - * @param {!number} pos position in the version string to be returned. - * @return {!number} browser version. - */ - function extractVersion(uastring, expr, pos) { - var match = uastring.match(expr); - return match && match.length >= pos && parseInt(match[pos], 10); - } - -// Wraps the peerconnection event eventNameToWrap in a function -// which returns the modified event object (or false to prevent -// the event). - function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) { - if (!window.RTCPeerConnection) { - return; - } - var proto = window.RTCPeerConnection.prototype; - var nativeAddEventListener = proto.addEventListener; - proto.addEventListener = function (nativeEventName, cb) { - if (nativeEventName !== eventNameToWrap) { - return nativeAddEventListener.apply(this, arguments); - } - var wrappedCallback = function wrappedCallback(e) { - var modifiedEvent = wrapper(e); - if (modifiedEvent) { - cb(modifiedEvent); - } - }; - this._eventMap = this._eventMap || {}; - this._eventMap[cb] = wrappedCallback; - return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]); - }; - - var nativeRemoveEventListener = proto.removeEventListener; - proto.removeEventListener = function (nativeEventName, cb) { - if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[cb]) { - return nativeRemoveEventListener.apply(this, arguments); - } - var unwrappedCb = this._eventMap[cb]; - delete this._eventMap[cb]; - return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]); - }; - - Object.defineProperty(proto, 'on' + eventNameToWrap, { - get: function get() { - return this['_on' + eventNameToWrap]; - }, - set: function set(cb) { - if (this['_on' + eventNameToWrap]) { - this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]); - delete this['_on' + eventNameToWrap]; - } - if (cb) { - this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb); - } - }, - - enumerable: true, - configurable: true - }); - } - - function disableLog(bool) { - if (typeof bool !== 'boolean') { - return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.'); - } - logDisabled_ = bool; - return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled'; - } - - /** - * Disable or enable deprecation warnings - * @param {!boolean} bool set to true to disable warnings. - */ - function disableWarnings(bool) { - if (typeof bool !== 'boolean') { - return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.'); - } - deprecationWarnings_ = !bool; - return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled'); - } - - function log() { - if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') { - if (logDisabled_) { - return; - } - if (typeof console !== 'undefined' && typeof console.log === 'function') { - console.log.apply(console, arguments); - } - } - } - - /** - * Shows a deprecation warning suggesting the modern and spec-compatible API. - */ - function deprecated(oldMethod, newMethod) { - if (!deprecationWarnings_) { - return; - } - console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.'); - } - - /** - * Browser detector. - * - * @return {object} result containing browser and version - * properties. - */ - function detectBrowser(window) { - var navigator = window.navigator; - - // Returned result object. - - var result = { browser: null, version: null }; - - // Fail early if it's not a browser - if (typeof window === 'undefined' || !window.navigator) { - result.browser = 'Not a browser.'; - return result; - } - - if (navigator.mozGetUserMedia) { - // Firefox. - result.browser = 'firefox'; - result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1); - } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection && !window.RTCIceGatherer) { - // Chrome, Chromium, Webview, Opera. - // Version matches Chrome/WebRTC version. - // Chrome 74 removed webkitGetUserMedia on http as well so we need the - // more complicated fallback to webkitRTCPeerConnection. - result.browser = 'chrome'; - result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2); - } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { - // Edge. - result.browser = 'edge'; - result.version = extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2); - } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { - // Safari. - result.browser = 'safari'; - result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1); - result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype; - } else { - // Default fallthrough: not supported. - result.browser = 'Not a supported browser.'; - return result; - } - - return result; - } - - /** - * Checks if something is an object. - * - * @param {*} val The something you want to check. - * @return true if val is an object, false otherwise. - */ - function isObject(val) { - return Object.prototype.toString.call(val) === '[object Object]'; - } - - /** - * Remove all empty objects and undefined values - * from a nested object -- an enhanced and vanilla version - * of Lodash's `compact`. - */ - function compactObject(data) { - if (!isObject(data)) { - return data; - } - - return Object.keys(data).reduce(function (accumulator, key) { - var isObj = isObject(data[key]); - var value = isObj ? compactObject(data[key]) : data[key]; - var isEmptyObject = isObj && !Object.keys(value).length; - if (value === undefined || isEmptyObject) { - return accumulator; - } - return Object.assign(accumulator, _defineProperty({}, key, value)); - }, {}); - } - - /* iterates the stats graph recursively. */ - function walkStats(stats, base, resultSet) { - if (!base || resultSet.has(base.id)) { - return; - } - resultSet.set(base.id, base); - Object.keys(base).forEach(function (name) { - if (name.endsWith('Id')) { - walkStats(stats, stats.get(base[name]), resultSet); - } else if (name.endsWith('Ids')) { - base[name].forEach(function (id) { - walkStats(stats, stats.get(id), resultSet); - }); - } - }); - } - - /* filter getStats for a sender/receiver track. */ - function filterStats(result, track, outbound) { - var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp'; - var filteredResult = new Map(); - if (track === null) { - return filteredResult; - } - var trackStats = []; - result.forEach(function (value) { - if (value.type === 'track' && value.trackIdentifier === track.id) { - trackStats.push(value); - } - }); - trackStats.forEach(function (trackStat) { - result.forEach(function (stats) { - if (stats.type === streamStatsType && stats.trackId === trackStat.id) { - walkStats(result, stats, filteredResult); - } - }); - }); - return filteredResult; - } - - },{}],16:[function(require,module,exports){ - /* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. - */ - /* eslint-env node */ - 'use strict'; - - var SDPUtils = require('sdp'); - - function fixStatsType(stat) { - return { - inboundrtp: 'inbound-rtp', - outboundrtp: 'outbound-rtp', - candidatepair: 'candidate-pair', - localcandidate: 'local-candidate', - remotecandidate: 'remote-candidate' - }[stat.type] || stat.type; - } - - function writeMediaSection(transceiver, caps, type, stream, dtlsRole) { - var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); - - // Map ICE parameters (ufrag, pwd) to SDP. - sdp += SDPUtils.writeIceParameters( - transceiver.iceGatherer.getLocalParameters()); - - // Map DTLS parameters to SDP. - sdp += SDPUtils.writeDtlsParameters( - transceiver.dtlsTransport.getLocalParameters(), - type === 'offer' ? 'actpass' : dtlsRole || 'active'); - - sdp += 'a=mid:' + transceiver.mid + '\r\n'; - - if (transceiver.rtpSender && transceiver.rtpReceiver) { - sdp += 'a=sendrecv\r\n'; - } else if (transceiver.rtpSender) { - sdp += 'a=sendonly\r\n'; - } else if (transceiver.rtpReceiver) { - sdp += 'a=recvonly\r\n'; - } else { - sdp += 'a=inactive\r\n'; - } - - if (transceiver.rtpSender) { - var trackId = transceiver.rtpSender._initialTrackId || - transceiver.rtpSender.track.id; - transceiver.rtpSender._initialTrackId = trackId; - // spec. - var msid = 'msid:' + (stream ? stream.id : '-') + ' ' + - trackId + '\r\n'; - sdp += 'a=' + msid; - // for Chrome. Legacy should no longer be required. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' ' + msid; - - // RTX - if (transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' ' + msid; - sdp += 'a=ssrc-group:FID ' + - transceiver.sendEncodingParameters[0].ssrc + ' ' + - transceiver.sendEncodingParameters[0].rtx.ssrc + - '\r\n'; - } - } - // FIXME: this should be written by writeRtpDescription. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - } - return sdp; - } - -// Edge does not like -// 1) stun: filtered after 14393 unless ?transport=udp is present -// 2) turn: that does not have all of turn:host:port?transport=udp -// 3) turn: with ipv6 addresses -// 4) turn: occurring muliple times - function filterIceServers(iceServers, edgeVersion) { - var hasTurn = false; - iceServers = JSON.parse(JSON.stringify(iceServers)); - return iceServers.filter(function(server) { - if (server && (server.urls || server.url)) { - var urls = server.urls || server.url; - if (server.url && !server.urls) { - console.warn('RTCIceServer.url is deprecated! Use urls instead.'); - } - var isString = typeof urls === 'string'; - if (isString) { - urls = [urls]; - } - urls = urls.filter(function(url) { - var validTurn = url.indexOf('turn:') === 0 && - url.indexOf('transport=udp') !== -1 && - url.indexOf('turn:[') === -1 && - !hasTurn; - - if (validTurn) { - hasTurn = true; - return true; - } - return url.indexOf('stun:') === 0 && edgeVersion >= 14393 && - url.indexOf('?transport=udp') === -1; - }); - - delete server.url; - server.urls = isString ? urls[0] : urls; - return !!urls.length; - } - }); - } - -// Determines the intersection of local and remote capabilities. - function getCommonCapabilities(localCapabilities, remoteCapabilities) { - var commonCapabilities = { - codecs: [], - headerExtensions: [], - fecMechanisms: [] - }; - - var findCodecByPayloadType = function(pt, codecs) { - pt = parseInt(pt, 10); - for (var i = 0; i < codecs.length; i++) { - if (codecs[i].payloadType === pt || - codecs[i].preferredPayloadType === pt) { - return codecs[i]; - } - } - }; - - var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) { - var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs); - var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs); - return lCodec && rCodec && - lCodec.name.toLowerCase() === rCodec.name.toLowerCase(); - }; - - localCapabilities.codecs.forEach(function(lCodec) { - for (var i = 0; i < remoteCapabilities.codecs.length; i++) { - var rCodec = remoteCapabilities.codecs[i]; - if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() && - lCodec.clockRate === rCodec.clockRate) { - if (lCodec.name.toLowerCase() === 'rtx' && - lCodec.parameters && rCodec.parameters.apt) { - // for RTX we need to find the local rtx that has a apt - // which points to the same local codec as the remote one. - if (!rtxCapabilityMatches(lCodec, rCodec, - localCapabilities.codecs, remoteCapabilities.codecs)) { - continue; - } - } - rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy - // number of channels is the highest common number of channels - rCodec.numChannels = Math.min(lCodec.numChannels, - rCodec.numChannels); - // push rCodec so we reply with offerer payload type - commonCapabilities.codecs.push(rCodec); - - // determine common feedback mechanisms - rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) { - for (var j = 0; j < lCodec.rtcpFeedback.length; j++) { - if (lCodec.rtcpFeedback[j].type === fb.type && - lCodec.rtcpFeedback[j].parameter === fb.parameter) { - return true; - } - } - return false; - }); - // FIXME: also need to determine .parameters - // see https://github.com/openpeer/ortc/issues/569 - break; - } - } - }); - - localCapabilities.headerExtensions.forEach(function(lHeaderExtension) { - for (var i = 0; i < remoteCapabilities.headerExtensions.length; - i++) { - var rHeaderExtension = remoteCapabilities.headerExtensions[i]; - if (lHeaderExtension.uri === rHeaderExtension.uri) { - commonCapabilities.headerExtensions.push(rHeaderExtension); - break; - } - } - }); - - // FIXME: fecMechanisms - return commonCapabilities; - } - -// is action=setLocalDescription with type allowed in signalingState - function isActionAllowedInSignalingState(action, type, signalingState) { - return { - offer: { - setLocalDescription: ['stable', 'have-local-offer'], - setRemoteDescription: ['stable', 'have-remote-offer'] - }, - answer: { - setLocalDescription: ['have-remote-offer', 'have-local-pranswer'], - setRemoteDescription: ['have-local-offer', 'have-remote-pranswer'] - } - }[type][action].indexOf(signalingState) !== -1; - } - - function maybeAddCandidate(iceTransport, candidate) { - // Edge's internal representation adds some fields therefore - // not all fieldѕ are taken into account. - var alreadyAdded = iceTransport.getRemoteCandidates() - .find(function(remoteCandidate) { - return candidate.foundation === remoteCandidate.foundation && - candidate.ip === remoteCandidate.ip && - candidate.port === remoteCandidate.port && - candidate.priority === remoteCandidate.priority && - candidate.protocol === remoteCandidate.protocol && - candidate.type === remoteCandidate.type; - }); - if (!alreadyAdded) { - iceTransport.addRemoteCandidate(candidate); - } - return !alreadyAdded; - } - - - function makeError(name, description) { - var e = new Error(description); - e.name = name; - // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names - e.code = { - NotSupportedError: 9, - InvalidStateError: 11, - InvalidAccessError: 15, - TypeError: undefined, - OperationError: undefined - }[name]; - return e; - } - - module.exports = function(window, edgeVersion) { - // https://w3c.github.io/mediacapture-main/#mediastream - // Helper function to add the track to the stream and - // dispatch the event ourselves. - function addTrackToStreamAndFireEvent(track, stream) { - stream.addTrack(track); - stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack', - {track: track})); - } - - function removeTrackFromStreamAndFireEvent(track, stream) { - stream.removeTrack(track); - stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack', - {track: track})); - } - - function fireAddTrack(pc, track, receiver, streams) { - var trackEvent = new Event('track'); - trackEvent.track = track; - trackEvent.receiver = receiver; - trackEvent.transceiver = {receiver: receiver}; - trackEvent.streams = streams; - window.setTimeout(function() { - pc._dispatchEvent('track', trackEvent); - }); - } - - var RTCPeerConnection = function(config) { - var pc = this; - - var _eventTarget = document.createDocumentFragment(); - ['addEventListener', 'removeEventListener', 'dispatchEvent'] - .forEach(function(method) { - pc[method] = _eventTarget[method].bind(_eventTarget); - }); - - this.canTrickleIceCandidates = null; - - this.needNegotiation = false; - - this.localStreams = []; - this.remoteStreams = []; - - this._localDescription = null; - this._remoteDescription = null; - - this.signalingState = 'stable'; - this.iceConnectionState = 'new'; - this.connectionState = 'new'; - this.iceGatheringState = 'new'; - - config = JSON.parse(JSON.stringify(config || {})); - - this.usingBundle = config.bundlePolicy === 'max-bundle'; - if (config.rtcpMuxPolicy === 'negotiate') { - throw(makeError('NotSupportedError', - 'rtcpMuxPolicy \'negotiate\' is not supported')); - } else if (!config.rtcpMuxPolicy) { - config.rtcpMuxPolicy = 'require'; - } - - switch (config.iceTransportPolicy) { - case 'all': - case 'relay': - break; - default: - config.iceTransportPolicy = 'all'; - break; - } - - switch (config.bundlePolicy) { - case 'balanced': - case 'max-compat': - case 'max-bundle': - break; - default: - config.bundlePolicy = 'balanced'; - break; - } - - config.iceServers = filterIceServers(config.iceServers || [], edgeVersion); - - this._iceGatherers = []; - if (config.iceCandidatePoolSize) { - for (var i = config.iceCandidatePoolSize; i > 0; i--) { - this._iceGatherers.push(new window.RTCIceGatherer({ - iceServers: config.iceServers, - gatherPolicy: config.iceTransportPolicy - })); - } - } else { - config.iceCandidatePoolSize = 0; - } - - this._config = config; - - // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ... - // everything that is needed to describe a SDP m-line. - this.transceivers = []; - - this._sdpSessionId = SDPUtils.generateSessionId(); - this._sdpSessionVersion = 0; - - this._dtlsRole = undefined; // role for a=setup to use in answers. - - this._isClosed = false; - }; - - Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', { - configurable: true, - get: function() { - return this._localDescription; - } - }); - Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', { - configurable: true, - get: function() { - return this._remoteDescription; - } - }); - - // set up event handlers on prototype - RTCPeerConnection.prototype.onicecandidate = null; - RTCPeerConnection.prototype.onaddstream = null; - RTCPeerConnection.prototype.ontrack = null; - RTCPeerConnection.prototype.onremovestream = null; - RTCPeerConnection.prototype.onsignalingstatechange = null; - RTCPeerConnection.prototype.oniceconnectionstatechange = null; - RTCPeerConnection.prototype.onconnectionstatechange = null; - RTCPeerConnection.prototype.onicegatheringstatechange = null; - RTCPeerConnection.prototype.onnegotiationneeded = null; - RTCPeerConnection.prototype.ondatachannel = null; - - RTCPeerConnection.prototype._dispatchEvent = function(name, event) { - if (this._isClosed) { - return; - } - this.dispatchEvent(event); - if (typeof this['on' + name] === 'function') { - this['on' + name](event); - } - }; - - RTCPeerConnection.prototype._emitGatheringStateChange = function() { - var event = new Event('icegatheringstatechange'); - this._dispatchEvent('icegatheringstatechange', event); - }; - - RTCPeerConnection.prototype.getConfiguration = function() { - return this._config; - }; - - RTCPeerConnection.prototype.getLocalStreams = function() { - return this.localStreams; - }; - - RTCPeerConnection.prototype.getRemoteStreams = function() { - return this.remoteStreams; - }; - - // internal helper to create a transceiver object. - // (which is not yet the same as the WebRTC 1.0 transceiver) - RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) { - var hasBundleTransport = this.transceivers.length > 0; - var transceiver = { - track: null, - iceGatherer: null, - iceTransport: null, - dtlsTransport: null, - localCapabilities: null, - remoteCapabilities: null, - rtpSender: null, - rtpReceiver: null, - kind: kind, - mid: null, - sendEncodingParameters: null, - recvEncodingParameters: null, - stream: null, - associatedRemoteMediaStreams: [], - wantReceive: true - }; - if (this.usingBundle && hasBundleTransport) { - transceiver.iceTransport = this.transceivers[0].iceTransport; - transceiver.dtlsTransport = this.transceivers[0].dtlsTransport; - } else { - var transports = this._createIceAndDtlsTransports(); - transceiver.iceTransport = transports.iceTransport; - transceiver.dtlsTransport = transports.dtlsTransport; - } - if (!doNotAdd) { - this.transceivers.push(transceiver); - } - return transceiver; - }; - - RTCPeerConnection.prototype.addTrack = function(track, stream) { - if (this._isClosed) { - throw makeError('InvalidStateError', - 'Attempted to call addTrack on a closed peerconnection.'); - } - - var alreadyExists = this.transceivers.find(function(s) { - return s.track === track; - }); - - if (alreadyExists) { - throw makeError('InvalidAccessError', 'Track already exists.'); - } - - var transceiver; - for (var i = 0; i < this.transceivers.length; i++) { - if (!this.transceivers[i].track && - this.transceivers[i].kind === track.kind) { - transceiver = this.transceivers[i]; - } - } - if (!transceiver) { - transceiver = this._createTransceiver(track.kind); - } - - this._maybeFireNegotiationNeeded(); - - if (this.localStreams.indexOf(stream) === -1) { - this.localStreams.push(stream); - } - - transceiver.track = track; - transceiver.stream = stream; - transceiver.rtpSender = new window.RTCRtpSender(track, - transceiver.dtlsTransport); - return transceiver.rtpSender; - }; - - RTCPeerConnection.prototype.addStream = function(stream) { - var pc = this; - if (edgeVersion >= 15025) { - stream.getTracks().forEach(function(track) { - pc.addTrack(track, stream); - }); - } else { - // Clone is necessary for local demos mostly, attaching directly - // to two different senders does not work (build 10547). - // Fixed in 15025 (or earlier) - var clonedStream = stream.clone(); - stream.getTracks().forEach(function(track, idx) { - var clonedTrack = clonedStream.getTracks()[idx]; - track.addEventListener('enabled', function(event) { - clonedTrack.enabled = event.enabled; - }); - }); - clonedStream.getTracks().forEach(function(track) { - pc.addTrack(track, clonedStream); - }); - } - }; - - RTCPeerConnection.prototype.removeTrack = function(sender) { - if (this._isClosed) { - throw makeError('InvalidStateError', - 'Attempted to call removeTrack on a closed peerconnection.'); - } - - if (!(sender instanceof window.RTCRtpSender)) { - throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' + - 'does not implement interface RTCRtpSender.'); - } - - var transceiver = this.transceivers.find(function(t) { - return t.rtpSender === sender; - }); - - if (!transceiver) { - throw makeError('InvalidAccessError', - 'Sender was not created by this connection.'); - } - var stream = transceiver.stream; - - transceiver.rtpSender.stop(); - transceiver.rtpSender = null; - transceiver.track = null; - transceiver.stream = null; - - // remove the stream from the set of local streams - var localStreams = this.transceivers.map(function(t) { - return t.stream; - }); - if (localStreams.indexOf(stream) === -1 && - this.localStreams.indexOf(stream) > -1) { - this.localStreams.splice(this.localStreams.indexOf(stream), 1); - } - - this._maybeFireNegotiationNeeded(); - }; - - RTCPeerConnection.prototype.removeStream = function(stream) { - var pc = this; - stream.getTracks().forEach(function(track) { - var sender = pc.getSenders().find(function(s) { - return s.track === track; - }); - if (sender) { - pc.removeTrack(sender); - } - }); - }; - - RTCPeerConnection.prototype.getSenders = function() { - return this.transceivers.filter(function(transceiver) { - return !!transceiver.rtpSender; - }) - .map(function(transceiver) { - return transceiver.rtpSender; - }); - }; - - RTCPeerConnection.prototype.getReceivers = function() { - return this.transceivers.filter(function(transceiver) { - return !!transceiver.rtpReceiver; - }) - .map(function(transceiver) { - return transceiver.rtpReceiver; - }); - }; - - - RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex, - usingBundle) { - var pc = this; - if (usingBundle && sdpMLineIndex > 0) { - return this.transceivers[0].iceGatherer; - } else if (this._iceGatherers.length) { - return this._iceGatherers.shift(); - } - var iceGatherer = new window.RTCIceGatherer({ - iceServers: this._config.iceServers, - gatherPolicy: this._config.iceTransportPolicy - }); - Object.defineProperty(iceGatherer, 'state', - {value: 'new', writable: true} - ); - - this.transceivers[sdpMLineIndex].bufferedCandidateEvents = []; - this.transceivers[sdpMLineIndex].bufferCandidates = function(event) { - var end = !event.candidate || Object.keys(event.candidate).length === 0; - // polyfill since RTCIceGatherer.state is not implemented in - // Edge 10547 yet. - iceGatherer.state = end ? 'completed' : 'gathering'; - if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) { - pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event); - } - }; - iceGatherer.addEventListener('localcandidate', - this.transceivers[sdpMLineIndex].bufferCandidates); - return iceGatherer; - }; - - // start gathering from an RTCIceGatherer. - RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) { - var pc = this; - var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; - if (iceGatherer.onlocalcandidate) { - return; - } - var bufferedCandidateEvents = - this.transceivers[sdpMLineIndex].bufferedCandidateEvents; - this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null; - iceGatherer.removeEventListener('localcandidate', - this.transceivers[sdpMLineIndex].bufferCandidates); - iceGatherer.onlocalcandidate = function(evt) { - if (pc.usingBundle && sdpMLineIndex > 0) { - // if we know that we use bundle we can drop candidates with - // ѕdpMLineIndex > 0. If we don't do this then our state gets - // confused since we dispose the extra ice gatherer. - return; - } - var event = new Event('icecandidate'); - event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex}; - - var cand = evt.candidate; - // Edge emits an empty object for RTCIceCandidateComplete‥ - var end = !cand || Object.keys(cand).length === 0; - if (end) { - // polyfill since RTCIceGatherer.state is not implemented in - // Edge 10547 yet. - if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') { - iceGatherer.state = 'completed'; - } - } else { - if (iceGatherer.state === 'new') { - iceGatherer.state = 'gathering'; - } - // RTCIceCandidate doesn't have a component, needs to be added - cand.component = 1; - // also the usernameFragment. TODO: update SDP to take both variants. - cand.ufrag = iceGatherer.getLocalParameters().usernameFragment; - - var serializedCandidate = SDPUtils.writeCandidate(cand); - event.candidate = Object.assign(event.candidate, - SDPUtils.parseCandidate(serializedCandidate)); - - event.candidate.candidate = serializedCandidate; - event.candidate.toJSON = function() { - return { - candidate: event.candidate.candidate, - sdpMid: event.candidate.sdpMid, - sdpMLineIndex: event.candidate.sdpMLineIndex, - usernameFragment: event.candidate.usernameFragment - }; - }; - } - - // update local description. - var sections = SDPUtils.getMediaSections(pc._localDescription.sdp); - if (!end) { - sections[event.candidate.sdpMLineIndex] += - 'a=' + event.candidate.candidate + '\r\n'; - } else { - sections[event.candidate.sdpMLineIndex] += - 'a=end-of-candidates\r\n'; - } - pc._localDescription.sdp = - SDPUtils.getDescription(pc._localDescription.sdp) + - sections.join(''); - var complete = pc.transceivers.every(function(transceiver) { - return transceiver.iceGatherer && - transceiver.iceGatherer.state === 'completed'; - }); - - if (pc.iceGatheringState !== 'gathering') { - pc.iceGatheringState = 'gathering'; - pc._emitGatheringStateChange(); - } - - // Emit candidate. Also emit null candidate when all gatherers are - // complete. - if (!end) { - pc._dispatchEvent('icecandidate', event); - } - if (complete) { - pc._dispatchEvent('icecandidate', new Event('icecandidate')); - pc.iceGatheringState = 'complete'; - pc._emitGatheringStateChange(); - } - }; - - // emit already gathered candidates. - window.setTimeout(function() { - bufferedCandidateEvents.forEach(function(e) { - iceGatherer.onlocalcandidate(e); - }); - }, 0); - }; - - // Create ICE transport and DTLS transport. - RTCPeerConnection.prototype._createIceAndDtlsTransports = function() { - var pc = this; - var iceTransport = new window.RTCIceTransport(null); - iceTransport.onicestatechange = function() { - pc._updateIceConnectionState(); - pc._updateConnectionState(); - }; - - var dtlsTransport = new window.RTCDtlsTransport(iceTransport); - dtlsTransport.ondtlsstatechange = function() { - pc._updateConnectionState(); - }; - dtlsTransport.onerror = function() { - // onerror does not set state to failed by itself. - Object.defineProperty(dtlsTransport, 'state', - {value: 'failed', writable: true}); - pc._updateConnectionState(); - }; - - return { - iceTransport: iceTransport, - dtlsTransport: dtlsTransport - }; - }; - - // Destroy ICE gatherer, ICE transport and DTLS transport. - // Without triggering the callbacks. - RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function( - sdpMLineIndex) { - var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; - if (iceGatherer) { - delete iceGatherer.onlocalcandidate; - delete this.transceivers[sdpMLineIndex].iceGatherer; - } - var iceTransport = this.transceivers[sdpMLineIndex].iceTransport; - if (iceTransport) { - delete iceTransport.onicestatechange; - delete this.transceivers[sdpMLineIndex].iceTransport; - } - var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport; - if (dtlsTransport) { - delete dtlsTransport.ondtlsstatechange; - delete dtlsTransport.onerror; - delete this.transceivers[sdpMLineIndex].dtlsTransport; - } - }; - - // Start the RTP Sender and Receiver for a transceiver. - RTCPeerConnection.prototype._transceive = function(transceiver, - send, recv) { - var params = getCommonCapabilities(transceiver.localCapabilities, - transceiver.remoteCapabilities); - if (send && transceiver.rtpSender) { - params.encodings = transceiver.sendEncodingParameters; - params.rtcp = { - cname: SDPUtils.localCName, - compound: transceiver.rtcpParameters.compound - }; - if (transceiver.recvEncodingParameters.length) { - params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc; - } - transceiver.rtpSender.send(params); - } - if (recv && transceiver.rtpReceiver && params.codecs.length > 0) { - // remove RTX field in Edge 14942 - if (transceiver.kind === 'video' - && transceiver.recvEncodingParameters - && edgeVersion < 15019) { - transceiver.recvEncodingParameters.forEach(function(p) { - delete p.rtx; - }); - } - if (transceiver.recvEncodingParameters.length) { - params.encodings = transceiver.recvEncodingParameters; - } else { - params.encodings = [{}]; - } - params.rtcp = { - compound: transceiver.rtcpParameters.compound - }; - if (transceiver.rtcpParameters.cname) { - params.rtcp.cname = transceiver.rtcpParameters.cname; - } - if (transceiver.sendEncodingParameters.length) { - params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc; - } - transceiver.rtpReceiver.receive(params); - } - }; - - RTCPeerConnection.prototype.setLocalDescription = function(description) { - var pc = this; - - // Note: pranswer is not supported. - if (['offer', 'answer'].indexOf(description.type) === -1) { - return Promise.reject(makeError('TypeError', - 'Unsupported type "' + description.type + '"')); - } - - if (!isActionAllowedInSignalingState('setLocalDescription', - description.type, pc.signalingState) || pc._isClosed) { - return Promise.reject(makeError('InvalidStateError', - 'Can not set local ' + description.type + - ' in state ' + pc.signalingState)); - } - - var sections; - var sessionpart; - if (description.type === 'offer') { - // VERY limited support for SDP munging. Limited to: - // * changing the order of codecs - sections = SDPUtils.splitSections(description.sdp); - sessionpart = sections.shift(); - sections.forEach(function(mediaSection, sdpMLineIndex) { - var caps = SDPUtils.parseRtpParameters(mediaSection); - pc.transceivers[sdpMLineIndex].localCapabilities = caps; - }); - - pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { - pc._gather(transceiver.mid, sdpMLineIndex); - }); - } else if (description.type === 'answer') { - sections = SDPUtils.splitSections(pc._remoteDescription.sdp); - sessionpart = sections.shift(); - var isIceLite = SDPUtils.matchPrefix(sessionpart, - 'a=ice-lite').length > 0; - sections.forEach(function(mediaSection, sdpMLineIndex) { - var transceiver = pc.transceivers[sdpMLineIndex]; - var iceGatherer = transceiver.iceGatherer; - var iceTransport = transceiver.iceTransport; - var dtlsTransport = transceiver.dtlsTransport; - var localCapabilities = transceiver.localCapabilities; - var remoteCapabilities = transceiver.remoteCapabilities; - - // treat bundle-only as not-rejected. - var rejected = SDPUtils.isRejected(mediaSection) && - SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0; - - if (!rejected && !transceiver.rejected) { - var remoteIceParameters = SDPUtils.getIceParameters( - mediaSection, sessionpart); - var remoteDtlsParameters = SDPUtils.getDtlsParameters( - mediaSection, sessionpart); - if (isIceLite) { - remoteDtlsParameters.role = 'server'; - } - - if (!pc.usingBundle || sdpMLineIndex === 0) { - pc._gather(transceiver.mid, sdpMLineIndex); - if (iceTransport.state === 'new') { - iceTransport.start(iceGatherer, remoteIceParameters, - isIceLite ? 'controlling' : 'controlled'); - } - if (dtlsTransport.state === 'new') { - dtlsTransport.start(remoteDtlsParameters); - } - } - - // Calculate intersection of capabilities. - var params = getCommonCapabilities(localCapabilities, - remoteCapabilities); - - // Start the RTCRtpSender. The RTCRtpReceiver for this - // transceiver has already been started in setRemoteDescription. - pc._transceive(transceiver, - params.codecs.length > 0, - false); - } - }); - } - - pc._localDescription = { - type: description.type, - sdp: description.sdp - }; - if (description.type === 'offer') { - pc._updateSignalingState('have-local-offer'); - } else { - pc._updateSignalingState('stable'); - } - - return Promise.resolve(); - }; - - RTCPeerConnection.prototype.setRemoteDescription = function(description) { - var pc = this; - - // Note: pranswer is not supported. - if (['offer', 'answer'].indexOf(description.type) === -1) { - return Promise.reject(makeError('TypeError', - 'Unsupported type "' + description.type + '"')); - } - - if (!isActionAllowedInSignalingState('setRemoteDescription', - description.type, pc.signalingState) || pc._isClosed) { - return Promise.reject(makeError('InvalidStateError', - 'Can not set remote ' + description.type + - ' in state ' + pc.signalingState)); - } - - var streams = {}; - pc.remoteStreams.forEach(function(stream) { - streams[stream.id] = stream; - }); - var receiverList = []; - var sections = SDPUtils.splitSections(description.sdp); - var sessionpart = sections.shift(); - var isIceLite = SDPUtils.matchPrefix(sessionpart, - 'a=ice-lite').length > 0; - var usingBundle = SDPUtils.matchPrefix(sessionpart, - 'a=group:BUNDLE ').length > 0; - pc.usingBundle = usingBundle; - var iceOptions = SDPUtils.matchPrefix(sessionpart, - 'a=ice-options:')[0]; - if (iceOptions) { - pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ') - .indexOf('trickle') >= 0; - } else { - pc.canTrickleIceCandidates = false; - } - - sections.forEach(function(mediaSection, sdpMLineIndex) { - var lines = SDPUtils.splitLines(mediaSection); - var kind = SDPUtils.getKind(mediaSection); - // treat bundle-only as not-rejected. - var rejected = SDPUtils.isRejected(mediaSection) && - SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0; - var protocol = lines[0].substr(2).split(' ')[2]; - - var direction = SDPUtils.getDirection(mediaSection, sessionpart); - var remoteMsid = SDPUtils.parseMsid(mediaSection); - - var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier(); - - // Reject datachannels which are not implemented yet. - if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' || - protocol === 'UDP/DTLS/SCTP'))) { - // TODO: this is dangerous in the case where a non-rejected m-line - // becomes rejected. - pc.transceivers[sdpMLineIndex] = { - mid: mid, - kind: kind, - protocol: protocol, - rejected: true - }; - return; - } - - if (!rejected && pc.transceivers[sdpMLineIndex] && - pc.transceivers[sdpMLineIndex].rejected) { - // recycle a rejected transceiver. - pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true); - } - - var transceiver; - var iceGatherer; - var iceTransport; - var dtlsTransport; - var rtpReceiver; - var sendEncodingParameters; - var recvEncodingParameters; - var localCapabilities; - - var track; - // FIXME: ensure the mediaSection has rtcp-mux set. - var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection); - var remoteIceParameters; - var remoteDtlsParameters; - if (!rejected) { - remoteIceParameters = SDPUtils.getIceParameters(mediaSection, - sessionpart); - remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, - sessionpart); - remoteDtlsParameters.role = 'client'; - } - recvEncodingParameters = - SDPUtils.parseRtpEncodingParameters(mediaSection); - - var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection); - - var isComplete = SDPUtils.matchPrefix(mediaSection, - 'a=end-of-candidates', sessionpart).length > 0; - var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:') - .map(function(cand) { - return SDPUtils.parseCandidate(cand); - }) - .filter(function(cand) { - return cand.component === 1; - }); - - // Check if we can use BUNDLE and dispose transports. - if ((description.type === 'offer' || description.type === 'answer') && - !rejected && usingBundle && sdpMLineIndex > 0 && - pc.transceivers[sdpMLineIndex]) { - pc._disposeIceAndDtlsTransports(sdpMLineIndex); - pc.transceivers[sdpMLineIndex].iceGatherer = - pc.transceivers[0].iceGatherer; - pc.transceivers[sdpMLineIndex].iceTransport = - pc.transceivers[0].iceTransport; - pc.transceivers[sdpMLineIndex].dtlsTransport = - pc.transceivers[0].dtlsTransport; - if (pc.transceivers[sdpMLineIndex].rtpSender) { - pc.transceivers[sdpMLineIndex].rtpSender.setTransport( - pc.transceivers[0].dtlsTransport); - } - if (pc.transceivers[sdpMLineIndex].rtpReceiver) { - pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport( - pc.transceivers[0].dtlsTransport); - } - } - if (description.type === 'offer' && !rejected) { - transceiver = pc.transceivers[sdpMLineIndex] || - pc._createTransceiver(kind); - transceiver.mid = mid; - - if (!transceiver.iceGatherer) { - transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, - usingBundle); - } - - if (cands.length && transceiver.iceTransport.state === 'new') { - if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { - transceiver.iceTransport.setRemoteCandidates(cands); - } else { - cands.forEach(function(candidate) { - maybeAddCandidate(transceiver.iceTransport, candidate); - }); - } - } - - localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); - - // filter RTX until additional stuff needed for RTX is implemented - // in adapter.js - if (edgeVersion < 15019) { - localCapabilities.codecs = localCapabilities.codecs.filter( - function(codec) { - return codec.name !== 'rtx'; - }); - } - - sendEncodingParameters = transceiver.sendEncodingParameters || [{ - ssrc: (2 * sdpMLineIndex + 2) * 1001 - }]; - - // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams - var isNewTrack = false; - if (direction === 'sendrecv' || direction === 'sendonly') { - isNewTrack = !transceiver.rtpReceiver; - rtpReceiver = transceiver.rtpReceiver || - new window.RTCRtpReceiver(transceiver.dtlsTransport, kind); - - if (isNewTrack) { - var stream; - track = rtpReceiver.track; - // FIXME: does not work with Plan B. - if (remoteMsid && remoteMsid.stream === '-') { - // no-op. a stream id of '-' means: no associated stream. - } else if (remoteMsid) { - if (!streams[remoteMsid.stream]) { - streams[remoteMsid.stream] = new window.MediaStream(); - Object.defineProperty(streams[remoteMsid.stream], 'id', { - get: function() { - return remoteMsid.stream; - } - }); - } - Object.defineProperty(track, 'id', { - get: function() { - return remoteMsid.track; - } - }); - stream = streams[remoteMsid.stream]; - } else { - if (!streams.default) { - streams.default = new window.MediaStream(); - } - stream = streams.default; - } - if (stream) { - addTrackToStreamAndFireEvent(track, stream); - transceiver.associatedRemoteMediaStreams.push(stream); - } - receiverList.push([track, rtpReceiver, stream]); - } - } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) { - transceiver.associatedRemoteMediaStreams.forEach(function(s) { - var nativeTrack = s.getTracks().find(function(t) { - return t.id === transceiver.rtpReceiver.track.id; - }); - if (nativeTrack) { - removeTrackFromStreamAndFireEvent(nativeTrack, s); - } - }); - transceiver.associatedRemoteMediaStreams = []; - } - - transceiver.localCapabilities = localCapabilities; - transceiver.remoteCapabilities = remoteCapabilities; - transceiver.rtpReceiver = rtpReceiver; - transceiver.rtcpParameters = rtcpParameters; - transceiver.sendEncodingParameters = sendEncodingParameters; - transceiver.recvEncodingParameters = recvEncodingParameters; - - // Start the RTCRtpReceiver now. The RTPSender is started in - // setLocalDescription. - pc._transceive(pc.transceivers[sdpMLineIndex], - false, - isNewTrack); - } else if (description.type === 'answer' && !rejected) { - transceiver = pc.transceivers[sdpMLineIndex]; - iceGatherer = transceiver.iceGatherer; - iceTransport = transceiver.iceTransport; - dtlsTransport = transceiver.dtlsTransport; - rtpReceiver = transceiver.rtpReceiver; - sendEncodingParameters = transceiver.sendEncodingParameters; - localCapabilities = transceiver.localCapabilities; - - pc.transceivers[sdpMLineIndex].recvEncodingParameters = - recvEncodingParameters; - pc.transceivers[sdpMLineIndex].remoteCapabilities = - remoteCapabilities; - pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters; - - if (cands.length && iceTransport.state === 'new') { - if ((isIceLite || isComplete) && - (!usingBundle || sdpMLineIndex === 0)) { - iceTransport.setRemoteCandidates(cands); - } else { - cands.forEach(function(candidate) { - maybeAddCandidate(transceiver.iceTransport, candidate); - }); - } - } - - if (!usingBundle || sdpMLineIndex === 0) { - if (iceTransport.state === 'new') { - iceTransport.start(iceGatherer, remoteIceParameters, - 'controlling'); - } - if (dtlsTransport.state === 'new') { - dtlsTransport.start(remoteDtlsParameters); - } - } - - // If the offer contained RTX but the answer did not, - // remove RTX from sendEncodingParameters. - var commonCapabilities = getCommonCapabilities( - transceiver.localCapabilities, - transceiver.remoteCapabilities); - - var hasRtx = commonCapabilities.codecs.filter(function(c) { - return c.name.toLowerCase() === 'rtx'; - }).length; - if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) { - delete transceiver.sendEncodingParameters[0].rtx; - } - - pc._transceive(transceiver, - direction === 'sendrecv' || direction === 'recvonly', - direction === 'sendrecv' || direction === 'sendonly'); - - // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams - if (rtpReceiver && - (direction === 'sendrecv' || direction === 'sendonly')) { - track = rtpReceiver.track; - if (remoteMsid) { - if (!streams[remoteMsid.stream]) { - streams[remoteMsid.stream] = new window.MediaStream(); - } - addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]); - receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); - } else { - if (!streams.default) { - streams.default = new window.MediaStream(); - } - addTrackToStreamAndFireEvent(track, streams.default); - receiverList.push([track, rtpReceiver, streams.default]); - } - } else { - // FIXME: actually the receiver should be created later. - delete transceiver.rtpReceiver; - } - } - }); - - if (pc._dtlsRole === undefined) { - pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive'; - } - - pc._remoteDescription = { - type: description.type, - sdp: description.sdp - }; - if (description.type === 'offer') { - pc._updateSignalingState('have-remote-offer'); - } else { - pc._updateSignalingState('stable'); - } - Object.keys(streams).forEach(function(sid) { - var stream = streams[sid]; - if (stream.getTracks().length) { - if (pc.remoteStreams.indexOf(stream) === -1) { - pc.remoteStreams.push(stream); - var event = new Event('addstream'); - event.stream = stream; - window.setTimeout(function() { - pc._dispatchEvent('addstream', event); - }); - } - - receiverList.forEach(function(item) { - var track = item[0]; - var receiver = item[1]; - if (stream.id !== item[2].id) { - return; - } - fireAddTrack(pc, track, receiver, [stream]); - }); - } - }); - receiverList.forEach(function(item) { - if (item[2]) { - return; - } - fireAddTrack(pc, item[0], item[1], []); - }); - - // check whether addIceCandidate({}) was called within four seconds after - // setRemoteDescription. - window.setTimeout(function() { - if (!(pc && pc.transceivers)) { - return; - } - pc.transceivers.forEach(function(transceiver) { - if (transceiver.iceTransport && - transceiver.iceTransport.state === 'new' && - transceiver.iceTransport.getRemoteCandidates().length > 0) { - console.warn('Timeout for addRemoteCandidate. Consider sending ' + - 'an end-of-candidates notification'); - transceiver.iceTransport.addRemoteCandidate({}); - } - }); - }, 4000); - - return Promise.resolve(); - }; - - RTCPeerConnection.prototype.close = function() { - this.transceivers.forEach(function(transceiver) { - /* not yet - if (transceiver.iceGatherer) { - transceiver.iceGatherer.close(); - } - */ - if (transceiver.iceTransport) { - transceiver.iceTransport.stop(); - } - if (transceiver.dtlsTransport) { - transceiver.dtlsTransport.stop(); - } - if (transceiver.rtpSender) { - transceiver.rtpSender.stop(); - } - if (transceiver.rtpReceiver) { - transceiver.rtpReceiver.stop(); - } - }); - // FIXME: clean up tracks, local streams, remote streams, etc - this._isClosed = true; - this._updateSignalingState('closed'); - }; - - // Update the signaling state. - RTCPeerConnection.prototype._updateSignalingState = function(newState) { - this.signalingState = newState; - var event = new Event('signalingstatechange'); - this._dispatchEvent('signalingstatechange', event); - }; - - // Determine whether to fire the negotiationneeded event. - RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() { - var pc = this; - if (this.signalingState !== 'stable' || this.needNegotiation === true) { - return; - } - this.needNegotiation = true; - window.setTimeout(function() { - if (pc.needNegotiation) { - pc.needNegotiation = false; - var event = new Event('negotiationneeded'); - pc._dispatchEvent('negotiationneeded', event); - } - }, 0); - }; - - // Update the ice connection state. - RTCPeerConnection.prototype._updateIceConnectionState = function() { - var newState; - var states = { - 'new': 0, - closed: 0, - checking: 0, - connected: 0, - completed: 0, - disconnected: 0, - failed: 0 - }; - this.transceivers.forEach(function(transceiver) { - if (transceiver.iceTransport && !transceiver.rejected) { - states[transceiver.iceTransport.state]++; - } - }); - - newState = 'new'; - if (states.failed > 0) { - newState = 'failed'; - } else if (states.checking > 0) { - newState = 'checking'; - } else if (states.disconnected > 0) { - newState = 'disconnected'; - } else if (states.new > 0) { - newState = 'new'; - } else if (states.connected > 0) { - newState = 'connected'; - } else if (states.completed > 0) { - newState = 'completed'; - } - - if (newState !== this.iceConnectionState) { - this.iceConnectionState = newState; - var event = new Event('iceconnectionstatechange'); - this._dispatchEvent('iceconnectionstatechange', event); - } - }; - - // Update the connection state. - RTCPeerConnection.prototype._updateConnectionState = function() { - var newState; - var states = { - 'new': 0, - closed: 0, - connecting: 0, - connected: 0, - completed: 0, - disconnected: 0, - failed: 0 - }; - this.transceivers.forEach(function(transceiver) { - if (transceiver.iceTransport && transceiver.dtlsTransport && - !transceiver.rejected) { - states[transceiver.iceTransport.state]++; - states[transceiver.dtlsTransport.state]++; - } - }); - // ICETransport.completed and connected are the same for this purpose. - states.connected += states.completed; - - newState = 'new'; - if (states.failed > 0) { - newState = 'failed'; - } else if (states.connecting > 0) { - newState = 'connecting'; - } else if (states.disconnected > 0) { - newState = 'disconnected'; - } else if (states.new > 0) { - newState = 'new'; - } else if (states.connected > 0) { - newState = 'connected'; - } - - if (newState !== this.connectionState) { - this.connectionState = newState; - var event = new Event('connectionstatechange'); - this._dispatchEvent('connectionstatechange', event); - } - }; - - RTCPeerConnection.prototype.createOffer = function() { - var pc = this; - - if (pc._isClosed) { - return Promise.reject(makeError('InvalidStateError', - 'Can not call createOffer after close')); - } - - var numAudioTracks = pc.transceivers.filter(function(t) { - return t.kind === 'audio'; - }).length; - var numVideoTracks = pc.transceivers.filter(function(t) { - return t.kind === 'video'; - }).length; - - // Determine number of audio and video tracks we need to send/recv. - var offerOptions = arguments[0]; - if (offerOptions) { - // Reject Chrome legacy constraints. - if (offerOptions.mandatory || offerOptions.optional) { - throw new TypeError( - 'Legacy mandatory/optional constraints not supported.'); - } - if (offerOptions.offerToReceiveAudio !== undefined) { - if (offerOptions.offerToReceiveAudio === true) { - numAudioTracks = 1; - } else if (offerOptions.offerToReceiveAudio === false) { - numAudioTracks = 0; - } else { - numAudioTracks = offerOptions.offerToReceiveAudio; - } - } - if (offerOptions.offerToReceiveVideo !== undefined) { - if (offerOptions.offerToReceiveVideo === true) { - numVideoTracks = 1; - } else if (offerOptions.offerToReceiveVideo === false) { - numVideoTracks = 0; - } else { - numVideoTracks = offerOptions.offerToReceiveVideo; - } - } - } - - pc.transceivers.forEach(function(transceiver) { - if (transceiver.kind === 'audio') { - numAudioTracks--; - if (numAudioTracks < 0) { - transceiver.wantReceive = false; - } - } else if (transceiver.kind === 'video') { - numVideoTracks--; - if (numVideoTracks < 0) { - transceiver.wantReceive = false; - } - } - }); - - // Create M-lines for recvonly streams. - while (numAudioTracks > 0 || numVideoTracks > 0) { - if (numAudioTracks > 0) { - pc._createTransceiver('audio'); - numAudioTracks--; - } - if (numVideoTracks > 0) { - pc._createTransceiver('video'); - numVideoTracks--; - } - } - - var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, - pc._sdpSessionVersion++); - pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { - // For each track, create an ice gatherer, ice transport, - // dtls transport, potentially rtpsender and rtpreceiver. - var track = transceiver.track; - var kind = transceiver.kind; - var mid = transceiver.mid || SDPUtils.generateIdentifier(); - transceiver.mid = mid; - - if (!transceiver.iceGatherer) { - transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, - pc.usingBundle); - } - - var localCapabilities = window.RTCRtpSender.getCapabilities(kind); - // filter RTX until additional stuff needed for RTX is implemented - // in adapter.js - if (edgeVersion < 15019) { - localCapabilities.codecs = localCapabilities.codecs.filter( - function(codec) { - return codec.name !== 'rtx'; - }); - } - localCapabilities.codecs.forEach(function(codec) { - // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552 - // by adding level-asymmetry-allowed=1 - if (codec.name === 'H264' && - codec.parameters['level-asymmetry-allowed'] === undefined) { - codec.parameters['level-asymmetry-allowed'] = '1'; - } - - // for subsequent offers, we might have to re-use the payload - // type of the last offer. - if (transceiver.remoteCapabilities && - transceiver.remoteCapabilities.codecs) { - transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) { - if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() && - codec.clockRate === remoteCodec.clockRate) { - codec.preferredPayloadType = remoteCodec.payloadType; - } - }); - } - }); - localCapabilities.headerExtensions.forEach(function(hdrExt) { - var remoteExtensions = transceiver.remoteCapabilities && - transceiver.remoteCapabilities.headerExtensions || []; - remoteExtensions.forEach(function(rHdrExt) { - if (hdrExt.uri === rHdrExt.uri) { - hdrExt.id = rHdrExt.id; - } - }); - }); - - // generate an ssrc now, to be used later in rtpSender.send - var sendEncodingParameters = transceiver.sendEncodingParameters || [{ - ssrc: (2 * sdpMLineIndex + 1) * 1001 - }]; - if (track) { - // add RTX - if (edgeVersion >= 15019 && kind === 'video' && - !sendEncodingParameters[0].rtx) { - sendEncodingParameters[0].rtx = { - ssrc: sendEncodingParameters[0].ssrc + 1 - }; - } - } - - if (transceiver.wantReceive) { - transceiver.rtpReceiver = new window.RTCRtpReceiver( - transceiver.dtlsTransport, kind); - } - - transceiver.localCapabilities = localCapabilities; - transceiver.sendEncodingParameters = sendEncodingParameters; - }); - - // always offer BUNDLE and dispose on return if not supported. - if (pc._config.bundlePolicy !== 'max-compat') { - sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { - return t.mid; - }).join(' ') + '\r\n'; - } - sdp += 'a=ice-options:trickle\r\n'; - - pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { - sdp += writeMediaSection(transceiver, transceiver.localCapabilities, - 'offer', transceiver.stream, pc._dtlsRole); - sdp += 'a=rtcp-rsize\r\n'; - - if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' && - (sdpMLineIndex === 0 || !pc.usingBundle)) { - transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) { - cand.component = 1; - sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n'; - }); - - if (transceiver.iceGatherer.state === 'completed') { - sdp += 'a=end-of-candidates\r\n'; - } - } - }); - - var desc = new window.RTCSessionDescription({ - type: 'offer', - sdp: sdp - }); - return Promise.resolve(desc); - }; - - RTCPeerConnection.prototype.createAnswer = function() { - var pc = this; - - if (pc._isClosed) { - return Promise.reject(makeError('InvalidStateError', - 'Can not call createAnswer after close')); - } - - if (!(pc.signalingState === 'have-remote-offer' || - pc.signalingState === 'have-local-pranswer')) { - return Promise.reject(makeError('InvalidStateError', - 'Can not call createAnswer in signalingState ' + pc.signalingState)); - } - - var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, - pc._sdpSessionVersion++); - if (pc.usingBundle) { - sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { - return t.mid; - }).join(' ') + '\r\n'; - } - sdp += 'a=ice-options:trickle\r\n'; - - var mediaSectionsInOffer = SDPUtils.getMediaSections( - pc._remoteDescription.sdp).length; - pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { - if (sdpMLineIndex + 1 > mediaSectionsInOffer) { - return; - } - if (transceiver.rejected) { - if (transceiver.kind === 'application') { - if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt - sdp += 'm=application 0 DTLS/SCTP 5000\r\n'; - } else { - sdp += 'm=application 0 ' + transceiver.protocol + - ' webrtc-datachannel\r\n'; - } - } else if (transceiver.kind === 'audio') { - sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' + - 'a=rtpmap:0 PCMU/8000\r\n'; - } else if (transceiver.kind === 'video') { - sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' + - 'a=rtpmap:120 VP8/90000\r\n'; - } - sdp += 'c=IN IP4 0.0.0.0\r\n' + - 'a=inactive\r\n' + - 'a=mid:' + transceiver.mid + '\r\n'; - return; - } - - // FIXME: look at direction. - if (transceiver.stream) { - var localTrack; - if (transceiver.kind === 'audio') { - localTrack = transceiver.stream.getAudioTracks()[0]; - } else if (transceiver.kind === 'video') { - localTrack = transceiver.stream.getVideoTracks()[0]; - } - if (localTrack) { - // add RTX - if (edgeVersion >= 15019 && transceiver.kind === 'video' && - !transceiver.sendEncodingParameters[0].rtx) { - transceiver.sendEncodingParameters[0].rtx = { - ssrc: transceiver.sendEncodingParameters[0].ssrc + 1 - }; - } - } - } - - // Calculate intersection of capabilities. - var commonCapabilities = getCommonCapabilities( - transceiver.localCapabilities, - transceiver.remoteCapabilities); - - var hasRtx = commonCapabilities.codecs.filter(function(c) { - return c.name.toLowerCase() === 'rtx'; - }).length; - if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) { - delete transceiver.sendEncodingParameters[0].rtx; - } - - sdp += writeMediaSection(transceiver, commonCapabilities, - 'answer', transceiver.stream, pc._dtlsRole); - if (transceiver.rtcpParameters && - transceiver.rtcpParameters.reducedSize) { - sdp += 'a=rtcp-rsize\r\n'; - } - }); - - var desc = new window.RTCSessionDescription({ - type: 'answer', - sdp: sdp - }); - return Promise.resolve(desc); - }; - - RTCPeerConnection.prototype.addIceCandidate = function(candidate) { - var pc = this; - var sections; - if (candidate && !(candidate.sdpMLineIndex !== undefined || - candidate.sdpMid)) { - return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required')); - } - - // TODO: needs to go into ops queue. - return new Promise(function(resolve, reject) { - if (!pc._remoteDescription) { - return reject(makeError('InvalidStateError', - 'Can not add ICE candidate without a remote description')); - } else if (!candidate || candidate.candidate === '') { - for (var j = 0; j < pc.transceivers.length; j++) { - if (pc.transceivers[j].rejected) { - continue; - } - pc.transceivers[j].iceTransport.addRemoteCandidate({}); - sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp); - sections[j] += 'a=end-of-candidates\r\n'; - pc._remoteDescription.sdp = - SDPUtils.getDescription(pc._remoteDescription.sdp) + - sections.join(''); - if (pc.usingBundle) { - break; - } - } - } else { - var sdpMLineIndex = candidate.sdpMLineIndex; - if (candidate.sdpMid) { - for (var i = 0; i < pc.transceivers.length; i++) { - if (pc.transceivers[i].mid === candidate.sdpMid) { - sdpMLineIndex = i; - break; - } - } - } - var transceiver = pc.transceivers[sdpMLineIndex]; - if (transceiver) { - if (transceiver.rejected) { - return resolve(); - } - var cand = Object.keys(candidate.candidate).length > 0 ? - SDPUtils.parseCandidate(candidate.candidate) : {}; - // Ignore Chrome's invalid candidates since Edge does not like them. - if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) { - return resolve(); - } - // Ignore RTCP candidates, we assume RTCP-MUX. - if (cand.component && cand.component !== 1) { - return resolve(); - } - // when using bundle, avoid adding candidates to the wrong - // ice transport. And avoid adding candidates added in the SDP. - if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 && - transceiver.iceTransport !== pc.transceivers[0].iceTransport)) { - if (!maybeAddCandidate(transceiver.iceTransport, cand)) { - return reject(makeError('OperationError', - 'Can not add ICE candidate')); - } - } - - // update the remoteDescription. - var candidateString = candidate.candidate.trim(); - if (candidateString.indexOf('a=') === 0) { - candidateString = candidateString.substr(2); - } - sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp); - sections[sdpMLineIndex] += 'a=' + - (cand.type ? candidateString : 'end-of-candidates') - + '\r\n'; - pc._remoteDescription.sdp = - SDPUtils.getDescription(pc._remoteDescription.sdp) + - sections.join(''); - } else { - return reject(makeError('OperationError', - 'Can not add ICE candidate')); - } - } - resolve(); - }); - }; - - RTCPeerConnection.prototype.getStats = function(selector) { - if (selector && selector instanceof window.MediaStreamTrack) { - var senderOrReceiver = null; - this.transceivers.forEach(function(transceiver) { - if (transceiver.rtpSender && - transceiver.rtpSender.track === selector) { - senderOrReceiver = transceiver.rtpSender; - } else if (transceiver.rtpReceiver && - transceiver.rtpReceiver.track === selector) { - senderOrReceiver = transceiver.rtpReceiver; - } - }); - if (!senderOrReceiver) { - throw makeError('InvalidAccessError', 'Invalid selector.'); - } - return senderOrReceiver.getStats(); - } - - var promises = []; - this.transceivers.forEach(function(transceiver) { - ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', - 'dtlsTransport'].forEach(function(method) { - if (transceiver[method]) { - promises.push(transceiver[method].getStats()); - } - }); - }); - return Promise.all(promises).then(function(allStats) { - var results = new Map(); - allStats.forEach(function(stats) { - stats.forEach(function(stat) { - results.set(stat.id, stat); - }); - }); - return results; - }); - }; - - // fix low-level stat names and return Map instead of object. - var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer', - 'RTCIceTransport', 'RTCDtlsTransport']; - ortcObjects.forEach(function(ortcObjectName) { - var obj = window[ortcObjectName]; - if (obj && obj.prototype && obj.prototype.getStats) { - var nativeGetstats = obj.prototype.getStats; - obj.prototype.getStats = function() { - return nativeGetstats.apply(this) - .then(function(nativeStats) { - var mapStats = new Map(); - Object.keys(nativeStats).forEach(function(id) { - nativeStats[id].type = fixStatsType(nativeStats[id]); - mapStats.set(id, nativeStats[id]); - }); - return mapStats; - }); - }; - } - }); - - // legacy callback shims. Should be moved to adapter.js some days. - var methods = ['createOffer', 'createAnswer']; - methods.forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { - var args = arguments; - if (typeof args[0] === 'function' || - typeof args[1] === 'function') { // legacy - return nativeMethod.apply(this, [arguments[2]]) - .then(function(description) { - if (typeof args[0] === 'function') { - args[0].apply(null, [description]); - } - }, function(error) { - if (typeof args[1] === 'function') { - args[1].apply(null, [error]); - } - }); - } - return nativeMethod.apply(this, arguments); - }; - }); - - methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']; - methods.forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { - var args = arguments; - if (typeof args[1] === 'function' || - typeof args[2] === 'function') { // legacy - return nativeMethod.apply(this, arguments) - .then(function() { - if (typeof args[1] === 'function') { - args[1].apply(null); - } - }, function(error) { - if (typeof args[2] === 'function') { - args[2].apply(null, [error]); - } - }); - } - return nativeMethod.apply(this, arguments); - }; - }); - - // getStats is special. It doesn't have a spec legacy method yet we support - // getStats(something, cb) without error callbacks. - ['getStats'].forEach(function(method) { - var nativeMethod = RTCPeerConnection.prototype[method]; - RTCPeerConnection.prototype[method] = function() { - var args = arguments; - if (typeof args[1] === 'function') { - return nativeMethod.apply(this, arguments) - .then(function() { - if (typeof args[1] === 'function') { - args[1].apply(null); - } - }); - } - return nativeMethod.apply(this, arguments); - }; - }); - - return RTCPeerConnection; - }; - - },{"sdp":17}],17:[function(require,module,exports){ - /* eslint-env node */ - 'use strict'; - -// SDP helpers. - var SDPUtils = {}; - -// Generate an alphanumeric identifier for cname or mids. -// TODO: use UUIDs instead? https://gist.github.com/jed/982883 - SDPUtils.generateIdentifier = function() { - return Math.random().toString(36).substr(2, 10); - }; - -// The RTCP CNAME used by all peerconnections from the same JS. - SDPUtils.localCName = SDPUtils.generateIdentifier(); - -// Splits SDP into lines, dealing with both CRLF and LF. - SDPUtils.splitLines = function(blob) { - return blob.trim().split('\n').map(function(line) { - return line.trim(); - }); - }; -// Splits SDP into sessionpart and mediasections. Ensures CRLF. - SDPUtils.splitSections = function(blob) { - var parts = blob.split('\nm='); - return parts.map(function(part, index) { - return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; - }); - }; - -// returns the session description. - SDPUtils.getDescription = function(blob) { - var sections = SDPUtils.splitSections(blob); - return sections && sections[0]; - }; - -// returns the individual media sections. - SDPUtils.getMediaSections = function(blob) { - var sections = SDPUtils.splitSections(blob); - sections.shift(); - return sections; - }; - -// Returns lines that start with a certain prefix. - SDPUtils.matchPrefix = function(blob, prefix) { - return SDPUtils.splitLines(blob).filter(function(line) { - return line.indexOf(prefix) === 0; - }); - }; - -// Parses an ICE candidate line. Sample input: -// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 -// rport 55996" - SDPUtils.parseCandidate = function(line) { - var parts; - // Parse both variants. - if (line.indexOf('a=candidate:') === 0) { - parts = line.substring(12).split(' '); - } else { - parts = line.substring(10).split(' '); - } - - var candidate = { - foundation: parts[0], - component: parseInt(parts[1], 10), - protocol: parts[2].toLowerCase(), - priority: parseInt(parts[3], 10), - ip: parts[4], - address: parts[4], // address is an alias for ip. - port: parseInt(parts[5], 10), - // skip parts[6] == 'typ' - type: parts[7] - }; - - for (var i = 8; i < parts.length; i += 2) { - switch (parts[i]) { - case 'raddr': - candidate.relatedAddress = parts[i + 1]; - break; - case 'rport': - candidate.relatedPort = parseInt(parts[i + 1], 10); - break; - case 'tcptype': - candidate.tcpType = parts[i + 1]; - break; - case 'ufrag': - candidate.ufrag = parts[i + 1]; // for backward compability. - candidate.usernameFragment = parts[i + 1]; - break; - default: // extension handling, in particular ufrag - candidate[parts[i]] = parts[i + 1]; - break; - } - } - return candidate; - }; - -// Translates a candidate object into SDP candidate attribute. - SDPUtils.writeCandidate = function(candidate) { - var sdp = []; - sdp.push(candidate.foundation); - sdp.push(candidate.component); - sdp.push(candidate.protocol.toUpperCase()); - sdp.push(candidate.priority); - sdp.push(candidate.address || candidate.ip); - sdp.push(candidate.port); - - var type = candidate.type; - sdp.push('typ'); - sdp.push(type); - if (type !== 'host' && candidate.relatedAddress && - candidate.relatedPort) { - sdp.push('raddr'); - sdp.push(candidate.relatedAddress); - sdp.push('rport'); - sdp.push(candidate.relatedPort); - } - if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { - sdp.push('tcptype'); - sdp.push(candidate.tcpType); - } - if (candidate.usernameFragment || candidate.ufrag) { - sdp.push('ufrag'); - sdp.push(candidate.usernameFragment || candidate.ufrag); - } - return 'candidate:' + sdp.join(' '); - }; - -// Parses an ice-options line, returns an array of option tags. -// a=ice-options:foo bar - SDPUtils.parseIceOptions = function(line) { - return line.substr(14).split(' '); - }; - -// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: -// a=rtpmap:111 opus/48000/2 - SDPUtils.parseRtpMap = function(line) { - var parts = line.substr(9).split(' '); - var parsed = { - payloadType: parseInt(parts.shift(), 10) // was: id - }; - - parts = parts[0].split('/'); - - parsed.name = parts[0]; - parsed.clockRate = parseInt(parts[1], 10); // was: clockrate - parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; - // legacy alias, got renamed back to channels in ORTC. - parsed.numChannels = parsed.channels; - return parsed; - }; - -// Generate an a=rtpmap line from RTCRtpCodecCapability or -// RTCRtpCodecParameters. - SDPUtils.writeRtpMap = function(codec) { - var pt = codec.payloadType; - if (codec.preferredPayloadType !== undefined) { - pt = codec.preferredPayloadType; - } - var channels = codec.channels || codec.numChannels || 1; - return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + - (channels !== 1 ? '/' + channels : '') + '\r\n'; - }; - -// Parses an a=extmap line (headerextension from RFC 5285). Sample input: -// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset -// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset - SDPUtils.parseExtmap = function(line) { - var parts = line.substr(9).split(' '); - return { - id: parseInt(parts[0], 10), - direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', - uri: parts[1] - }; - }; - -// Generates a=extmap line from RTCRtpHeaderExtensionParameters or -// RTCRtpHeaderExtension. - SDPUtils.writeExtmap = function(headerExtension) { - return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + - (headerExtension.direction && headerExtension.direction !== 'sendrecv' - ? '/' + headerExtension.direction - : '') + - ' ' + headerExtension.uri + '\r\n'; - }; - -// Parses an ftmp line, returns dictionary. Sample input: -// a=fmtp:96 vbr=on;cng=on -// Also deals with vbr=on; cng=on - SDPUtils.parseFmtp = function(line) { - var parsed = {}; - var kv; - var parts = line.substr(line.indexOf(' ') + 1).split(';'); - for (var j = 0; j < parts.length; j++) { - kv = parts[j].trim().split('='); - parsed[kv[0].trim()] = kv[1]; - } - return parsed; - }; - -// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. - SDPUtils.writeFmtp = function(codec) { - var line = ''; - var pt = codec.payloadType; - if (codec.preferredPayloadType !== undefined) { - pt = codec.preferredPayloadType; - } - if (codec.parameters && Object.keys(codec.parameters).length) { - var params = []; - Object.keys(codec.parameters).forEach(function(param) { - if (codec.parameters[param]) { - params.push(param + '=' + codec.parameters[param]); - } else { - params.push(param); - } - }); - line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; - } - return line; - }; - -// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: -// a=rtcp-fb:98 nack rpsi - SDPUtils.parseRtcpFb = function(line) { - var parts = line.substr(line.indexOf(' ') + 1).split(' '); - return { - type: parts.shift(), - parameter: parts.join(' ') - }; - }; -// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. - SDPUtils.writeRtcpFb = function(codec) { - var lines = ''; - var pt = codec.payloadType; - if (codec.preferredPayloadType !== undefined) { - pt = codec.preferredPayloadType; - } - if (codec.rtcpFeedback && codec.rtcpFeedback.length) { - // FIXME: special handling for trr-int? - codec.rtcpFeedback.forEach(function(fb) { - lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + - (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + - '\r\n'; - }); - } - return lines; - }; - -// Parses an RFC 5576 ssrc media attribute. Sample input: -// a=ssrc:3735928559 cname:something - SDPUtils.parseSsrcMedia = function(line) { - var sp = line.indexOf(' '); - var parts = { - ssrc: parseInt(line.substr(7, sp - 7), 10) - }; - var colon = line.indexOf(':', sp); - if (colon > -1) { - parts.attribute = line.substr(sp + 1, colon - sp - 1); - parts.value = line.substr(colon + 1); - } else { - parts.attribute = line.substr(sp + 1); - } - return parts; - }; - - SDPUtils.parseSsrcGroup = function(line) { - var parts = line.substr(13).split(' '); - return { - semantics: parts.shift(), - ssrcs: parts.map(function(ssrc) { - return parseInt(ssrc, 10); - }) - }; - }; - -// Extracts the MID (RFC 5888) from a media section. -// returns the MID or undefined if no mid line was found. - SDPUtils.getMid = function(mediaSection) { - var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; - if (mid) { - return mid.substr(6); - } - }; - - SDPUtils.parseFingerprint = function(line) { - var parts = line.substr(14).split(' '); - return { - algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. - value: parts[1] - }; - }; - -// Extracts DTLS parameters from SDP media section or sessionpart. -// FIXME: for consistency with other functions this should only -// get the fingerprint line as input. See also getIceParameters. - SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) { - var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, - 'a=fingerprint:'); - // Note: a=setup line is ignored since we use the 'auto' role. - // Note2: 'algorithm' is not case sensitive except in Edge. - return { - role: 'auto', - fingerprints: lines.map(SDPUtils.parseFingerprint) - }; - }; - -// Serializes DTLS parameters to SDP. - SDPUtils.writeDtlsParameters = function(params, setupType) { - var sdp = 'a=setup:' + setupType + '\r\n'; - params.fingerprints.forEach(function(fp) { - sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; - }); - return sdp; - }; -// Parses ICE information from SDP media section or sessionpart. -// FIXME: for consistency with other functions this should only -// get the ice-ufrag and ice-pwd lines as input. - SDPUtils.getIceParameters = function(mediaSection, sessionpart) { - var lines = SDPUtils.splitLines(mediaSection); - // Search in session part, too. - lines = lines.concat(SDPUtils.splitLines(sessionpart)); - var iceParameters = { - usernameFragment: lines.filter(function(line) { - return line.indexOf('a=ice-ufrag:') === 0; - })[0].substr(12), - password: lines.filter(function(line) { - return line.indexOf('a=ice-pwd:') === 0; - })[0].substr(10) - }; - return iceParameters; - }; - -// Serializes ICE parameters to SDP. - SDPUtils.writeIceParameters = function(params) { - return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + - 'a=ice-pwd:' + params.password + '\r\n'; - }; - -// Parses the SDP media section and returns RTCRtpParameters. - SDPUtils.parseRtpParameters = function(mediaSection) { - var description = { - codecs: [], - headerExtensions: [], - fecMechanisms: [], - rtcp: [] - }; - var lines = SDPUtils.splitLines(mediaSection); - var mline = lines[0].split(' '); - for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..] - var pt = mline[i]; - var rtpmapline = SDPUtils.matchPrefix( - mediaSection, 'a=rtpmap:' + pt + ' ')[0]; - if (rtpmapline) { - var codec = SDPUtils.parseRtpMap(rtpmapline); - var fmtps = SDPUtils.matchPrefix( - mediaSection, 'a=fmtp:' + pt + ' '); - // Only the first a=fmtp: is considered. - codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; - codec.rtcpFeedback = SDPUtils.matchPrefix( - mediaSection, 'a=rtcp-fb:' + pt + ' ') - .map(SDPUtils.parseRtcpFb); - description.codecs.push(codec); - // parse FEC mechanisms from rtpmap lines. - switch (codec.name.toUpperCase()) { - case 'RED': - case 'ULPFEC': - description.fecMechanisms.push(codec.name.toUpperCase()); - break; - default: // only RED and ULPFEC are recognized as FEC mechanisms. - break; - } - } - } - SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) { - description.headerExtensions.push(SDPUtils.parseExtmap(line)); - }); - // FIXME: parse rtcp. - return description; - }; - -// Generates parts of the SDP media section describing the capabilities / -// parameters. - SDPUtils.writeRtpDescription = function(kind, caps) { - var sdp = ''; - - // Build the mline. - sdp += 'm=' + kind + ' '; - sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. - sdp += ' UDP/TLS/RTP/SAVPF '; - sdp += caps.codecs.map(function(codec) { - if (codec.preferredPayloadType !== undefined) { - return codec.preferredPayloadType; - } - return codec.payloadType; - }).join(' ') + '\r\n'; - - sdp += 'c=IN IP4 0.0.0.0\r\n'; - sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; - - // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. - caps.codecs.forEach(function(codec) { - sdp += SDPUtils.writeRtpMap(codec); - sdp += SDPUtils.writeFmtp(codec); - sdp += SDPUtils.writeRtcpFb(codec); - }); - var maxptime = 0; - caps.codecs.forEach(function(codec) { - if (codec.maxptime > maxptime) { - maxptime = codec.maxptime; - } - }); - if (maxptime > 0) { - sdp += 'a=maxptime:' + maxptime + '\r\n'; - } - sdp += 'a=rtcp-mux\r\n'; - - if (caps.headerExtensions) { - caps.headerExtensions.forEach(function(extension) { - sdp += SDPUtils.writeExtmap(extension); - }); - } - // FIXME: write fecMechanisms. - return sdp; - }; - -// Parses the SDP media section and returns an array of -// RTCRtpEncodingParameters. - SDPUtils.parseRtpEncodingParameters = function(mediaSection) { - var encodingParameters = []; - var description = SDPUtils.parseRtpParameters(mediaSection); - var hasRed = description.fecMechanisms.indexOf('RED') !== -1; - var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; - - // filter a=ssrc:... cname:, ignore PlanB-msid - var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(parts) { - return parts.attribute === 'cname'; - }); - var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; - var secondarySsrc; - - var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') - .map(function(line) { - var parts = line.substr(17).split(' '); - return parts.map(function(part) { - return parseInt(part, 10); - }); - }); - if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { - secondarySsrc = flows[0][1]; - } - - description.codecs.forEach(function(codec) { - if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { - var encParam = { - ssrc: primarySsrc, - codecPayloadType: parseInt(codec.parameters.apt, 10) - }; - if (primarySsrc && secondarySsrc) { - encParam.rtx = {ssrc: secondarySsrc}; - } - encodingParameters.push(encParam); - if (hasRed) { - encParam = JSON.parse(JSON.stringify(encParam)); - encParam.fec = { - ssrc: primarySsrc, - mechanism: hasUlpfec ? 'red+ulpfec' : 'red' - }; - encodingParameters.push(encParam); - } - } - }); - if (encodingParameters.length === 0 && primarySsrc) { - encodingParameters.push({ - ssrc: primarySsrc - }); - } - - // we support both b=AS and b=TIAS but interpret AS as TIAS. - var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); - if (bandwidth.length) { - if (bandwidth[0].indexOf('b=TIAS:') === 0) { - bandwidth = parseInt(bandwidth[0].substr(7), 10); - } else if (bandwidth[0].indexOf('b=AS:') === 0) { - // use formula from JSEP to convert b=AS to TIAS value. - bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 - - (50 * 40 * 8); - } else { - bandwidth = undefined; - } - encodingParameters.forEach(function(params) { - params.maxBitrate = bandwidth; - }); - } - return encodingParameters; - }; - -// parses http://draft.ortc.org/#rtcrtcpparameters* - SDPUtils.parseRtcpParameters = function(mediaSection) { - var rtcpParameters = {}; - - // Gets the first SSRC. Note tha with RTX there might be multiple - // SSRCs. - var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(obj) { - return obj.attribute === 'cname'; - })[0]; - if (remoteSsrc) { - rtcpParameters.cname = remoteSsrc.value; - rtcpParameters.ssrc = remoteSsrc.ssrc; - } - - // Edge uses the compound attribute instead of reducedSize - // compound is !reducedSize - var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); - rtcpParameters.reducedSize = rsize.length > 0; - rtcpParameters.compound = rsize.length === 0; - - // parses the rtcp-mux attrіbute. - // Note that Edge does not support unmuxed RTCP. - var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); - rtcpParameters.mux = mux.length > 0; - - return rtcpParameters; - }; - -// parses either a=msid: or a=ssrc:... msid lines and returns -// the id of the MediaStream and MediaStreamTrack. - SDPUtils.parseMsid = function(mediaSection) { - var parts; - var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); - if (spec.length === 1) { - parts = spec[0].substr(7).split(' '); - return {stream: parts[0], track: parts[1]}; - } - var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(msidParts) { - return msidParts.attribute === 'msid'; - }); - if (planB.length > 0) { - parts = planB[0].value.split(' '); - return {stream: parts[0], track: parts[1]}; - } - }; - -// Generate a session ID for SDP. -// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1 -// recommends using a cryptographically random +ve 64-bit value -// but right now this should be acceptable and within the right range - SDPUtils.generateSessionId = function() { - return Math.random().toString().substr(2, 21); - }; - -// Write boilder plate for start of SDP -// sessId argument is optional - if not supplied it will -// be generated randomly -// sessVersion is optional and defaults to 2 -// sessUser is optional and defaults to 'thisisadapterortc' - SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) { - var sessionId; - var version = sessVer !== undefined ? sessVer : 2; - if (sessId) { - sessionId = sessId; - } else { - sessionId = SDPUtils.generateSessionId(); - } - var user = sessUser || 'thisisadapterortc'; - // FIXME: sess-id should be an NTP timestamp. - return 'v=0\r\n' + - 'o=' + user + ' ' + sessionId + ' ' + version + - ' IN IP4 127.0.0.1\r\n' + - 's=-\r\n' + - 't=0 0\r\n'; - }; - - SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) { - var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); - - // Map ICE parameters (ufrag, pwd) to SDP. - sdp += SDPUtils.writeIceParameters( - transceiver.iceGatherer.getLocalParameters()); - - // Map DTLS parameters to SDP. - sdp += SDPUtils.writeDtlsParameters( - transceiver.dtlsTransport.getLocalParameters(), - type === 'offer' ? 'actpass' : 'active'); - - sdp += 'a=mid:' + transceiver.mid + '\r\n'; - - if (transceiver.direction) { - sdp += 'a=' + transceiver.direction + '\r\n'; - } else if (transceiver.rtpSender && transceiver.rtpReceiver) { - sdp += 'a=sendrecv\r\n'; - } else if (transceiver.rtpSender) { - sdp += 'a=sendonly\r\n'; - } else if (transceiver.rtpReceiver) { - sdp += 'a=recvonly\r\n'; - } else { - sdp += 'a=inactive\r\n'; - } - - if (transceiver.rtpSender) { - // spec. - var msid = 'msid:' + stream.id + ' ' + - transceiver.rtpSender.track.id + '\r\n'; - sdp += 'a=' + msid; - - // for Chrome. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' ' + msid; - if (transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' ' + msid; - sdp += 'a=ssrc-group:FID ' + - transceiver.sendEncodingParameters[0].ssrc + ' ' + - transceiver.sendEncodingParameters[0].rtx.ssrc + - '\r\n'; - } - } - // FIXME: this should be written by writeRtpDescription. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - } - return sdp; - }; - -// Gets the direction from the mediaSection or the sessionpart. - SDPUtils.getDirection = function(mediaSection, sessionpart) { - // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. - var lines = SDPUtils.splitLines(mediaSection); - for (var i = 0; i < lines.length; i++) { - switch (lines[i]) { - case 'a=sendrecv': - case 'a=sendonly': - case 'a=recvonly': - case 'a=inactive': - return lines[i].substr(2); - default: - // FIXME: What should happen here? - } - } - if (sessionpart) { - return SDPUtils.getDirection(sessionpart); - } - return 'sendrecv'; - }; - - SDPUtils.getKind = function(mediaSection) { - var lines = SDPUtils.splitLines(mediaSection); - var mline = lines[0].split(' '); - return mline[0].substr(2); - }; - - SDPUtils.isRejected = function(mediaSection) { - return mediaSection.split(' ', 2)[1] === '0'; - }; - - SDPUtils.parseMLine = function(mediaSection) { - var lines = SDPUtils.splitLines(mediaSection); - var parts = lines[0].substr(2).split(' '); - return { - kind: parts[0], - port: parseInt(parts[1], 10), - protocol: parts[2], - fmt: parts.slice(3).join(' ') - }; - }; - - SDPUtils.parseOLine = function(mediaSection) { - var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; - var parts = line.substr(2).split(' '); - return { - username: parts[0], - sessionId: parts[1], - sessionVersion: parseInt(parts[2], 10), - netType: parts[3], - addressType: parts[4], - address: parts[5] - }; - }; - -// a very naive interpretation of a valid SDP. - SDPUtils.isValidSDP = function(blob) { - if (typeof blob !== 'string' || blob.length === 0) { - return false; - } - var lines = SDPUtils.splitLines(blob); - for (var i = 0; i < lines.length; i++) { - if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { - return false; - } - // TODO: check the modifier a bit more. - } - return true; - }; - -// Expose public methods. - if (typeof module === 'object') { - module.exports = SDPUtils; - } - - },{}]},{},[1])(1) -}); \ No newline at end of file diff --git a/shared/js/main.ts b/shared/js/main.ts index 3c4956c5..8895e7f6 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -1,3 +1,4 @@ +import * as moment from "moment"; import * as loader from "tc-loader"; import {settings, Settings} from "tc-shared/settings"; import * as profiles from "tc-shared/profiles/ConnectionProfile"; @@ -90,7 +91,6 @@ function setup_close() { }; } -declare function moment(...arguments) : any; function setup_jsrender() : boolean { if(!js_render) { loader.critical_error("Missing jsrender extension!"); diff --git a/shared/js/ui/modal/ModalAvatarList.ts b/shared/js/ui/modal/ModalAvatarList.ts index 1131c40e..8d3ac2e7 100644 --- a/shared/js/ui/modal/ModalAvatarList.ts +++ b/shared/js/ui/modal/ModalAvatarList.ts @@ -6,6 +6,7 @@ import {media_image_type} from "tc-shared/FileManager"; import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; import {ClientEntry} from "tc-shared/ui/client"; import * as log from "tc-shared/log"; +import * as moment from "moment"; const avatar_to_uid = (id: string) => { const buffer = new Uint8Array(id.length / 2); @@ -24,7 +25,6 @@ export const human_file_size = (size: number) => { return (size / Math.pow(1024, exp)).toFixed(2) + 'KMGTPE'.charAt(exp - 1) + "iB"; }; -declare const moment; export function spawnAvatarList(client: ConnectionHandler) { const modal = createModal({ header: tr("Avatars"), diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index 20f1cc31..a011205f 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -8,6 +8,7 @@ import * as log from "tc-shared/log"; import * as tooltip from "tc-shared/ui/elements/Tooltip"; import * as htmltags from "tc-shared/ui/htmltags"; import {format_time, formatMessage} from "tc-shared/ui/frames/chat"; +import * as moment from "moment"; export function openBanList(client: ConnectionHandler) { let modal: Modal; @@ -270,7 +271,6 @@ export const duration_data = { }, }; -declare const moment; function generate_dom(controller: BanListController) : JQuery { const template = $("#tmpl_ban_list").renderTag(); diff --git a/shared/js/ui/modal/ModalClientInfo.ts b/shared/js/ui/modal/ModalClientInfo.ts index b2249283..4c7633d6 100644 --- a/shared/js/ui/modal/ModalClientInfo.ts +++ b/shared/js/ui/modal/ModalClientInfo.ts @@ -4,6 +4,7 @@ import {createInfoModal, createModal, Modal} from "tc-shared/ui/elements/Modal"; import {copy_to_clipboard} from "tc-shared/utils/helpers"; import * as i18nc from "tc-shared/i18n/country"; import * as tooltip from "tc-shared/ui/elements/Tooltip"; +import * as moment from "moment"; import {format_number, network} from "tc-shared/ui/frames/chat"; type InfoUpdateCallback = (info: ClientConnectionInfo) => any; @@ -126,7 +127,6 @@ function apply_client_status(client: ClientEntry, tag: JQuery, modal: Modal, cal } } -declare const moment; function apply_basic_info(client: ClientEntry, tag: JQuery, modal: Modal, callbacks: InfoUpdateCallback[]) { /* Unique ID */ { diff --git a/shared/js/ui/modal/ModalPoke.ts b/shared/js/ui/modal/ModalPoke.ts index 02ea59df..2fbfe011 100644 --- a/shared/js/ui/modal/ModalPoke.ts +++ b/shared/js/ui/modal/ModalPoke.ts @@ -2,6 +2,7 @@ import {ConnectionHandler} from "tc-shared/ConnectionHandler"; import {createModal, Modal} from "tc-shared/ui/elements/Modal"; import * as htmltags from "tc-shared/ui/htmltags"; import {bbcode_chat} from "tc-shared/ui/frames/chat"; +import * as moment from "moment"; let global_modal: PokeModal; @@ -10,7 +11,6 @@ interface ServerEntry { add_message(invoker: PokeInvoker, message: string); } -declare const moment; class PokeModal { private _handle: Modal; private source_map: ServerEntry[] = []; diff --git a/shared/js/ui/modal/ModalServerInfo.ts b/shared/js/ui/modal/ModalServerInfo.ts index 5a3ef892..9096bebd 100644 --- a/shared/js/ui/modal/ModalServerInfo.ts +++ b/shared/js/ui/modal/ModalServerInfo.ts @@ -12,8 +12,8 @@ import * as tooltip from "tc-shared/ui/elements/Tooltip"; import * as i18nc from "tc-shared/i18n/country"; import {format_time, formatMessage} from "tc-shared/ui/frames/chat"; import {Hostbanner} from "tc-shared/ui/frames/hostbanner"; +import * as moment from "moment"; -declare const moment; export function openServerInfo(server: ServerEntry) { let modal: Modal; let update_callbacks: ServerBandwidthInfoUpdateCallback[] = []; diff --git a/vendor/moment/moment.js b/vendor/moment/moment.js deleted file mode 100644 index dbd2cb9c..00000000 --- a/vendor/moment/moment.js +++ /dev/null @@ -1,14380 +0,0 @@ -;(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - global.moment = factory() -}(this, (function () { 'use strict'; - - var hookCallback; - - function hooks () { - return hookCallback.apply(null, arguments); - } - - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback (callback) { - hookCallback = callback; - } - - function isArray(input) { - return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; - } - - function isObject(input) { - // IE8 will treat undefined and null as object if it wasn't for - // input != null - return input != null && Object.prototype.toString.call(input) === '[object Object]'; - } - - function isObjectEmpty(obj) { - if (Object.getOwnPropertyNames) { - return (Object.getOwnPropertyNames(obj).length === 0); - } else { - var k; - for (k in obj) { - if (obj.hasOwnProperty(k)) { - return false; - } - } - return true; - } - } - - function isUndefined(input) { - return input === void 0; - } - - function isNumber(input) { - return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; - } - - function isDate(input) { - return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; - } - - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } - - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; - } - - function createUTC (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } - - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso : false, - parsedDateParts : [], - meridiem : null, - rfc2822 : false, - weekdayMismatch : false - }; - } - - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - - var some; - if (Array.prototype.some) { - some = Array.prototype.some; - } else { - some = function (fun) { - var t = Object(this); - var len = t.length >>> 0; - - for (var i = 0; i < len; i++) { - if (i in t && fun.call(this, t[i], i, t)) { - return true; - } - } - - return false; - }; - } - - function isValid(m) { - if (m._isValid == null) { - var flags = getParsingFlags(m); - var parsedParts = some.call(flags.parsedDateParts, function (i) { - return i != null; - }); - var isNowValid = !isNaN(m._d.getTime()) && - flags.overflow < 0 && - !flags.empty && - !flags.invalidMonth && - !flags.invalidWeekday && - !flags.weekdayMismatch && - !flags.nullInput && - !flags.invalidFormat && - !flags.userInvalidated && - (!flags.meridiem || (flags.meridiem && parsedParts)); - - if (m._strict) { - isNowValid = isNowValid && - flags.charsLeftOver === 0 && - flags.unusedTokens.length === 0 && - flags.bigHour === undefined; - } - - if (Object.isFrozen == null || !Object.isFrozen(m)) { - m._isValid = isNowValid; - } - else { - return isNowValid; - } - } - return m._isValid; - } - - function createInvalid (flags) { - var m = createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); - } - else { - getParsingFlags(m).userInvalidated = true; - } - - return m; - } - - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - var momentProperties = hooks.momentProperties = []; - - function copyConfig(to, from) { - var i, prop, val; - - if (!isUndefined(from._isAMomentObject)) { - to._isAMomentObject = from._isAMomentObject; - } - if (!isUndefined(from._i)) { - to._i = from._i; - } - if (!isUndefined(from._f)) { - to._f = from._f; - } - if (!isUndefined(from._l)) { - to._l = from._l; - } - if (!isUndefined(from._strict)) { - to._strict = from._strict; - } - if (!isUndefined(from._tzm)) { - to._tzm = from._tzm; - } - if (!isUndefined(from._isUTC)) { - to._isUTC = from._isUTC; - } - if (!isUndefined(from._offset)) { - to._offset = from._offset; - } - if (!isUndefined(from._pf)) { - to._pf = getParsingFlags(from); - } - if (!isUndefined(from._locale)) { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i = 0; i < momentProperties.length; i++) { - prop = momentProperties[i]; - val = from[prop]; - if (!isUndefined(val)) { - to[prop] = val; - } - } - } - - return to; - } - - var updateInProgress = false; - - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - if (!this.isValid()) { - this._d = new Date(NaN); - } - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - hooks.updateOffset(this); - updateInProgress = false; - } - } - - function isMoment (obj) { - return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); - } - - function absFloor (number) { - if (number < 0) { - // -0 -> 0 - return Math.ceil(number) || 0; - } else { - return Math.floor(number); - } - } - - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; - - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - - return value; - } - - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } - - function warn(msg) { - if (hooks.suppressDeprecationWarnings === false && - (typeof console !== 'undefined') && console.warn) { - console.warn('Deprecation warning: ' + msg); - } - } - - function deprecate(msg, fn) { - var firstTime = true; - - return extend(function () { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(null, msg); - } - if (firstTime) { - var args = []; - var arg; - for (var i = 0; i < arguments.length; i++) { - arg = ''; - if (typeof arguments[i] === 'object') { - arg += '\n[' + i + '] '; - for (var key in arguments[0]) { - arg += key + ': ' + arguments[0][key] + ', '; - } - arg = arg.slice(0, -2); // Remove trailing comma and space - } else { - arg = arguments[i]; - } - args.push(arg); - } - warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - - var deprecations = {}; - - function deprecateSimple(name, msg) { - if (hooks.deprecationHandler != null) { - hooks.deprecationHandler(name, msg); - } - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } - - hooks.suppressDeprecationWarnings = false; - hooks.deprecationHandler = null; - - function isFunction(input) { - return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; - } - - function set (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (isFunction(prop)) { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - this._config = config; - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. - // TODO: Remove "ordinalParse" fallback in next major release. - this._dayOfMonthOrdinalParseLenient = new RegExp( - (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + - '|' + (/\d{1,2}/).source); - } - - function mergeConfigs(parentConfig, childConfig) { - var res = extend({}, parentConfig), prop; - for (prop in childConfig) { - if (hasOwnProp(childConfig, prop)) { - if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { - res[prop] = {}; - extend(res[prop], parentConfig[prop]); - extend(res[prop], childConfig[prop]); - } else if (childConfig[prop] != null) { - res[prop] = childConfig[prop]; - } else { - delete res[prop]; - } - } - } - for (prop in parentConfig) { - if (hasOwnProp(parentConfig, prop) && - !hasOwnProp(childConfig, prop) && - isObject(parentConfig[prop])) { - // make sure changes to properties don't modify parent config - res[prop] = extend({}, res[prop]); - } - } - return res; - } - - function Locale(config) { - if (config != null) { - this.set(config); - } - } - - var keys; - - if (Object.keys) { - keys = Object.keys; - } else { - keys = function (obj) { - var i, res = []; - for (i in obj) { - if (hasOwnProp(obj, i)) { - res.push(i); - } - } - return res; - }; - } - - var defaultCalendar = { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }; - - function calendar (key, mom, now) { - var output = this._calendar[key] || this._calendar['sameElse']; - return isFunction(output) ? output.call(mom, now) : output; - } - - var defaultLongDateFormat = { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }; - - function longDateFormat (key) { - var format = this._longDateFormat[key], - formatUpper = this._longDateFormat[key.toUpperCase()]; - - if (format || !formatUpper) { - return format; - } - - this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - - return this._longDateFormat[key]; - } - - var defaultInvalidDate = 'Invalid date'; - - function invalidDate () { - return this._invalidDate; - } - - var defaultOrdinal = '%d'; - var defaultDayOfMonthOrdinalParse = /\d{1,2}/; - - function ordinal (number) { - return this._ordinal.replace('%d', number); - } - - var defaultRelativeTime = { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }; - - function relativeTime (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (isFunction(output)) ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - } - - function pastFuture (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return isFunction(format) ? format(output) : format.replace(/%s/i, output); - } - - var aliases = {}; - - function addUnitAlias (unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } - - function normalizeUnits(units) { - return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; - } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; - } - - var priorities = {}; - - function addUnitPriority(unit, priority) { - priorities[unit] = priority; - } - - function getPrioritizedUnits(unitsObj) { - var units = []; - for (var u in unitsObj) { - units.push({unit: u, priority: priorities[u]}); - } - units.sort(function (a, b) { - return a.priority - b.priority; - }); - return units; - } - - function zeroFill(number, targetLength, forceSign) { - var absNumber = '' + Math.abs(number), - zerosToFill = targetLength - absNumber.length, - sign = number >= 0; - return (sign ? (forceSign ? '+' : '') : '-') + - Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; - } - - var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; - - var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; - - var formatFunctions = {}; - - var formatTokenFunctions = {}; - - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken (token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal(func.apply(this, arguments), token); - }; - } - } - - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } - - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - - return function (mom) { - var output = '', i; - for (i = 0; i < length; i++) { - output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - - format = expandFormat(format, m.localeData()); - formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); - - return formatFunctions[format](m); - } - - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - - return format; - } - - var match1 = /\d/; // 0 - 9 - var match2 = /\d\d/; // 00 - 99 - var match3 = /\d{3}/; // 000 - 999 - var match4 = /\d{4}/; // 0000 - 9999 - var match6 = /[+-]?\d{6}/; // -999999 - 999999 - var match1to2 = /\d\d?/; // 0 - 99 - var match3to4 = /\d\d\d\d?/; // 999 - 9999 - var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 - var match1to3 = /\d{1,3}/; // 0 - 999 - var match1to4 = /\d{1,4}/; // 0 - 9999 - var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 - - var matchUnsigned = /\d+/; // 0 - inf - var matchSigned = /[+-]?\d+/; // -inf - inf - - var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z - var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z - - var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 - - // any word (or two) characters or numbers including two/three word month in arabic. - // includes scottish gaelic two word and hyphenated months - var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; - - var regexes = {}; - - function addRegexToken (token, regex, strictRegex) { - regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { - return (isStrict && strictRegex) ? strictRegex : regex; - }; - } - - function getParseRegexForToken (token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } - - return regexes[token](config._strict, config._locale); - } - - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - })); - } - - function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } - - var tokens = {}; - - function addParseToken (token, callback) { - var i, func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (isNumber(callback)) { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; - } - } - - function addWeekParseToken (token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } - - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); - } - } - - var YEAR = 0; - var MONTH = 1; - var DATE = 2; - var HOUR = 3; - var MINUTE = 4; - var SECOND = 5; - var MILLISECOND = 6; - var WEEK = 7; - var WEEKDAY = 8; - - // FORMATTING - - addFormatToken('Y', 0, 0, function () { - var y = this.year(); - return y <= 9999 ? '' + y : '+' + y; - }); - - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); - - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - - // ALIASES - - addUnitAlias('year', 'y'); - - // PRIORITIES - - addUnitPriority('year', 1); - - // PARSING - - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); - - addParseToken(['YYYYY', 'YYYYYY'], YEAR); - addParseToken('YYYY', function (input, array) { - array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken('YY', function (input, array) { - array[YEAR] = hooks.parseTwoDigitYear(input); - }); - addParseToken('Y', function (input, array) { - array[YEAR] = parseInt(input, 10); - }); - - // HELPERS - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } - - // HOOKS - - hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - // MOMENTS - - var getSetYear = makeGetSet('FullYear', true); - - function getIsLeapYear () { - return isLeapYear(this.year()); - } - - function makeGetSet (unit, keepTime) { - return function (value) { - if (value != null) { - set$1(this, unit, value); - hooks.updateOffset(this, keepTime); - return this; - } else { - return get(this, unit); - } - }; - } - - function get (mom, unit) { - return mom.isValid() ? - mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; - } - - function set$1 (mom, unit, value) { - if (mom.isValid() && !isNaN(value)) { - if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); - } - else { - mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } - } - - // MOMENTS - - function stringGet (units) { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](); - } - return this; - } - - - function stringSet (units, value) { - if (typeof units === 'object') { - units = normalizeObjectUnits(units); - var prioritized = getPrioritizedUnits(units); - for (var i = 0; i < prioritized.length; i++) { - this[prioritized[i].unit](units[prioritized[i].unit]); - } - } else { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](value); - } - } - return this; - } - - function mod(n, x) { - return ((n % x) + x) % x; - } - - var indexOf; - - if (Array.prototype.indexOf) { - indexOf = Array.prototype.indexOf; - } else { - indexOf = function (o) { - // I know - var i; - for (i = 0; i < this.length; ++i) { - if (this[i] === o) { - return i; - } - } - return -1; - }; - } - - function daysInMonth(year, month) { - if (isNaN(year) || isNaN(month)) { - return NaN; - } - var modMonth = mod(month, 12); - year += (month - modMonth) / 12; - return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); - } - - // FORMATTING - - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); - - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PRIORITY - - addUnitPriority('month', 8); - - // PARSING - - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', function (isStrict, locale) { - return locale.monthsShortRegex(isStrict); - }); - addRegexToken('MMMM', function (isStrict, locale) { - return locale.monthsRegex(isStrict); - }); - - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); - - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; - } else { - getParsingFlags(config).invalidMonth = input; - } - }); - - // LOCALES - - var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); - function localeMonths (m, format) { - if (!m) { - return isArray(this._months) ? this._months : - this._months['standalone']; - } - return isArray(this._months) ? this._months[m.month()] : - this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; - } - - var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); - function localeMonthsShort (m, format) { - if (!m) { - return isArray(this._monthsShort) ? this._monthsShort : - this._monthsShort['standalone']; - } - return isArray(this._monthsShort) ? this._monthsShort[m.month()] : - this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; - } - - function handleStrictParse(monthName, format, strict) { - var i, ii, mom, llc = monthName.toLocaleLowerCase(); - if (!this._monthsParse) { - // this is not used - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - for (i = 0; i < 12; ++i) { - mom = createUTC([2000, i]); - this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); - this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'MMM') { - ii = indexOf.call(this._shortMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeMonthsParse (monthName, format, strict) { - var i, mom, regex; - - if (this._monthsParseExact) { - return handleStrictParse.call(this, monthName, format, strict); - } - - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - - // TODO: add sorting - // Sorting makes sure if one month (or abbr) is a prefix of another - // see sorting in computeMonthsParse - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - } - - // MOMENTS - - function setMonth (mom, value) { - var dayOfMonth; - - if (!mom.isValid()) { - // No op - return mom; - } - - if (typeof value === 'string') { - if (/^\d+$/.test(value)) { - value = toInt(value); - } else { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (!isNumber(value)) { - return mom; - } - } - } - - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } - - function getSetMonth (value) { - if (value != null) { - setMonth(this, value); - hooks.updateOffset(this, true); - return this; - } else { - return get(this, 'Month'); - } - } - - function getDaysInMonth () { - return daysInMonth(this.year(), this.month()); - } - - var defaultMonthsShortRegex = matchWord; - function monthsShortRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsShortStrictRegex; - } else { - return this._monthsShortRegex; - } - } else { - if (!hasOwnProp(this, '_monthsShortRegex')) { - this._monthsShortRegex = defaultMonthsShortRegex; - } - return this._monthsShortStrictRegex && isStrict ? - this._monthsShortStrictRegex : this._monthsShortRegex; - } - } - - var defaultMonthsRegex = matchWord; - function monthsRegex (isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, '_monthsRegex')) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsStrictRegex; - } else { - return this._monthsRegex; - } - } else { - if (!hasOwnProp(this, '_monthsRegex')) { - this._monthsRegex = defaultMonthsRegex; - } - return this._monthsStrictRegex && isStrict ? - this._monthsStrictRegex : this._monthsRegex; - } - } - - function computeMonthsParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var shortPieces = [], longPieces = [], mixedPieces = [], - i, mom; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, i]); - shortPieces.push(this.monthsShort(mom, '')); - longPieces.push(this.months(mom, '')); - mixedPieces.push(this.months(mom, '')); - mixedPieces.push(this.monthsShort(mom, '')); - } - // Sorting makes sure if one month (or abbr) is a prefix of another it - // will match the longer piece. - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 12; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - } - for (i = 0; i < 24; i++) { - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._monthsShortRegex = this._monthsRegex; - this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); - } - - function createDate (y, m, d, h, M, s, ms) { - // can't just apply() to create a date: - // https://stackoverflow.com/q/181348 - var date = new Date(y, m, d, h, M, s, ms); - - // the date constructor remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { - date.setFullYear(y); - } - return date; - } - - function createUTCDate (y) { - var date = new Date(Date.UTC.apply(null, arguments)); - - // the Date.UTC function remaps years 0-99 to 1900-1999 - if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { - date.setUTCFullYear(y); - } - return date; - } - - // start-of-first-week - start-of-year - function firstWeekOffset(year, dow, doy) { - var // first-week day -- which january is always in the first week (4 for iso, 1 for other) - fwd = 7 + dow - doy, - // first-week day local weekday -- which local weekday is fwd - fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; - - return -fwdlw + fwd - 1; - } - - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, dow, doy) { - var localWeekday = (7 + weekday - dow) % 7, - weekOffset = firstWeekOffset(year, dow, doy), - dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, - resYear, resDayOfYear; - - if (dayOfYear <= 0) { - resYear = year - 1; - resDayOfYear = daysInYear(resYear) + dayOfYear; - } else if (dayOfYear > daysInYear(year)) { - resYear = year + 1; - resDayOfYear = dayOfYear - daysInYear(year); - } else { - resYear = year; - resDayOfYear = dayOfYear; - } - - return { - year: resYear, - dayOfYear: resDayOfYear - }; - } - - function weekOfYear(mom, dow, doy) { - var weekOffset = firstWeekOffset(mom.year(), dow, doy), - week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, - resWeek, resYear; - - if (week < 1) { - resYear = mom.year() - 1; - resWeek = week + weeksInYear(resYear, dow, doy); - } else if (week > weeksInYear(mom.year(), dow, doy)) { - resWeek = week - weeksInYear(mom.year(), dow, doy); - resYear = mom.year() + 1; - } else { - resYear = mom.year(); - resWeek = week; - } - - return { - week: resWeek, - year: resYear - }; - } - - function weeksInYear(year, dow, doy) { - var weekOffset = firstWeekOffset(year, dow, doy), - weekOffsetNext = firstWeekOffset(year + 1, dow, doy); - return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; - } - - // FORMATTING - - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PRIORITIES - - addUnitPriority('week', 5); - addUnitPriority('isoWeek', 5); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // LOCALES - - function localeWeek (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - } - - var defaultLocaleWeek = { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }; - - function localeFirstDayOfWeek () { - return this._week.dow; - } - - function localeFirstDayOfYear () { - return this._week.doy; - } - - // MOMENTS - - function getSetWeek (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - function getSetISOWeek (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - // FORMATTING - - addFormatToken('d', 0, 'do', 'day'); - - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); - - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); - - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); - - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); - - // ALIASES - - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); - - // PRIORITY - addUnitPriority('day', 11); - addUnitPriority('weekday', 11); - addUnitPriority('isoWeekday', 11); - - // PARSING - - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', function (isStrict, locale) { - return locale.weekdaysMinRegex(isStrict); - }); - addRegexToken('ddd', function (isStrict, locale) { - return locale.weekdaysShortRegex(isStrict); - }); - addRegexToken('dddd', function (isStrict, locale) { - return locale.weekdaysRegex(isStrict); - }); - - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { - var weekday = config._locale.weekdaysParse(input, token, config._strict); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input !== 'string') { - return input; - } - - if (!isNaN(input)) { - return parseInt(input, 10); - } - - input = locale.weekdaysParse(input); - if (typeof input === 'number') { - return input; - } - - return null; - } - - function parseIsoWeekday(input, locale) { - if (typeof input === 'string') { - return locale.weekdaysParse(input) % 7 || 7; - } - return isNaN(input) ? null : input; - } - - // LOCALES - - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); - function localeWeekdays (m, format) { - if (!m) { - return isArray(this._weekdays) ? this._weekdays : - this._weekdays['standalone']; - } - return isArray(this._weekdays) ? this._weekdays[m.day()] : - this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; - } - - var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); - function localeWeekdaysShort (m) { - return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; - } - - var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); - function localeWeekdaysMin (m) { - return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; - } - - function handleStrictParse$1(weekdayName, format, strict) { - var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._shortWeekdaysParse = []; - this._minWeekdaysParse = []; - - for (i = 0; i < 7; ++i) { - mom = createUTC([2000, 1]).day(i); - this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); - this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); - this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); - } - } - - if (strict) { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format === 'dddd') { - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format === 'ddd') { - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } - } - - function localeWeekdaysParse (weekdayName, format, strict) { - var i, mom, regex; - - if (this._weekdaysParseExact) { - return handleStrictParse$1.call(this, weekdayName, format, strict); - } - - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._minWeekdaysParse = []; - this._shortWeekdaysParse = []; - this._fullWeekdaysParse = []; - } - - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - - mom = createUTC([2000, 1]).day(i); - if (strict && !this._fullWeekdaysParse[i]) { - this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i'); - this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i'); - this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i'); - } - if (!this._weekdaysParse[i]) { - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - - // MOMENTS - - function getSetDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - } - - function getSetLocaleDayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } - - function getSetISODayOfWeek (input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - - if (input != null) { - var weekday = parseIsoWeekday(input, this.localeData()); - return this.day(this.day() % 7 ? weekday : weekday - 7); - } else { - return this.day() || 7; - } - } - - var defaultWeekdaysRegex = matchWord; - function weekdaysRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysStrictRegex; - } else { - return this._weekdaysRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysRegex')) { - this._weekdaysRegex = defaultWeekdaysRegex; - } - return this._weekdaysStrictRegex && isStrict ? - this._weekdaysStrictRegex : this._weekdaysRegex; - } - } - - var defaultWeekdaysShortRegex = matchWord; - function weekdaysShortRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysShortStrictRegex; - } else { - return this._weekdaysShortRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysShortRegex')) { - this._weekdaysShortRegex = defaultWeekdaysShortRegex; - } - return this._weekdaysShortStrictRegex && isStrict ? - this._weekdaysShortStrictRegex : this._weekdaysShortRegex; - } - } - - var defaultWeekdaysMinRegex = matchWord; - function weekdaysMinRegex (isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, '_weekdaysRegex')) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysMinStrictRegex; - } else { - return this._weekdaysMinRegex; - } - } else { - if (!hasOwnProp(this, '_weekdaysMinRegex')) { - this._weekdaysMinRegex = defaultWeekdaysMinRegex; - } - return this._weekdaysMinStrictRegex && isStrict ? - this._weekdaysMinStrictRegex : this._weekdaysMinRegex; - } - } - - - function computeWeekdaysParse () { - function cmpLenRev(a, b) { - return b.length - a.length; - } - - var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], - i, mom, minp, shortp, longp; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - mom = createUTC([2000, 1]).day(i); - minp = this.weekdaysMin(mom, ''); - shortp = this.weekdaysShort(mom, ''); - longp = this.weekdays(mom, ''); - minPieces.push(minp); - shortPieces.push(shortp); - longPieces.push(longp); - mixedPieces.push(minp); - mixedPieces.push(shortp); - mixedPieces.push(longp); - } - // Sorting makes sure if one weekday (or abbr) is a prefix of another it - // will match the longer piece. - minPieces.sort(cmpLenRev); - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - for (i = 0; i < 7; i++) { - shortPieces[i] = regexEscape(shortPieces[i]); - longPieces[i] = regexEscape(longPieces[i]); - mixedPieces[i] = regexEscape(mixedPieces[i]); - } - - this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); - this._weekdaysShortRegex = this._weekdaysRegex; - this._weekdaysMinRegex = this._weekdaysRegex; - - this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); - this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); - this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); - } - - // FORMATTING - - function hFormat() { - return this.hours() % 12 || 12; - } - - function kFormat() { - return this.hours() || 24; - } - - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, hFormat); - addFormatToken('k', ['kk', 2], 0, kFormat); - - addFormatToken('hmm', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); - }); - - addFormatToken('hmmss', 0, 0, function () { - return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - addFormatToken('Hmm', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2); - }); - - addFormatToken('Hmmss', 0, 0, function () { - return '' + this.hours() + zeroFill(this.minutes(), 2) + - zeroFill(this.seconds(), 2); - }); - - function meridiem (token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); - }); - } - - meridiem('a', true); - meridiem('A', false); - - // ALIASES - - addUnitAlias('hour', 'h'); - - // PRIORITY - addUnitPriority('hour', 13); - - // PARSING - - function matchMeridiem (isStrict, locale) { - return locale._meridiemParse; - } - - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('k', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); - addRegexToken('kk', match1to2, match2); - - addRegexToken('hmm', match3to4); - addRegexToken('hmmss', match5to6); - addRegexToken('Hmm', match3to4); - addRegexToken('Hmmss', match5to6); - - addParseToken(['H', 'HH'], HOUR); - addParseToken(['k', 'kk'], function (input, array, config) { - var kInput = toInt(input); - array[HOUR] = kInput === 24 ? 0 : kInput; - }); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - getParsingFlags(config).bigHour = true; - }); - addParseToken('Hmm', function (input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - }); - addParseToken('Hmmss', function (input, array, config) { - var pos1 = input.length - 4; - var pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - }); - - // LOCALES - - function localeIsPM (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - } - - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; - function localeMeridiem (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - } - - - // MOMENTS - - // Setting the hour should keep the time, because the user explicitly - // specified which hour they want. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - var getSetHour = makeGetSet('Hours', true); - - var baseConfig = { - calendar: defaultCalendar, - longDateFormat: defaultLongDateFormat, - invalidDate: defaultInvalidDate, - ordinal: defaultOrdinal, - dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, - relativeTime: defaultRelativeTime, - - months: defaultLocaleMonths, - monthsShort: defaultLocaleMonthsShort, - - week: defaultLocaleWeek, - - weekdays: defaultLocaleWeekdays, - weekdaysMin: defaultLocaleWeekdaysMin, - weekdaysShort: defaultLocaleWeekdaysShort, - - meridiemParse: defaultLocaleMeridiemParse - }; - - // internal storage for locale config files - var locales = {}; - var localeFamilies = {}; - var globalLocale; - - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return globalLocale; - } - - function loadLocale(name) { - var oldLocale = null; - // TODO: Find a better way to register and load all the locales in Node - if (!locales[name] && (typeof module !== 'undefined') && - module && module.exports) { - try { - oldLocale = globalLocale._abbr; - var aliasedRequire = require; - aliasedRequire('./locale/' + name); - getSetGlobalLocale(oldLocale); - } catch (e) {} - } - return locales[name]; - } - - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function getSetGlobalLocale (key, values) { - var data; - if (key) { - if (isUndefined(values)) { - data = getLocale(key); - } - else { - data = defineLocale(key, values); - } - - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } - else { - if ((typeof console !== 'undefined') && console.warn) { - //warn user if arguments are passed but the locale could not be set - console.warn('Locale ' + key + ' not found. Did you forget to load it?'); - } - } - } - - return globalLocale._abbr; - } - - function defineLocale (name, config) { - if (config !== null) { - var locale, parentConfig = baseConfig; - config.abbr = name; - if (locales[name] != null) { - deprecateSimple('defineLocaleOverride', - 'use moment.updateLocale(localeName, config) to change ' + - 'an existing locale. moment.defineLocale(localeName, ' + - 'config) should only be used for creating a new locale ' + - 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); - parentConfig = locales[name]._config; - } else if (config.parentLocale != null) { - if (locales[config.parentLocale] != null) { - parentConfig = locales[config.parentLocale]._config; - } else { - locale = loadLocale(config.parentLocale); - if (locale != null) { - parentConfig = locale._config; - } else { - if (!localeFamilies[config.parentLocale]) { - localeFamilies[config.parentLocale] = []; - } - localeFamilies[config.parentLocale].push({ - name: name, - config: config - }); - return null; - } - } - } - locales[name] = new Locale(mergeConfigs(parentConfig, config)); - - if (localeFamilies[name]) { - localeFamilies[name].forEach(function (x) { - defineLocale(x.name, x.config); - }); - } - - // backwards compat for now: also set the locale - // make sure we set the locale AFTER all child locales have been - // created, so we won't end up with the child locale set. - getSetGlobalLocale(name); - - - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - } - - function updateLocale(name, config) { - if (config != null) { - var locale, tmpLocale, parentConfig = baseConfig; - // MERGE - tmpLocale = loadLocale(name); - if (tmpLocale != null) { - parentConfig = tmpLocale._config; - } - config = mergeConfigs(parentConfig, config); - locale = new Locale(config); - locale.parentLocale = locales[name]; - locales[name] = locale; - - // backwards compat for now: also set the locale - getSetGlobalLocale(name); - } else { - // pass null for config to unupdate, useful for tests - if (locales[name] != null) { - if (locales[name].parentLocale != null) { - locales[name] = locales[name].parentLocale; - } else if (locales[name] != null) { - delete locales[name]; - } - } - } - return locales[name]; - } - - // returns locale data - function getLocale (key) { - var locale; - - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - - if (!key) { - return globalLocale; - } - - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } - - return chooseLocale(key); - } - - function listLocales() { - return keys(locales); - } - - function checkOverflow (m) { - var overflow; - var a = m._a; - - if (a && getParsingFlags(m).overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : - a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : - a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : - a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : - a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : - a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : - -1; - - if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - if (getParsingFlags(m)._overflowWeeks && overflow === -1) { - overflow = WEEK; - } - if (getParsingFlags(m)._overflowWeekday && overflow === -1) { - overflow = WEEKDAY; - } - - getParsingFlags(m).overflow = overflow; - } - - return m; - } - - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - - function currentDateArray(config) { - // hooks is actually the exported moment object - var nowValue = new Date(hooks.now()); - if (config._useUTC) { - return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; - } - return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray (config) { - var i, date, input = [], currentDate, expectedWeekday, yearToUse; - - if (config._d) { - return; - } - - currentDate = currentDateArray(config); - - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - - //if the day of the year is set, figure out what it is - if (config._dayOfYear != null) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - - if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { - getParsingFlags(config)._overflowDayOfYear = true; - } - - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } - - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } - - config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); - expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); - - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - - if (config._nextDay) { - config._a[HOUR] = 24; - } - - // check for mismatching day of week - if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { - getParsingFlags(config).weekdayMismatch = true; - } - } - - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; - - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - if (weekday < 1 || weekday > 7) { - weekdayOverflow = true; - } - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - - var curWeek = weekOfYear(createLocal(), dow, doy); - - weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); - - // Default to current week. - week = defaults(w.w, curWeek.week); - - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < 0 || weekday > 6) { - weekdayOverflow = true; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - if (w.e < 0 || w.e > 6) { - weekdayOverflow = true; - } - } else { - // default to begining of week - weekday = dow; - } - } - if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { - getParsingFlags(config)._overflowWeeks = true; - } else if (weekdayOverflow != null) { - getParsingFlags(config)._overflowWeekday = true; - } else { - temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - } - - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - - var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; - - var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], - ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], - ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], - ['GGGG-[W]WW', /\d{4}-W\d\d/, false], - ['YYYY-DDD', /\d{4}-\d{3}/], - ['YYYY-MM', /\d{4}-\d\d/, false], - ['YYYYYYMMDD', /[+-]\d{10}/], - ['YYYYMMDD', /\d{8}/], - // YYYYMM is NOT allowed by the standard - ['GGGG[W]WWE', /\d{4}W\d{3}/], - ['GGGG[W]WW', /\d{4}W\d{2}/, false], - ['YYYYDDD', /\d{7}/] - ]; - - // iso time formats and regexes - var isoTimes = [ - ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], - ['HH:mm:ss', /\d\d:\d\d:\d\d/], - ['HH:mm', /\d\d:\d\d/], - ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], - ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], - ['HHmmss', /\d\d\d\d\d\d/], - ['HHmm', /\d\d\d\d/], - ['HH', /\d\d/] - ]; - - var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; - - // date from iso format - function configFromISO(config) { - var i, l, - string = config._i, - match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), - allowTime, dateFormat, timeFormat, tzFormat; - - if (match) { - getParsingFlags(config).iso = true; - - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(match[1])) { - dateFormat = isoDates[i][0]; - allowTime = isoDates[i][2] !== false; - break; - } - } - if (dateFormat == null) { - config._isValid = false; - return; - } - if (match[3]) { - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - // match[2] should be 'T' or space - timeFormat = (match[2] || ' ') + isoTimes[i][0]; - break; - } - } - if (timeFormat == null) { - config._isValid = false; - return; - } - } - if (!allowTime && timeFormat != null) { - config._isValid = false; - return; - } - if (match[4]) { - if (tzRegex.exec(match[4])) { - tzFormat = 'Z'; - } else { - config._isValid = false; - return; - } - } - config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); - configFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - - // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 - var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; - - function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { - var result = [ - untruncateYear(yearStr), - defaultLocaleMonthsShort.indexOf(monthStr), - parseInt(dayStr, 10), - parseInt(hourStr, 10), - parseInt(minuteStr, 10) - ]; - - if (secondStr) { - result.push(parseInt(secondStr, 10)); - } - - return result; - } - - function untruncateYear(yearStr) { - var year = parseInt(yearStr, 10); - if (year <= 49) { - return 2000 + year; - } else if (year <= 999) { - return 1900 + year; - } - return year; - } - - function preprocessRFC2822(s) { - // Remove comments and folding whitespace and replace multiple-spaces with a single space - return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - } - - function checkWeekday(weekdayStr, parsedInput, config) { - if (weekdayStr) { - // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. - var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), - weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); - if (weekdayProvided !== weekdayActual) { - getParsingFlags(config).weekdayMismatch = true; - config._isValid = false; - return false; - } - } - return true; - } - - var obsOffsets = { - UT: 0, - GMT: 0, - EDT: -4 * 60, - EST: -5 * 60, - CDT: -5 * 60, - CST: -6 * 60, - MDT: -6 * 60, - MST: -7 * 60, - PDT: -7 * 60, - PST: -8 * 60 - }; - - function calculateOffset(obsOffset, militaryOffset, numOffset) { - if (obsOffset) { - return obsOffsets[obsOffset]; - } else if (militaryOffset) { - // the only allowed military tz is Z - return 0; - } else { - var hm = parseInt(numOffset, 10); - var m = hm % 100, h = (hm - m) / 100; - return h * 60 + m; - } - } - - // date and time from ref 2822 format - function configFromRFC2822(config) { - var match = rfc2822.exec(preprocessRFC2822(config._i)); - if (match) { - var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); - if (!checkWeekday(match[1], parsedArray, config)) { - return; - } - - config._a = parsedArray; - config._tzm = calculateOffset(match[8], match[9], match[10]); - - config._d = createUTCDate.apply(null, config._a); - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - - getParsingFlags(config).rfc2822 = true; - } else { - config._isValid = false; - } - } - - // date from iso format or fallback - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - - if (matched !== null) { - config._d = new Date(+matched[1]); - return; - } - - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - configFromRFC2822(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - - // Final attempt, use Input Fallback - hooks.createFromInputFallback(config); - } - - hooks.createFromInputFallback = deprecate( - 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + - 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + - 'discouraged and will be removed in an upcoming major release. Please refer to ' + - 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); - - // constant that refers to the ISO standard - hooks.ISO_8601 = function () {}; - - // constant that refers to the RFC 2822 form - hooks.RFC_2822 = function () {}; - - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === hooks.ISO_8601) { - configFromISO(config); - return; - } - if (config._f === hooks.RFC_2822) { - configFromRFC2822(config); - return; - } - config._a = []; - getParsingFlags(config).empty = true; - - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; - - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - // console.log('token', token, 'parsedInput', parsedInput, - // 'regex', getParseRegexForToken(token, config)); - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } - else { - getParsingFlags(config).unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - getParsingFlags(config).unusedTokens.push(token); - } - } - - // add remaining unparsed input length to the string - getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - - // clear _12h flag if hour is <= 12 - if (config._a[HOUR] <= 12 && - getParsingFlags(config).bigHour === true && - config._a[HOUR] > 0) { - getParsingFlags(config).bigHour = undefined; - } - - getParsingFlags(config).parsedDateParts = config._a.slice(0); - getParsingFlags(config).meridiem = config._meridiem; - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - - configFromArray(config); - checkOverflow(config); - } - - - function meridiemFixWrap (locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; - } - } - - // date from string and array of format strings - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, - - scoreToBeat, - i, - currentScore; - - if (config._f.length === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = new Date(NaN); - return; - } - - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - - if (!isValid(tempConfig)) { - continue; - } - - // if there is any input that was not parsed add a penalty for that format - currentScore += getParsingFlags(tempConfig).charsLeftOver; - - //or tokens - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - - getParsingFlags(tempConfig).score = currentScore; - - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - - extend(config, bestMoment || tempConfig); - } - - function configFromObject(config) { - if (config._d) { - return; - } - - var i = normalizeObjectUnits(config._i); - config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { - return obj && parseInt(obj, 10); - }); - - configFromArray(config); - } - - function createFromConfig (config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } - - return res; - } - - function prepareConfig (config) { - var input = config._i, - format = config._f; - - config._locale = config._locale || getLocale(config._l); - - if (input === null || (format === undefined && input === '')) { - return createInvalid({nullInput: true}); - } - - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } - - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isDate(input)) { - config._d = input; - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else { - configFromInput(config); - } - - if (!isValid(config)) { - config._d = null; - } - - return config; - } - - function configFromInput(config) { - var input = config._i; - if (isUndefined(input)) { - config._d = new Date(hooks.now()); - } else if (isDate(input)) { - config._d = new Date(input.valueOf()); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (isObject(input)) { - configFromObject(config); - } else if (isNumber(input)) { - // from milliseconds - config._d = new Date(input); - } else { - hooks.createFromInputFallback(config); - } - } - - function createLocalOrUTC (input, format, locale, strict, isUTC) { - var c = {}; - - if (locale === true || locale === false) { - strict = locale; - locale = undefined; - } - - if ((isObject(input) && isObjectEmpty(input)) || - (isArray(input) && input.length === 0)) { - input = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - - return createFromConfig(c); - } - - function createLocal (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); - } - - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other < this ? this : other; - } else { - return createInvalid(); - } - } - ); - - var prototypeMax = deprecate( - 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', - function () { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other > this ? this : other; - } else { - return createInvalid(); - } - } - ); - - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - - // TODO: Use [].sort instead? - function min () { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - } - - function max () { - var args = [].slice.call(arguments, 0); - - return pickBy('isAfter', args); - } - - var now = function () { - return Date.now ? Date.now() : +(new Date()); - }; - - var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; - - function isDurationValid(m) { - for (var key in m) { - if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { - return false; - } - } - - var unitHasDecimal = false; - for (var i = 0; i < ordering.length; ++i) { - if (m[ordering[i]]) { - if (unitHasDecimal) { - return false; // only allow non-integers for smallest unit - } - if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { - unitHasDecimal = true; - } - } - } - - return true; - } - - function isValid$1() { - return this._isValid; - } - - function createInvalid$1() { - return createDuration(NaN); - } - - function Duration (duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - this._isValid = isDurationValid(normalizedInput); - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible to translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = getLocale(); - - this._bubble(); - } - - function isDuration (obj) { - return obj instanceof Duration; - } - - function absRound (number) { - if (number < 0) { - return Math.round(-1 * number) * -1; - } else { - return Math.round(number); - } - } - - // FORMATTING - - function offset (token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(); - var sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); - }); - } - - offset('Z', ':'); - offset('ZZ', ''); - - // PARSING - - addRegexToken('Z', matchShortOffset); - addRegexToken('ZZ', matchShortOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(matchShortOffset, input); - }); - - // HELPERS - - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; - - function offsetFromString(matcher, string) { - var matches = (string || '').match(matcher); - - if (matches === null) { - return null; - } - - var chunk = matches[matches.length - 1] || []; - var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - var minutes = +(parts[1] * 60) + toInt(parts[2]); - - return minutes === 0 ? - 0 : - parts[0] === '+' ? minutes : -minutes; - } - - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); - // Use low-level api, because this fn is low-level api. - res._d.setTime(res._d.valueOf() + diff); - hooks.updateOffset(res, false); - return res; - } else { - return createLocal(input).local(); - } - } - - function getDateOffset (m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset() / 15) * 15; - } - - // HOOKS - - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - hooks.updateOffset = function () {}; - - // MOMENTS - - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset (input, keepLocalTime, keepMinutes) { - var offset = this._offset || 0, - localAdjust; - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(matchShortOffset, input); - if (input === null) { - return this; - } - } else if (Math.abs(input) < 16 && !keepMinutes) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addSubtract(this, createDuration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : getDateOffset(this); - } - } - - function getSetZone (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); - - return this; - } else { - return -this.utcOffset(); - } - } - - function setOffsetToUTC (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } - - function setOffsetToLocal (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; - - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); - } - } - return this; - } - - function setOffsetToParsedOffset () { - if (this._tzm != null) { - this.utcOffset(this._tzm, false, true); - } else if (typeof this._i === 'string') { - var tZone = offsetFromString(matchOffset, this._i); - if (tZone != null) { - this.utcOffset(tZone); - } - else { - this.utcOffset(0, true); - } - } - return this; - } - - function hasAlignedHourOffset (input) { - if (!this.isValid()) { - return false; - } - input = input ? createLocal(input).utcOffset() : 0; - - return (this.utcOffset() - input) % 60 === 0; - } - - function isDaylightSavingTime () { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); - } - - function isDaylightSavingTimeShifted () { - if (!isUndefined(this._isDSTShifted)) { - return this._isDSTShifted; - } - - var c = {}; - - copyConfig(c, this); - c = prepareConfig(c); - - if (c._a) { - var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); - this._isDSTShifted = this.isValid() && - compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; - } - - return this._isDSTShifted; - } - - function isLocal () { - return this.isValid() ? !this._isUTC : false; - } - - function isUtcOffset () { - return this.isValid() ? this._isUTC : false; - } - - function isUtc () { - return this.isValid() ? this._isUTC && this._offset === 0 : false; - } - - // ASP.NET json date format regex - var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; - - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - // and further modified to allow for strings containing both week and day - var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; - - function createDuration (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; - - if (isDuration(input)) { - duration = { - ms : input._milliseconds, - d : input._days, - M : input._months - }; - } else if (isNumber(input)) { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : 0, - d : toInt(match[DATE]) * sign, - h : toInt(match[HOUR]) * sign, - m : toInt(match[MINUTE]) * sign, - s : toInt(match[SECOND]) * sign, - ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match - }; - } else if (!!(match = isoRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; - duration = { - y : parseIso(match[2], sign), - M : parseIso(match[3], sign), - w : parseIso(match[4], sign), - d : parseIso(match[5], sign), - h : parseIso(match[6], sign), - m : parseIso(match[7], sign), - s : parseIso(match[8], sign) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); - - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - - ret = new Duration(duration); - - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - - return ret; - } - - createDuration.fn = Duration.prototype; - createDuration.invalid = createInvalid$1; - - function parseIso (inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } - - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; - - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } - - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - - return res; - } - - function momentsDifference(base, other) { - var res; - if (!(base.isValid() && other.isValid())) { - return {milliseconds: 0, months: 0}; - } - - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - - return res; - } - - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + - 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); - tmp = val; val = period; period = tmp; - } - - val = typeof val === 'string' ? +val : val; - dur = createDuration(val, period); - addSubtract(this, dur, direction); - return this; - }; - } - - function addSubtract (mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = absRound(duration._days), - months = absRound(duration._months); - - if (!mom.isValid()) { - // No op - return; - } - - updateOffset = updateOffset == null ? true : updateOffset; - - if (months) { - setMonth(mom, get(mom, 'Month') + months * isAdding); - } - if (days) { - set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); - } - if (milliseconds) { - mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); - } - if (updateOffset) { - hooks.updateOffset(mom, days || months); - } - } - - var add = createAdder(1, 'add'); - var subtract = createAdder(-1, 'subtract'); - - function getCalendarFormat(myMoment, now) { - var diff = myMoment.diff(now, 'days', true); - return diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - } - - function calendar$1 (time, formats) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - format = hooks.calendarFormat(this, sod) || 'sameElse'; - - var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); - - return this.format(output || this.localeData().calendar(format, this, createLocal(now))); - } - - function clone () { - return new Moment(this); - } - - function isAfter (input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() > localInput.valueOf(); - } else { - return localInput.valueOf() < this.clone().startOf(units).valueOf(); - } - } - - function isBefore (input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() < localInput.valueOf(); - } else { - return this.clone().endOf(units).valueOf() < localInput.valueOf(); - } - } - - function isBetween (from, to, units, inclusivity) { - inclusivity = inclusivity || '()'; - return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && - (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); - } - - function isSame (input, units) { - var localInput = isMoment(input) ? input : createLocal(input), - inputMs; - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - return this.valueOf() === localInput.valueOf(); - } else { - inputMs = localInput.valueOf(); - return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); - } - } - - function isSameOrAfter (input, units) { - return this.isSame(input, units) || this.isAfter(input,units); - } - - function isSameOrBefore (input, units) { - return this.isSame(input, units) || this.isBefore(input,units); - } - - function diff (input, units, asFloat) { - var that, - zoneDelta, - output; - - if (!this.isValid()) { - return NaN; - } - - that = cloneWithOffset(input, this); - - if (!that.isValid()) { - return NaN; - } - - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; - - units = normalizeUnits(units); - - switch (units) { - case 'year': output = monthDiff(this, that) / 12; break; - case 'month': output = monthDiff(this, that); break; - case 'quarter': output = monthDiff(this, that) / 3; break; - case 'second': output = (this - that) / 1e3; break; // 1000 - case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 - case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 - case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst - case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst - default: output = this - that; - } - - return asFloat ? output : absFloor(output); - } - - function monthDiff (a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - - //check for negative zero, return zero if negative zero - return -(wholeMonthDiff + adjust) || 0; - } - - hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; - - function toString () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } - - function toISOString(keepOffset) { - if (!this.isValid()) { - return null; - } - var utc = keepOffset !== true; - var m = utc ? this.clone().utc() : this; - if (m.year() < 0 || m.year() > 9999) { - return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); - } - if (isFunction(Date.prototype.toISOString)) { - // native implementation is ~50x faster, use it when we can - if (utc) { - return this.toDate().toISOString(); - } else { - return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z')); - } - } - return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); - } - - /** - * Return a human readable representation of a moment that can - * also be evaluated to get a new moment which is the same - * - * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects - */ - function inspect () { - if (!this.isValid()) { - return 'moment.invalid(/* ' + this._i + ' */)'; - } - var func = 'moment'; - var zone = ''; - if (!this.isLocal()) { - func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; - zone = 'Z'; - } - var prefix = '[' + func + '("]'; - var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; - var datetime = '-MM-DD[T]HH:mm:ss.SSS'; - var suffix = zone + '[")]'; - - return this.format(prefix + year + datetime + suffix); - } - - function format (inputString) { - if (!inputString) { - inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; - } - var output = formatMoment(this, inputString); - return this.localeData().postformat(output); - } - - function from (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - createLocal(time).isValid())) { - return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function fromNow (withoutSuffix) { - return this.from(createLocal(), withoutSuffix); - } - - function to (time, withoutSuffix) { - if (this.isValid() && - ((isMoment(time) && time.isValid()) || - createLocal(time).isValid())) { - return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - - function toNow (withoutSuffix) { - return this.to(createLocal(), withoutSuffix); - } - - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - function locale (key) { - var newLocaleData; - - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - } - - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); - - function localeData () { - return this._locale; - } - - function startOf (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - case 'date': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - } - - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } - if (units === 'isoWeek') { - this.isoWeekday(1); - } - - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } - - return this; - } - - function endOf (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - - // 'date' is an alias for 'day', so it should be considered as such. - if (units === 'date') { - units = 'day'; - } - - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - } - - function valueOf () { - return this._d.valueOf() - ((this._offset || 0) * 60000); - } - - function unix () { - return Math.floor(this.valueOf() / 1000); - } - - function toDate () { - return new Date(this.valueOf()); - } - - function toArray () { - var m = this; - return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; - } - - function toObject () { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds() - }; - } - - function toJSON () { - // new Date(NaN).toJSON() === null - return this.isValid() ? this.toISOString() : null; - } - - function isValid$2 () { - return isValid(this); - } - - function parsingFlags () { - return extend({}, getParsingFlags(this)); - } - - function invalidAt () { - return getParsingFlags(this).overflow; - } - - function creationData() { - return { - input: this._i, - format: this._f, - locale: this._locale, - isUTC: this._isUTC, - strict: this._strict - }; - } - - // FORMATTING - - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken (token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } - - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - - // ALIASES - - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); - - // PRIORITY - - addUnitPriority('weekYear', 1); - addUnitPriority('isoWeekYear', 1); - - - // PARSING - - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); - - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { - week[token.substr(0, 2)] = toInt(input); - }); - - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = hooks.parseTwoDigitYear(input); - }); - - // MOMENTS - - function getSetWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, - this.week(), - this.weekday(), - this.localeData()._week.dow, - this.localeData()._week.doy); - } - - function getSetISOWeekYear (input) { - return getSetWeekYearHelper.call(this, - input, this.isoWeek(), this.isoWeekday(), 1, 4); - } - - function getISOWeeksInYear () { - return weeksInYear(this.year(), 1, 4); - } - - function getWeeksInYear () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - - function getSetWeekYearHelper(input, week, weekday, dow, doy) { - var weeksTarget; - if (input == null) { - return weekOfYear(this, dow, doy).year; - } else { - weeksTarget = weeksInYear(input, dow, doy); - if (week > weeksTarget) { - week = weeksTarget; - } - return setWeekAll.call(this, input, week, weekday, dow, doy); - } - } - - function setWeekAll(weekYear, week, weekday, dow, doy) { - var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), - date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - - this.year(date.getUTCFullYear()); - this.month(date.getUTCMonth()); - this.date(date.getUTCDate()); - return this; - } - - // FORMATTING - - addFormatToken('Q', 0, 'Qo', 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); - - // PRIORITY - - addUnitPriority('quarter', 7); - - // PARSING - - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - - // MOMENTS - - function getSetQuarter (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } - - // FORMATTING - - addFormatToken('D', ['DD', 2], 'Do', 'date'); - - // ALIASES - - addUnitAlias('date', 'D'); - - // PRIORITY - addUnitPriority('date', 9); - - // PARSING - - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - // TODO: Remove "ordinalParse" fallback in next major release. - return isStrict ? - (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : - locale._dayOfMonthOrdinalParseLenient; - }); - - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0]); - }); - - // MOMENTS - - var getSetDayOfMonth = makeGetSet('Date', true); - - // FORMATTING - - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - - // ALIASES - - addUnitAlias('dayOfYear', 'DDD'); - - // PRIORITY - addUnitPriority('dayOfYear', 4); - - // PARSING - - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); - - // HELPERS - - // MOMENTS - - function getSetDayOfYear (input) { - var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - } - - // FORMATTING - - addFormatToken('m', ['mm', 2], 0, 'minute'); - - // ALIASES - - addUnitAlias('minute', 'm'); - - // PRIORITY - - addUnitPriority('minute', 14); - - // PARSING - - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); - - // MOMENTS - - var getSetMinute = makeGetSet('Minutes', false); - - // FORMATTING - - addFormatToken('s', ['ss', 2], 0, 'second'); - - // ALIASES - - addUnitAlias('second', 's'); - - // PRIORITY - - addUnitPriority('second', 15); - - // PARSING - - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - - // MOMENTS - - var getSetSecond = makeGetSet('Seconds', false); - - // FORMATTING - - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); - - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); - - addFormatToken(0, ['SSS', 3], 0, 'millisecond'); - addFormatToken(0, ['SSSS', 4], 0, function () { - return this.millisecond() * 10; - }); - addFormatToken(0, ['SSSSS', 5], 0, function () { - return this.millisecond() * 100; - }); - addFormatToken(0, ['SSSSSS', 6], 0, function () { - return this.millisecond() * 1000; - }); - addFormatToken(0, ['SSSSSSS', 7], 0, function () { - return this.millisecond() * 10000; - }); - addFormatToken(0, ['SSSSSSSS', 8], 0, function () { - return this.millisecond() * 100000; - }); - addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { - return this.millisecond() * 1000000; - }); - - - // ALIASES - - addUnitAlias('millisecond', 'ms'); - - // PRIORITY - - addUnitPriority('millisecond', 16); - - // PARSING - - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - - var token; - for (token = 'SSSS'; token.length <= 9; token += 'S') { - addRegexToken(token, matchUnsigned); - } - - function parseMs(input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - } - - for (token = 'S'; token.length <= 9; token += 'S') { - addParseToken(token, parseMs); - } - // MOMENTS - - var getSetMillisecond = makeGetSet('Milliseconds', false); - - // FORMATTING - - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); - - // MOMENTS - - function getZoneAbbr () { - return this._isUTC ? 'UTC' : ''; - } - - function getZoneName () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - } - - var proto = Moment.prototype; - - proto.add = add; - proto.calendar = calendar$1; - proto.clone = clone; - proto.diff = diff; - proto.endOf = endOf; - proto.format = format; - proto.from = from; - proto.fromNow = fromNow; - proto.to = to; - proto.toNow = toNow; - proto.get = stringGet; - proto.invalidAt = invalidAt; - proto.isAfter = isAfter; - proto.isBefore = isBefore; - proto.isBetween = isBetween; - proto.isSame = isSame; - proto.isSameOrAfter = isSameOrAfter; - proto.isSameOrBefore = isSameOrBefore; - proto.isValid = isValid$2; - proto.lang = lang; - proto.locale = locale; - proto.localeData = localeData; - proto.max = prototypeMax; - proto.min = prototypeMin; - proto.parsingFlags = parsingFlags; - proto.set = stringSet; - proto.startOf = startOf; - proto.subtract = subtract; - proto.toArray = toArray; - proto.toObject = toObject; - proto.toDate = toDate; - proto.toISOString = toISOString; - proto.inspect = inspect; - proto.toJSON = toJSON; - proto.toString = toString; - proto.unix = unix; - proto.valueOf = valueOf; - proto.creationData = creationData; - proto.year = getSetYear; - proto.isLeapYear = getIsLeapYear; - proto.weekYear = getSetWeekYear; - proto.isoWeekYear = getSetISOWeekYear; - proto.quarter = proto.quarters = getSetQuarter; - proto.month = getSetMonth; - proto.daysInMonth = getDaysInMonth; - proto.week = proto.weeks = getSetWeek; - proto.isoWeek = proto.isoWeeks = getSetISOWeek; - proto.weeksInYear = getWeeksInYear; - proto.isoWeeksInYear = getISOWeeksInYear; - proto.date = getSetDayOfMonth; - proto.day = proto.days = getSetDayOfWeek; - proto.weekday = getSetLocaleDayOfWeek; - proto.isoWeekday = getSetISODayOfWeek; - proto.dayOfYear = getSetDayOfYear; - proto.hour = proto.hours = getSetHour; - proto.minute = proto.minutes = getSetMinute; - proto.second = proto.seconds = getSetSecond; - proto.millisecond = proto.milliseconds = getSetMillisecond; - proto.utcOffset = getSetOffset; - proto.utc = setOffsetToUTC; - proto.local = setOffsetToLocal; - proto.parseZone = setOffsetToParsedOffset; - proto.hasAlignedHourOffset = hasAlignedHourOffset; - proto.isDST = isDaylightSavingTime; - proto.isLocal = isLocal; - proto.isUtcOffset = isUtcOffset; - proto.isUtc = isUtc; - proto.isUTC = isUtc; - proto.zoneAbbr = getZoneAbbr; - proto.zoneName = getZoneName; - proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); - proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); - proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); - proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); - proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); - - function createUnix (input) { - return createLocal(input * 1000); - } - - function createInZone () { - return createLocal.apply(null, arguments).parseZone(); - } - - function preParsePostFormat (string) { - return string; - } - - var proto$1 = Locale.prototype; - - proto$1.calendar = calendar; - proto$1.longDateFormat = longDateFormat; - proto$1.invalidDate = invalidDate; - proto$1.ordinal = ordinal; - proto$1.preparse = preParsePostFormat; - proto$1.postformat = preParsePostFormat; - proto$1.relativeTime = relativeTime; - proto$1.pastFuture = pastFuture; - proto$1.set = set; - - proto$1.months = localeMonths; - proto$1.monthsShort = localeMonthsShort; - proto$1.monthsParse = localeMonthsParse; - proto$1.monthsRegex = monthsRegex; - proto$1.monthsShortRegex = monthsShortRegex; - proto$1.week = localeWeek; - proto$1.firstDayOfYear = localeFirstDayOfYear; - proto$1.firstDayOfWeek = localeFirstDayOfWeek; - - proto$1.weekdays = localeWeekdays; - proto$1.weekdaysMin = localeWeekdaysMin; - proto$1.weekdaysShort = localeWeekdaysShort; - proto$1.weekdaysParse = localeWeekdaysParse; - - proto$1.weekdaysRegex = weekdaysRegex; - proto$1.weekdaysShortRegex = weekdaysShortRegex; - proto$1.weekdaysMinRegex = weekdaysMinRegex; - - proto$1.isPM = localeIsPM; - proto$1.meridiem = localeMeridiem; - - function get$1 (format, index, field, setter) { - var locale = getLocale(); - var utc = createUTC().set(setter, index); - return locale[field](utc, format); - } - - function listMonthsImpl (format, index, field) { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - - if (index != null) { - return get$1(format, index, field, 'month'); - } - - var i; - var out = []; - for (i = 0; i < 12; i++) { - out[i] = get$1(format, i, field, 'month'); - } - return out; - } - - // () - // (5) - // (fmt, 5) - // (fmt) - // (true) - // (true, 5) - // (true, fmt, 5) - // (true, fmt) - function listWeekdaysImpl (localeSorted, format, index, field) { - if (typeof localeSorted === 'boolean') { - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } else { - format = localeSorted; - index = format; - localeSorted = false; - - if (isNumber(format)) { - index = format; - format = undefined; - } - - format = format || ''; - } - - var locale = getLocale(), - shift = localeSorted ? locale._week.dow : 0; - - if (index != null) { - return get$1(format, (index + shift) % 7, field, 'day'); - } - - var i; - var out = []; - for (i = 0; i < 7; i++) { - out[i] = get$1(format, (i + shift) % 7, field, 'day'); - } - return out; - } - - function listMonths (format, index) { - return listMonthsImpl(format, index, 'months'); - } - - function listMonthsShort (format, index) { - return listMonthsImpl(format, index, 'monthsShort'); - } - - function listWeekdays (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); - } - - function listWeekdaysShort (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); - } - - function listWeekdaysMin (localeSorted, format, index) { - return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); - } - - getSetGlobalLocale('en', { - dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - // Side effect imports - - hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); - hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); - - var mathAbs = Math.abs; - - function abs () { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function addSubtract$1 (duration, input, value, direction) { - var other = createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function add$1 (input, value) { - return addSubtract$1(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function subtract$1 (input, value) { - return addSubtract$1(this, input, value, -1); - } - - function absCeil (number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); - } - } - - function bubble () { - var milliseconds = this._milliseconds; - var days = this._days; - var months = this._months; - var data = this._data; - var seconds, minutes, hours, years, monthsFromDays; - - // if we have a mix of positive and negative values, bubble down first - // check: https://github.com/moment/moment/issues/2166 - if (!((milliseconds >= 0 && days >= 0 && months >= 0) || - (milliseconds <= 0 && days <= 0 && months <= 0))) { - milliseconds += absCeil(monthsToDays(months) + days) * 864e5; - days = 0; - months = 0; - } - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // convert days to months - monthsFromDays = absFloor(daysToMonths(days)); - months += monthsFromDays; - days -= absCeil(monthsToDays(monthsFromDays)); - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; - } - - function daysToMonths (days) { - // 400 years have 146097 days (taking into account leap year rules) - // 400 years have 12 months === 4800 - return days * 4800 / 146097; - } - - function monthsToDays (months) { - // the reverse of daysToMonths - return months * 146097 / 4800; - } - - function as (units) { - if (!this.isValid()) { - return NaN; - } - var days; - var months; - var milliseconds = this._milliseconds; - - units = normalizeUnits(units); - - if (units === 'month' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToMonths(days); - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case 'week' : return days / 7 + milliseconds / 6048e5; - case 'day' : return days + milliseconds / 864e5; - case 'hour' : return days * 24 + milliseconds / 36e5; - case 'minute' : return days * 1440 + milliseconds / 6e4; - case 'second' : return days * 86400 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 864e5) + milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - } - - // TODO: Use this.as('ms')? - function valueOf$1 () { - if (!this.isValid()) { - return NaN; - } - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs (alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'); - var asSeconds = makeAs('s'); - var asMinutes = makeAs('m'); - var asHours = makeAs('h'); - var asDays = makeAs('d'); - var asWeeks = makeAs('w'); - var asMonths = makeAs('M'); - var asYears = makeAs('y'); - - function clone$1 () { - return createDuration(this); - } - - function get$2 (units) { - units = normalizeUnits(units); - return this.isValid() ? this[units + 's']() : NaN; - } - - function makeGetter(name) { - return function () { - return this.isValid() ? this._data[name] : NaN; - }; - } - - var milliseconds = makeGetter('milliseconds'); - var seconds = makeGetter('seconds'); - var minutes = makeGetter('minutes'); - var hours = makeGetter('hours'); - var days = makeGetter('days'); - var months = makeGetter('months'); - var years = makeGetter('years'); - - function weeks () { - return absFloor(this.days() / 7); - } - - var round = Math.round; - var thresholds = { - ss: 44, // a few seconds to seconds - s : 45, // seconds to minute - m : 45, // minutes to hour - h : 22, // hours to day - d : 26, // days to month - M : 11 // months to year - }; - - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - - function relativeTime$1 (posNegDuration, withoutSuffix, locale) { - var duration = createDuration(posNegDuration).abs(); - var seconds = round(duration.as('s')); - var minutes = round(duration.as('m')); - var hours = round(duration.as('h')); - var days = round(duration.as('d')); - var months = round(duration.as('M')); - var years = round(duration.as('y')); - - var a = seconds <= thresholds.ss && ['s', seconds] || - seconds < thresholds.s && ['ss', seconds] || - minutes <= 1 && ['m'] || - minutes < thresholds.m && ['mm', minutes] || - hours <= 1 && ['h'] || - hours < thresholds.h && ['hh', hours] || - days <= 1 && ['d'] || - days < thresholds.d && ['dd', days] || - months <= 1 && ['M'] || - months < thresholds.M && ['MM', months] || - years <= 1 && ['y'] || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set the rounding function for relative time strings - function getSetRelativeTimeRounding (roundingFunction) { - if (roundingFunction === undefined) { - return round; - } - if (typeof(roundingFunction) === 'function') { - round = roundingFunction; - return true; - } - return false; - } - - // This function allows you to set a threshold for relative time strings - function getSetRelativeTimeThreshold (threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - if (threshold === 's') { - thresholds.ss = limit - 1; - } - return true; - } - - function humanize (withSuffix) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var locale = this.localeData(); - var output = relativeTime$1(this, !withSuffix, locale); - - if (withSuffix) { - output = locale.pastFuture(+this, output); - } - - return locale.postformat(output); - } - - var abs$1 = Math.abs; - - function sign(x) { - return ((x > 0) - (x < 0)) || +x; - } - - function toISOString$1() { - // for ISO strings we do not use the normal bubbling rules: - // * milliseconds bubble up until they become hours - // * days do not bubble at all - // * months bubble up until they become years - // This is because there is no context-free conversion between hours and days - // (think of clock changes) - // and also not between days and months (28-31 days per month) - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - - var seconds = abs$1(this._milliseconds) / 1000; - var days = abs$1(this._days); - var months = abs$1(this._months); - var minutes, hours, years; - - // 3600 seconds -> 60 minutes -> 1 hour - minutes = absFloor(seconds / 60); - hours = absFloor(minutes / 60); - seconds %= 60; - minutes %= 60; - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = years; - var M = months; - var D = days; - var h = hours; - var m = minutes; - var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; - var total = this.asSeconds(); - - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } - - var totalSign = total < 0 ? '-' : ''; - var ymSign = sign(this._months) !== sign(total) ? '-' : ''; - var daysSign = sign(this._days) !== sign(total) ? '-' : ''; - var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; - - return totalSign + 'P' + - (Y ? ymSign + Y + 'Y' : '') + - (M ? ymSign + M + 'M' : '') + - (D ? daysSign + D + 'D' : '') + - ((h || m || s) ? 'T' : '') + - (h ? hmsSign + h + 'H' : '') + - (m ? hmsSign + m + 'M' : '') + - (s ? hmsSign + s + 'S' : ''); - } - - var proto$2 = Duration.prototype; - - proto$2.isValid = isValid$1; - proto$2.abs = abs; - proto$2.add = add$1; - proto$2.subtract = subtract$1; - proto$2.as = as; - proto$2.asMilliseconds = asMilliseconds; - proto$2.asSeconds = asSeconds; - proto$2.asMinutes = asMinutes; - proto$2.asHours = asHours; - proto$2.asDays = asDays; - proto$2.asWeeks = asWeeks; - proto$2.asMonths = asMonths; - proto$2.asYears = asYears; - proto$2.valueOf = valueOf$1; - proto$2._bubble = bubble; - proto$2.clone = clone$1; - proto$2.get = get$2; - proto$2.milliseconds = milliseconds; - proto$2.seconds = seconds; - proto$2.minutes = minutes; - proto$2.hours = hours; - proto$2.days = days; - proto$2.weeks = weeks; - proto$2.months = months; - proto$2.years = years; - proto$2.humanize = humanize; - proto$2.toISOString = toISOString$1; - proto$2.toString = toISOString$1; - proto$2.toJSON = toISOString$1; - proto$2.locale = locale; - proto$2.localeData = localeData; - - proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); - proto$2.lang = lang; - - // Side effect imports - - // FORMATTING - - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input, 10) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - // Side effect imports - - //! moment.js - - hooks.version = '2.22.2'; - - setHookCallback(createLocal); - - hooks.fn = proto; - hooks.min = min; - hooks.max = max; - hooks.now = now; - hooks.utc = createUTC; - hooks.unix = createUnix; - hooks.months = listMonths; - hooks.isDate = isDate; - hooks.locale = getSetGlobalLocale; - hooks.invalid = createInvalid; - hooks.duration = createDuration; - hooks.isMoment = isMoment; - hooks.weekdays = listWeekdays; - hooks.parseZone = createInZone; - hooks.localeData = getLocale; - hooks.isDuration = isDuration; - hooks.monthsShort = listMonthsShort; - hooks.weekdaysMin = listWeekdaysMin; - hooks.defineLocale = defineLocale; - hooks.updateLocale = updateLocale; - hooks.locales = listLocales; - hooks.weekdaysShort = listWeekdaysShort; - hooks.normalizeUnits = normalizeUnits; - hooks.relativeTimeRounding = getSetRelativeTimeRounding; - hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; - hooks.calendarFormat = getCalendarFormat; - hooks.prototype = proto; - - // currently HTML5 input type only supports 24-hour formats - hooks.HTML5_FMT = { - DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // - DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // - DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // - DATE: 'YYYY-MM-DD', // - TIME: 'HH:mm', // - TIME_SECONDS: 'HH:mm:ss', // - TIME_MS: 'HH:mm:ss.SSS', // - WEEK: 'YYYY-[W]WW', // - MONTH: 'YYYY-MM' // - }; - - //! moment.js locale configuration - - hooks.defineLocale('af', { - months : 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split('_'), - weekdaysShort : 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'), - weekdaysMin : 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'), - meridiemParse: /vm|nm/i, - isPM : function (input) { - return /^nm$/i.test(input); - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower ? 'vm' : 'VM'; - } else { - return isLower ? 'nm' : 'NM'; - } - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Vandag om] LT', - nextDay : '[Môre om] LT', - nextWeek : 'dddd [om] LT', - lastDay : '[Gister om] LT', - lastWeek : '[Laas] dddd [om] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'oor %s', - past : '%s gelede', - s : '\'n paar sekondes', - ss : '%d sekondes', - m : '\'n minuut', - mm : '%d minute', - h : '\'n uur', - hh : '%d ure', - d : '\'n dag', - dd : '%d dae', - M : '\'n maand', - MM : '%d maande', - y : '\'n jaar', - yy : '%d jaar' - }, - dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); // Thanks to Joris Röling : https://github.com/jjupiter - }, - week : { - dow : 1, // Maandag is die eerste dag van die week. - doy : 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ar-dz', { - months : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - monthsShort : 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'أح_إث_ثلا_أر_خم_جم_سب'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - ss : '%d ثانية', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 4 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ar-kw', { - months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - ss : '%d ثانية', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap = { - '1': '1', - '2': '2', - '3': '3', - '4': '4', - '5': '5', - '6': '6', - '7': '7', - '8': '8', - '9': '9', - '0': '0' - }, pluralForm = function (n) { - return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; - }, plurals = { - s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], - m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], - h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], - d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'], - M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], - y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] - }, pluralize = function (u) { - return function (number, withoutSuffix, string, isFuture) { - var f = pluralForm(number), - str = plurals[u][pluralForm(number)]; - if (f === 2) { - str = str[withoutSuffix ? 0 : 1]; - } - return str.replace(/%d/i, number); - }; - }, months$1 = [ - 'يناير', - 'فبراير', - 'مارس', - 'أبريل', - 'مايو', - 'يونيو', - 'يوليو', - 'أغسطس', - 'سبتمبر', - 'أكتوبر', - 'نوفمبر', - 'ديسمبر' - ]; - - hooks.defineLocale('ar-ly', { - months : months$1, - monthsShort : months$1, - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'D/\u200FM/\u200FYYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ص|م/, - isPM : function (input) { - return 'م' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ص'; - } else { - return 'م'; - } - }, - calendar : { - sameDay: '[اليوم عند الساعة] LT', - nextDay: '[غدًا عند الساعة] LT', - nextWeek: 'dddd [عند الساعة] LT', - lastDay: '[أمس عند الساعة] LT', - lastWeek: 'dddd [عند الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'بعد %s', - past : 'منذ %s', - s : pluralize('s'), - ss : pluralize('s'), - m : pluralize('m'), - mm : pluralize('m'), - h : pluralize('h'), - hh : pluralize('h'), - d : pluralize('d'), - dd : pluralize('d'), - M : pluralize('M'), - MM : pluralize('M'), - y : pluralize('y'), - yy : pluralize('y') - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap[match]; - }).replace(/,/g, '،'); - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ar-ma', { - months : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - monthsShort : 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split('_'), - weekdays : 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - ss : '%d ثانية', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$1 = { - '1': '١', - '2': '٢', - '3': '٣', - '4': '٤', - '5': '٥', - '6': '٦', - '7': '٧', - '8': '٨', - '9': '٩', - '0': '٠' - }, numberMap = { - '١': '1', - '٢': '2', - '٣': '3', - '٤': '4', - '٥': '5', - '٦': '6', - '٧': '7', - '٨': '8', - '٩': '9', - '٠': '0' - }; - - hooks.defineLocale('ar-sa', { - months : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - monthsShort : 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ص|م/, - isPM : function (input) { - return 'م' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ص'; - } else { - return 'م'; - } - }, - calendar : { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'في %s', - past : 'منذ %s', - s : 'ثوان', - ss : '%d ثانية', - m : 'دقيقة', - mm : '%d دقائق', - h : 'ساعة', - hh : '%d ساعات', - d : 'يوم', - dd : '%d أيام', - M : 'شهر', - MM : '%d أشهر', - y : 'سنة', - yy : '%d سنوات' - }, - preparse: function (string) { - return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { - return numberMap[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$1[match]; - }).replace(/,/g, '،'); - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ar-tn', { - months: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - monthsShort: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split('_'), - weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[اليوم على الساعة] LT', - nextDay: '[غدا على الساعة] LT', - nextWeek: 'dddd [على الساعة] LT', - lastDay: '[أمس على الساعة] LT', - lastWeek: 'dddd [على الساعة] LT', - sameElse: 'L' - }, - relativeTime: { - future: 'في %s', - past: 'منذ %s', - s: 'ثوان', - ss : '%d ثانية', - m: 'دقيقة', - mm: '%d دقائق', - h: 'ساعة', - hh: '%d ساعات', - d: 'يوم', - dd: '%d أيام', - M: 'شهر', - MM: '%d أشهر', - y: 'سنة', - yy: '%d سنوات' - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$2 = { - '1': '١', - '2': '٢', - '3': '٣', - '4': '٤', - '5': '٥', - '6': '٦', - '7': '٧', - '8': '٨', - '9': '٩', - '0': '٠' - }, numberMap$1 = { - '١': '1', - '٢': '2', - '٣': '3', - '٤': '4', - '٥': '5', - '٦': '6', - '٧': '7', - '٨': '8', - '٩': '9', - '٠': '0' - }, pluralForm$1 = function (n) { - return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5; - }, plurals$1 = { - s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'], - m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'], - h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'], - d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'], - M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'], - y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام'] - }, pluralize$1 = function (u) { - return function (number, withoutSuffix, string, isFuture) { - var f = pluralForm$1(number), - str = plurals$1[u][pluralForm$1(number)]; - if (f === 2) { - str = str[withoutSuffix ? 0 : 1]; - } - return str.replace(/%d/i, number); - }; - }, months$2 = [ - 'يناير', - 'فبراير', - 'مارس', - 'أبريل', - 'مايو', - 'يونيو', - 'يوليو', - 'أغسطس', - 'سبتمبر', - 'أكتوبر', - 'نوفمبر', - 'ديسمبر' - ]; - - hooks.defineLocale('ar', { - months : months$2, - monthsShort : months$2, - weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'), - weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'), - weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'D/\u200FM/\u200FYYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ص|م/, - isPM : function (input) { - return 'م' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ص'; - } else { - return 'م'; - } - }, - calendar : { - sameDay: '[اليوم عند الساعة] LT', - nextDay: '[غدًا عند الساعة] LT', - nextWeek: 'dddd [عند الساعة] LT', - lastDay: '[أمس عند الساعة] LT', - lastWeek: 'dddd [عند الساعة] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'بعد %s', - past : 'منذ %s', - s : pluralize$1('s'), - ss : pluralize$1('s'), - m : pluralize$1('m'), - mm : pluralize$1('m'), - h : pluralize$1('h'), - hh : pluralize$1('h'), - d : pluralize$1('d'), - dd : pluralize$1('d'), - M : pluralize$1('M'), - MM : pluralize$1('M'), - y : pluralize$1('y'), - yy : pluralize$1('y') - }, - preparse: function (string) { - return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) { - return numberMap$1[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$2[match]; - }).replace(/,/g, '،'); - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var suffixes = { - 1: '-inci', - 5: '-inci', - 8: '-inci', - 70: '-inci', - 80: '-inci', - 2: '-nci', - 7: '-nci', - 20: '-nci', - 50: '-nci', - 3: '-üncü', - 4: '-üncü', - 100: '-üncü', - 6: '-ncı', - 9: '-uncu', - 10: '-uncu', - 30: '-uncu', - 60: '-ıncı', - 90: '-ıncı' - }; - - hooks.defineLocale('az', { - months : 'yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr'.split('_'), - monthsShort : 'yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek'.split('_'), - weekdays : 'Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə'.split('_'), - weekdaysShort : 'Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən'.split('_'), - weekdaysMin : 'Bz_BE_ÇA_Çə_CA_Cü_Şə'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[bugün saat] LT', - nextDay : '[sabah saat] LT', - nextWeek : '[gələn həftə] dddd [saat] LT', - lastDay : '[dünən] LT', - lastWeek : '[keçən həftə] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s sonra', - past : '%s əvvəl', - s : 'birneçə saniyə', - ss : '%d saniyə', - m : 'bir dəqiqə', - mm : '%d dəqiqə', - h : 'bir saat', - hh : '%d saat', - d : 'bir gün', - dd : '%d gün', - M : 'bir ay', - MM : '%d ay', - y : 'bir il', - yy : '%d il' - }, - meridiemParse: /gecə|səhər|gündüz|axşam/, - isPM : function (input) { - return /^(gündüz|axşam)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'gecə'; - } else if (hour < 12) { - return 'səhər'; - } else if (hour < 17) { - return 'gündüz'; - } else { - return 'axşam'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/, - ordinal : function (number) { - if (number === 0) { // special case for zero - return number + '-ıncı'; - } - var a = number % 10, - b = number % 100 - a, - c = number >= 100 ? 100 : null; - return number + (suffixes[a] || suffixes[b] || suffixes[c]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function plural(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); - } - function relativeTimeWithPlural(number, withoutSuffix, key) { - var format = { - 'ss': withoutSuffix ? 'секунда_секунды_секунд' : 'секунду_секунды_секунд', - 'mm': withoutSuffix ? 'хвіліна_хвіліны_хвілін' : 'хвіліну_хвіліны_хвілін', - 'hh': withoutSuffix ? 'гадзіна_гадзіны_гадзін' : 'гадзіну_гадзіны_гадзін', - 'dd': 'дзень_дні_дзён', - 'MM': 'месяц_месяцы_месяцаў', - 'yy': 'год_гады_гадоў' - }; - if (key === 'm') { - return withoutSuffix ? 'хвіліна' : 'хвіліну'; - } - else if (key === 'h') { - return withoutSuffix ? 'гадзіна' : 'гадзіну'; - } - else { - return number + ' ' + plural(format[key], +number); - } - } - - hooks.defineLocale('be', { - months : { - format: 'студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня'.split('_'), - standalone: 'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split('_') - }, - monthsShort : 'студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж'.split('_'), - weekdays : { - format: 'нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу'.split('_'), - standalone: 'нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота'.split('_'), - isFormat: /\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/ - }, - weekdaysShort : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), - weekdaysMin : 'нд_пн_ат_ср_чц_пт_сб'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY г.', - LLL : 'D MMMM YYYY г., HH:mm', - LLLL : 'dddd, D MMMM YYYY г., HH:mm' - }, - calendar : { - sameDay: '[Сёння ў] LT', - nextDay: '[Заўтра ў] LT', - lastDay: '[Учора ў] LT', - nextWeek: function () { - return '[У] dddd [ў] LT'; - }, - lastWeek: function () { - switch (this.day()) { - case 0: - case 3: - case 5: - case 6: - return '[У мінулую] dddd [ў] LT'; - case 1: - case 2: - case 4: - return '[У мінулы] dddd [ў] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'праз %s', - past : '%s таму', - s : 'некалькі секунд', - m : relativeTimeWithPlural, - mm : relativeTimeWithPlural, - h : relativeTimeWithPlural, - hh : relativeTimeWithPlural, - d : 'дзень', - dd : relativeTimeWithPlural, - M : 'месяц', - MM : relativeTimeWithPlural, - y : 'год', - yy : relativeTimeWithPlural - }, - meridiemParse: /ночы|раніцы|дня|вечара/, - isPM : function (input) { - return /^(дня|вечара)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночы'; - } else if (hour < 12) { - return 'раніцы'; - } else if (hour < 17) { - return 'дня'; - } else { - return 'вечара'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}-(і|ы|га)/, - ordinal: function (number, period) { - switch (period) { - case 'M': - case 'd': - case 'DDD': - case 'w': - case 'W': - return (number % 10 === 2 || number % 10 === 3) && (number % 100 !== 12 && number % 100 !== 13) ? number + '-і' : number + '-ы'; - case 'D': - return number + '-га'; - default: - return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('bg', { - months : 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'), - monthsShort : 'янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек'.split('_'), - weekdays : 'неделя_понеделник_вторник_сряда_четвъртък_петък_събота'.split('_'), - weekdaysShort : 'нед_пон_вто_сря_чет_пет_съб'.split('_'), - weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'D.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Днес в] LT', - nextDay : '[Утре в] LT', - nextWeek : 'dddd [в] LT', - lastDay : '[Вчера в] LT', - lastWeek : function () { - switch (this.day()) { - case 0: - case 3: - case 6: - return '[В изминалата] dddd [в] LT'; - case 1: - case 2: - case 4: - case 5: - return '[В изминалия] dddd [в] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'след %s', - past : 'преди %s', - s : 'няколко секунди', - ss : '%d секунди', - m : 'минута', - mm : '%d минути', - h : 'час', - hh : '%d часа', - d : 'ден', - dd : '%d дни', - M : 'месец', - MM : '%d месеца', - y : 'година', - yy : '%d години' - }, - dayOfMonthOrdinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, - ordinal : function (number) { - var lastDigit = number % 10, - last2Digits = number % 100; - if (number === 0) { - return number + '-ев'; - } else if (last2Digits === 0) { - return number + '-ен'; - } else if (last2Digits > 10 && last2Digits < 20) { - return number + '-ти'; - } else if (lastDigit === 1) { - return number + '-ви'; - } else if (lastDigit === 2) { - return number + '-ри'; - } else if (lastDigit === 7 || lastDigit === 8) { - return number + '-ми'; - } else { - return number + '-ти'; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('bm', { - months : 'Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo'.split('_'), - monthsShort : 'Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des'.split('_'), - weekdays : 'Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri'.split('_'), - weekdaysShort : 'Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib'.split('_'), - weekdaysMin : 'Ka_Nt_Ta_Ar_Al_Ju_Si'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'MMMM [tile] D [san] YYYY', - LLL : 'MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm', - LLLL : 'dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm' - }, - calendar : { - sameDay : '[Bi lɛrɛ] LT', - nextDay : '[Sini lɛrɛ] LT', - nextWeek : 'dddd [don lɛrɛ] LT', - lastDay : '[Kunu lɛrɛ] LT', - lastWeek : 'dddd [tɛmɛnen lɛrɛ] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s kɔnɔ', - past : 'a bɛ %s bɔ', - s : 'sanga dama dama', - ss : 'sekondi %d', - m : 'miniti kelen', - mm : 'miniti %d', - h : 'lɛrɛ kelen', - hh : 'lɛrɛ %d', - d : 'tile kelen', - dd : 'tile %d', - M : 'kalo kelen', - MM : 'kalo %d', - y : 'san kelen', - yy : 'san %d' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$3 = { - '1': '১', - '2': '২', - '3': '৩', - '4': '৪', - '5': '৫', - '6': '৬', - '7': '৭', - '8': '৮', - '9': '৯', - '0': '০' - }, - numberMap$2 = { - '১': '1', - '২': '2', - '৩': '3', - '৪': '4', - '৫': '5', - '৬': '6', - '৭': '7', - '৮': '8', - '৯': '9', - '০': '0' - }; - - hooks.defineLocale('bn', { - months : 'জানুয়ারী_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split('_'), - monthsShort : 'জানু_ফেব_মার্চ_এপ্র_মে_জুন_জুল_আগ_সেপ্ট_অক্টো_নভে_ডিসে'.split('_'), - weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার'.split('_'), - weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি'.split('_'), - weekdaysMin : 'রবি_সোম_মঙ্গ_বুধ_বৃহঃ_শুক্র_শনি'.split('_'), - longDateFormat : { - LT : 'A h:mm সময়', - LTS : 'A h:mm:ss সময়', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm সময়', - LLLL : 'dddd, D MMMM YYYY, A h:mm সময়' - }, - calendar : { - sameDay : '[আজ] LT', - nextDay : '[আগামীকাল] LT', - nextWeek : 'dddd, LT', - lastDay : '[গতকাল] LT', - lastWeek : '[গত] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s পরে', - past : '%s আগে', - s : 'কয়েক সেকেন্ড', - ss : '%d সেকেন্ড', - m : 'এক মিনিট', - mm : '%d মিনিট', - h : 'এক ঘন্টা', - hh : '%d ঘন্টা', - d : 'এক দিন', - dd : '%d দিন', - M : 'এক মাস', - MM : '%d মাস', - y : 'এক বছর', - yy : '%d বছর' - }, - preparse: function (string) { - return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) { - return numberMap$2[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$3[match]; - }); - }, - meridiemParse: /রাত|সকাল|দুপুর|বিকাল|রাত/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'রাত' && hour >= 4) || - (meridiem === 'দুপুর' && hour < 5) || - meridiem === 'বিকাল') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'রাত'; - } else if (hour < 10) { - return 'সকাল'; - } else if (hour < 17) { - return 'দুপুর'; - } else if (hour < 20) { - return 'বিকাল'; - } else { - return 'রাত'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$4 = { - '1': '༡', - '2': '༢', - '3': '༣', - '4': '༤', - '5': '༥', - '6': '༦', - '7': '༧', - '8': '༨', - '9': '༩', - '0': '༠' - }, - numberMap$3 = { - '༡': '1', - '༢': '2', - '༣': '3', - '༤': '4', - '༥': '5', - '༦': '6', - '༧': '7', - '༨': '8', - '༩': '9', - '༠': '0' - }; - - hooks.defineLocale('bo', { - months : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), - monthsShort : 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'), - weekdays : 'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split('_'), - weekdaysShort : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), - weekdaysMin : 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm', - LLLL : 'dddd, D MMMM YYYY, A h:mm' - }, - calendar : { - sameDay : '[དི་རིང] LT', - nextDay : '[སང་ཉིན] LT', - nextWeek : '[བདུན་ཕྲག་རྗེས་མ], LT', - lastDay : '[ཁ་སང] LT', - lastWeek : '[བདུན་ཕྲག་མཐའ་མ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ལ་', - past : '%s སྔན་ལ', - s : 'ལམ་སང', - ss : '%d སྐར་ཆ།', - m : 'སྐར་མ་གཅིག', - mm : '%d སྐར་མ', - h : 'ཆུ་ཚོད་གཅིག', - hh : '%d ཆུ་ཚོད', - d : 'ཉིན་གཅིག', - dd : '%d ཉིན་', - M : 'ཟླ་བ་གཅིག', - MM : '%d ཟླ་བ', - y : 'ལོ་གཅིག', - yy : '%d ལོ' - }, - preparse: function (string) { - return string.replace(/[༡༢༣༤༥༦༧༨༩༠]/g, function (match) { - return numberMap$3[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$4[match]; - }); - }, - meridiemParse: /མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'མཚན་མོ' && hour >= 4) || - (meridiem === 'ཉིན་གུང' && hour < 5) || - meridiem === 'དགོང་དག') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'མཚན་མོ'; - } else if (hour < 10) { - return 'ཞོགས་ཀས'; - } else if (hour < 17) { - return 'ཉིན་གུང'; - } else if (hour < 20) { - return 'དགོང་དག'; - } else { - return 'མཚན་མོ'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function relativeTimeWithMutation(number, withoutSuffix, key) { - var format = { - 'mm': 'munutenn', - 'MM': 'miz', - 'dd': 'devezh' - }; - return number + ' ' + mutation(format[key], number); - } - function specialMutationForYears(number) { - switch (lastNumber(number)) { - case 1: - case 3: - case 4: - case 5: - case 9: - return number + ' bloaz'; - default: - return number + ' vloaz'; - } - } - function lastNumber(number) { - if (number > 9) { - return lastNumber(number % 10); - } - return number; - } - function mutation(text, number) { - if (number === 2) { - return softMutation(text); - } - return text; - } - function softMutation(text) { - var mutationTable = { - 'm': 'v', - 'b': 'v', - 'd': 'z' - }; - if (mutationTable[text.charAt(0)] === undefined) { - return text; - } - return mutationTable[text.charAt(0)] + text.substring(1); - } - - hooks.defineLocale('br', { - months : 'Genver_C\'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split('_'), - monthsShort : 'Gen_C\'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'), - weekdays : 'Sul_Lun_Meurzh_Merc\'her_Yaou_Gwener_Sadorn'.split('_'), - weekdaysShort : 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'), - weekdaysMin : 'Su_Lu_Me_Mer_Ya_Gw_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'h[e]mm A', - LTS : 'h[e]mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D [a viz] MMMM YYYY', - LLL : 'D [a viz] MMMM YYYY h[e]mm A', - LLLL : 'dddd, D [a viz] MMMM YYYY h[e]mm A' - }, - calendar : { - sameDay : '[Hiziv da] LT', - nextDay : '[Warc\'hoazh da] LT', - nextWeek : 'dddd [da] LT', - lastDay : '[Dec\'h da] LT', - lastWeek : 'dddd [paset da] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'a-benn %s', - past : '%s \'zo', - s : 'un nebeud segondennoù', - ss : '%d eilenn', - m : 'ur vunutenn', - mm : relativeTimeWithMutation, - h : 'un eur', - hh : '%d eur', - d : 'un devezh', - dd : relativeTimeWithMutation, - M : 'ur miz', - MM : relativeTimeWithMutation, - y : 'ur bloaz', - yy : specialMutationForYears - }, - dayOfMonthOrdinalParse: /\d{1,2}(añ|vet)/, - ordinal : function (number) { - var output = (number === 1) ? 'añ' : 'vet'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function translate(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { - case 'ss': - if (number === 1) { - result += 'sekunda'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'sekunde'; - } else { - result += 'sekundi'; - } - return result; - case 'm': - return withoutSuffix ? 'jedna minuta' : 'jedne minute'; - case 'mm': - if (number === 1) { - result += 'minuta'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'minute'; - } else { - result += 'minuta'; - } - return result; - case 'h': - return withoutSuffix ? 'jedan sat' : 'jednog sata'; - case 'hh': - if (number === 1) { - result += 'sat'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'sata'; - } else { - result += 'sati'; - } - return result; - case 'dd': - if (number === 1) { - result += 'dan'; - } else { - result += 'dana'; - } - return result; - case 'MM': - if (number === 1) { - result += 'mjesec'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'mjeseca'; - } else { - result += 'mjeseci'; - } - return result; - case 'yy': - if (number === 1) { - result += 'godina'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'godine'; - } else { - result += 'godina'; - } - return result; - } - } - - hooks.defineLocale('bs', { - months : 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort : 'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danas u] LT', - nextDay : '[sutra u] LT', - nextWeek : function () { - switch (this.day()) { - case 0: - return '[u] [nedjelju] [u] LT'; - case 3: - return '[u] [srijedu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[u] dddd [u] LT'; - } - }, - lastDay : '[jučer u] LT', - lastWeek : function () { - switch (this.day()) { - case 0: - case 3: - return '[prošlu] dddd [u] LT'; - case 6: - return '[prošle] [subote] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[prošli] dddd [u] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'par sekundi', - ss : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : 'dan', - dd : translate, - M : 'mjesec', - MM : translate, - y : 'godinu', - yy : translate - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ca', { - months : { - standalone: 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'), - format: 'de gener_de febrer_de març_d\'abril_de maig_de juny_de juliol_d\'agost_de setembre_d\'octubre_de novembre_de desembre'.split('_'), - isFormat: /D[oD]?(\s)+MMMM/ - }, - monthsShort : 'gen._febr._març_abr._maig_juny_jul._ag._set._oct._nov._des.'.split('_'), - monthsParseExact : true, - weekdays : 'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split('_'), - weekdaysShort : 'dg._dl._dt._dc._dj._dv._ds.'.split('_'), - weekdaysMin : 'dg_dl_dt_dc_dj_dv_ds'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM [de] YYYY', - ll : 'D MMM YYYY', - LLL : 'D MMMM [de] YYYY [a les] H:mm', - lll : 'D MMM YYYY, H:mm', - LLLL : 'dddd D MMMM [de] YYYY [a les] H:mm', - llll : 'ddd D MMM YYYY, H:mm' - }, - calendar : { - sameDay : function () { - return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - nextDay : function () { - return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - lastDay : function () { - return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'd\'aquí %s', - past : 'fa %s', - s : 'uns segons', - ss : '%d segons', - m : 'un minut', - mm : '%d minuts', - h : 'una hora', - hh : '%d hores', - d : 'un dia', - dd : '%d dies', - M : 'un mes', - MM : '%d mesos', - y : 'un any', - yy : '%d anys' - }, - dayOfMonthOrdinalParse: /\d{1,2}(r|n|t|è|a)/, - ordinal : function (number, period) { - var output = (number === 1) ? 'r' : - (number === 2) ? 'n' : - (number === 3) ? 'r' : - (number === 4) ? 't' : 'è'; - if (period === 'w' || period === 'W') { - output = 'a'; - } - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var months$3 = 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split('_'), - monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_'); - function plural$1(n) { - return (n > 1) && (n < 5) && (~~(n / 10) !== 1); - } - function translate$1(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { - case 's': // a few seconds / in a few seconds / a few seconds ago - return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami'; - case 'ss': // 9 seconds / in 9 seconds / 9 seconds ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'sekundy' : 'sekund'); - } else { - return result + 'sekundami'; - } - break; - case 'm': // a minute / in a minute / a minute ago - return withoutSuffix ? 'minuta' : (isFuture ? 'minutu' : 'minutou'); - case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'minuty' : 'minut'); - } else { - return result + 'minutami'; - } - break; - case 'h': // an hour / in an hour / an hour ago - return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou'); - case 'hh': // 9 hours / in 9 hours / 9 hours ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'hodiny' : 'hodin'); - } else { - return result + 'hodinami'; - } - break; - case 'd': // a day / in a day / a day ago - return (withoutSuffix || isFuture) ? 'den' : 'dnem'; - case 'dd': // 9 days / in 9 days / 9 days ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'dny' : 'dní'); - } else { - return result + 'dny'; - } - break; - case 'M': // a month / in a month / a month ago - return (withoutSuffix || isFuture) ? 'měsíc' : 'měsícem'; - case 'MM': // 9 months / in 9 months / 9 months ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'měsíce' : 'měsíců'); - } else { - return result + 'měsíci'; - } - break; - case 'y': // a year / in a year / a year ago - return (withoutSuffix || isFuture) ? 'rok' : 'rokem'; - case 'yy': // 9 years / in 9 years / 9 years ago - if (withoutSuffix || isFuture) { - return result + (plural$1(number) ? 'roky' : 'let'); - } else { - return result + 'lety'; - } - break; - } - } - - hooks.defineLocale('cs', { - months : months$3, - monthsShort : monthsShort, - monthsParse : (function (months, monthsShort) { - var i, _monthsParse = []; - for (i = 0; i < 12; i++) { - // use custom parser to solve problem with July (červenec) - _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i'); - } - return _monthsParse; - }(months$3, monthsShort)), - shortMonthsParse : (function (monthsShort) { - var i, _shortMonthsParse = []; - for (i = 0; i < 12; i++) { - _shortMonthsParse[i] = new RegExp('^' + monthsShort[i] + '$', 'i'); - } - return _shortMonthsParse; - }(monthsShort)), - longMonthsParse : (function (months) { - var i, _longMonthsParse = []; - for (i = 0; i < 12; i++) { - _longMonthsParse[i] = new RegExp('^' + months[i] + '$', 'i'); - } - return _longMonthsParse; - }(months$3)), - weekdays : 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'), - weekdaysShort : 'ne_po_út_st_čt_pá_so'.split('_'), - weekdaysMin : 'ne_po_út_st_čt_pá_so'.split('_'), - longDateFormat : { - LT: 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd D. MMMM YYYY H:mm', - l : 'D. M. YYYY' - }, - calendar : { - sameDay: '[dnes v] LT', - nextDay: '[zítra v] LT', - nextWeek: function () { - switch (this.day()) { - case 0: - return '[v neděli v] LT'; - case 1: - case 2: - return '[v] dddd [v] LT'; - case 3: - return '[ve středu v] LT'; - case 4: - return '[ve čtvrtek v] LT'; - case 5: - return '[v pátek v] LT'; - case 6: - return '[v sobotu v] LT'; - } - }, - lastDay: '[včera v] LT', - lastWeek: function () { - switch (this.day()) { - case 0: - return '[minulou neděli v] LT'; - case 1: - case 2: - return '[minulé] dddd [v] LT'; - case 3: - return '[minulou středu v] LT'; - case 4: - case 5: - return '[minulý] dddd [v] LT'; - case 6: - return '[minulou sobotu v] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'za %s', - past : 'před %s', - s : translate$1, - ss : translate$1, - m : translate$1, - mm : translate$1, - h : translate$1, - hh : translate$1, - d : translate$1, - dd : translate$1, - M : translate$1, - MM : translate$1, - y : translate$1, - yy : translate$1 - }, - dayOfMonthOrdinalParse : /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('cv', { - months : 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split('_'), - monthsShort : 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'), - weekdays : 'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split('_'), - weekdaysShort : 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'), - weekdaysMin : 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]', - LLL : 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm', - LLLL : 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm' - }, - calendar : { - sameDay: '[Паян] LT [сехетре]', - nextDay: '[Ыран] LT [сехетре]', - lastDay: '[Ӗнер] LT [сехетре]', - nextWeek: '[Ҫитес] dddd LT [сехетре]', - lastWeek: '[Иртнӗ] dddd LT [сехетре]', - sameElse: 'L' - }, - relativeTime : { - future : function (output) { - var affix = /сехет$/i.exec(output) ? 'рен' : /ҫул$/i.exec(output) ? 'тан' : 'ран'; - return output + affix; - }, - past : '%s каялла', - s : 'пӗр-ик ҫеккунт', - ss : '%d ҫеккунт', - m : 'пӗр минут', - mm : '%d минут', - h : 'пӗр сехет', - hh : '%d сехет', - d : 'пӗр кун', - dd : '%d кун', - M : 'пӗр уйӑх', - MM : '%d уйӑх', - y : 'пӗр ҫул', - yy : '%d ҫул' - }, - dayOfMonthOrdinalParse: /\d{1,2}-мӗш/, - ordinal : '%d-мӗш', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('cy', { - months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split('_'), - monthsShort: 'Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag'.split('_'), - weekdays: 'Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn'.split('_'), - weekdaysShort: 'Sul_Llun_Maw_Mer_Iau_Gwe_Sad'.split('_'), - weekdaysMin: 'Su_Ll_Ma_Me_Ia_Gw_Sa'.split('_'), - weekdaysParseExact : true, - // time formats are the same as en-gb - longDateFormat: { - LT: 'HH:mm', - LTS : 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd, D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[Heddiw am] LT', - nextDay: '[Yfory am] LT', - nextWeek: 'dddd [am] LT', - lastDay: '[Ddoe am] LT', - lastWeek: 'dddd [diwethaf am] LT', - sameElse: 'L' - }, - relativeTime: { - future: 'mewn %s', - past: '%s yn ôl', - s: 'ychydig eiliadau', - ss: '%d eiliad', - m: 'munud', - mm: '%d munud', - h: 'awr', - hh: '%d awr', - d: 'diwrnod', - dd: '%d diwrnod', - M: 'mis', - MM: '%d mis', - y: 'blwyddyn', - yy: '%d flynedd' - }, - dayOfMonthOrdinalParse: /\d{1,2}(fed|ain|af|il|ydd|ed|eg)/, - // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh - ordinal: function (number) { - var b = number, - output = '', - lookup = [ - '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed - 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed - ]; - if (b > 20) { - if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) { - output = 'fed'; // not 30ain, 70ain or 90ain - } else { - output = 'ain'; - } - } else if (b > 0) { - output = lookup[b]; - } - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('da', { - months : 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), - weekdaysShort : 'søn_man_tir_ons_tor_fre_lør'.split('_'), - weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd [d.] D. MMMM YYYY [kl.] HH:mm' - }, - calendar : { - sameDay : '[i dag kl.] LT', - nextDay : '[i morgen kl.] LT', - nextWeek : 'på dddd [kl.] LT', - lastDay : '[i går kl.] LT', - lastWeek : '[i] dddd[s kl.] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s siden', - s : 'få sekunder', - ss : '%d sekunder', - m : 'et minut', - mm : '%d minutter', - h : 'en time', - hh : '%d timer', - d : 'en dag', - dd : '%d dage', - M : 'en måned', - MM : '%d måneder', - y : 'et år', - yy : '%d år' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eine Minute', 'einer Minute'], - 'h': ['eine Stunde', 'einer Stunde'], - 'd': ['ein Tag', 'einem Tag'], - 'dd': [number + ' Tage', number + ' Tagen'], - 'M': ['ein Monat', 'einem Monat'], - 'MM': [number + ' Monate', number + ' Monaten'], - 'y': ['ein Jahr', 'einem Jahr'], - 'yy': [number + ' Jahre', number + ' Jahren'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; - } - - hooks.defineLocale('de-at', { - months : 'Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort : 'Jän._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), - weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), - weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd, D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[heute um] LT [Uhr]', - sameElse: 'L', - nextDay: '[morgen um] LT [Uhr]', - nextWeek: 'dddd [um] LT [Uhr]', - lastDay: '[gestern um] LT [Uhr]', - lastWeek: '[letzten] dddd [um] LT [Uhr]' - }, - relativeTime : { - future : 'in %s', - past : 'vor %s', - s : 'ein paar Sekunden', - ss : '%d Sekunden', - m : processRelativeTime, - mm : '%d Minuten', - h : processRelativeTime, - hh : '%d Stunden', - d : processRelativeTime, - dd : processRelativeTime, - M : processRelativeTime, - MM : processRelativeTime, - y : processRelativeTime, - yy : processRelativeTime - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$1(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eine Minute', 'einer Minute'], - 'h': ['eine Stunde', 'einer Stunde'], - 'd': ['ein Tag', 'einem Tag'], - 'dd': [number + ' Tage', number + ' Tagen'], - 'M': ['ein Monat', 'einem Monat'], - 'MM': [number + ' Monate', number + ' Monaten'], - 'y': ['ein Jahr', 'einem Jahr'], - 'yy': [number + ' Jahre', number + ' Jahren'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; - } - - hooks.defineLocale('de-ch', { - months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort : 'Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), - weekdaysShort : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd, D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[heute um] LT [Uhr]', - sameElse: 'L', - nextDay: '[morgen um] LT [Uhr]', - nextWeek: 'dddd [um] LT [Uhr]', - lastDay: '[gestern um] LT [Uhr]', - lastWeek: '[letzten] dddd [um] LT [Uhr]' - }, - relativeTime : { - future : 'in %s', - past : 'vor %s', - s : 'ein paar Sekunden', - ss : '%d Sekunden', - m : processRelativeTime$1, - mm : '%d Minuten', - h : processRelativeTime$1, - hh : '%d Stunden', - d : processRelativeTime$1, - dd : processRelativeTime$1, - M : processRelativeTime$1, - MM : processRelativeTime$1, - y : processRelativeTime$1, - yy : processRelativeTime$1 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$2(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eine Minute', 'einer Minute'], - 'h': ['eine Stunde', 'einer Stunde'], - 'd': ['ein Tag', 'einem Tag'], - 'dd': [number + ' Tage', number + ' Tagen'], - 'M': ['ein Monat', 'einem Monat'], - 'MM': [number + ' Monate', number + ' Monaten'], - 'y': ['ein Jahr', 'einem Jahr'], - 'yy': [number + ' Jahre', number + ' Jahren'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; - } - - hooks.defineLocale('de', { - months : 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort : 'Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split('_'), - weekdaysShort : 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'), - weekdaysMin : 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY HH:mm', - LLLL : 'dddd, D. MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[heute um] LT [Uhr]', - sameElse: 'L', - nextDay: '[morgen um] LT [Uhr]', - nextWeek: 'dddd [um] LT [Uhr]', - lastDay: '[gestern um] LT [Uhr]', - lastWeek: '[letzten] dddd [um] LT [Uhr]' - }, - relativeTime : { - future : 'in %s', - past : 'vor %s', - s : 'ein paar Sekunden', - ss : '%d Sekunden', - m : processRelativeTime$2, - mm : '%d Minuten', - h : processRelativeTime$2, - hh : '%d Stunden', - d : processRelativeTime$2, - dd : processRelativeTime$2, - M : processRelativeTime$2, - MM : processRelativeTime$2, - y : processRelativeTime$2, - yy : processRelativeTime$2 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var months$4 = [ - 'ޖެނުއަރީ', - 'ފެބްރުއަރީ', - 'މާރިޗު', - 'އޭޕްރީލު', - 'މޭ', - 'ޖޫން', - 'ޖުލައި', - 'އޯގަސްޓު', - 'ސެޕްޓެމްބަރު', - 'އޮކްޓޯބަރު', - 'ނޮވެމްބަރު', - 'ޑިސެމްބަރު' - ], weekdays = [ - 'އާދިއްތަ', - 'ހޯމަ', - 'އަންގާރަ', - 'ބުދަ', - 'ބުރާސްފަތި', - 'ހުކުރު', - 'ހޮނިހިރު' - ]; - - hooks.defineLocale('dv', { - months : months$4, - monthsShort : months$4, - weekdays : weekdays, - weekdaysShort : weekdays, - weekdaysMin : 'އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި'.split('_'), - longDateFormat : { - - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'D/M/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - meridiemParse: /މކ|މފ/, - isPM : function (input) { - return 'މފ' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'މކ'; - } else { - return 'މފ'; - } - }, - calendar : { - sameDay : '[މިއަދު] LT', - nextDay : '[މާދަމާ] LT', - nextWeek : 'dddd LT', - lastDay : '[އިއްޔެ] LT', - lastWeek : '[ފާއިތުވި] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ތެރޭގައި %s', - past : 'ކުރިން %s', - s : 'ސިކުންތުކޮޅެއް', - ss : 'd% ސިކުންތު', - m : 'މިނިޓެއް', - mm : 'މިނިޓު %d', - h : 'ގަޑިއިރެއް', - hh : 'ގަޑިއިރު %d', - d : 'ދުވަހެއް', - dd : 'ދުވަސް %d', - M : 'މަހެއް', - MM : 'މަސް %d', - y : 'އަހަރެއް', - yy : 'އަހަރު %d' - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/,/g, '،'); - }, - week : { - dow : 7, // Sunday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('el', { - monthsNominativeEl : 'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split('_'), - monthsGenitiveEl : 'Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου'.split('_'), - months : function (momentToFormat, format) { - if (!momentToFormat) { - return this._monthsNominativeEl; - } else if (typeof format === 'string' && /D/.test(format.substring(0, format.indexOf('MMMM')))) { // if there is a day number before 'MMMM' - return this._monthsGenitiveEl[momentToFormat.month()]; - } else { - return this._monthsNominativeEl[momentToFormat.month()]; - } - }, - monthsShort : 'Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ'.split('_'), - weekdays : 'Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο'.split('_'), - weekdaysShort : 'Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ'.split('_'), - weekdaysMin : 'Κυ_Δε_Τρ_Τε_Πε_Πα_Σα'.split('_'), - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'μμ' : 'ΜΜ'; - } else { - return isLower ? 'πμ' : 'ΠΜ'; - } - }, - isPM : function (input) { - return ((input + '').toLowerCase()[0] === 'μ'); - }, - meridiemParse : /[ΠΜ]\.?Μ?\.?/i, - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendarEl : { - sameDay : '[Σήμερα {}] LT', - nextDay : '[Αύριο {}] LT', - nextWeek : 'dddd [{}] LT', - lastDay : '[Χθες {}] LT', - lastWeek : function () { - switch (this.day()) { - case 6: - return '[το προηγούμενο] dddd [{}] LT'; - default: - return '[την προηγούμενη] dddd [{}] LT'; - } - }, - sameElse : 'L' - }, - calendar : function (key, mom) { - var output = this._calendarEl[key], - hours = mom && mom.hours(); - if (isFunction(output)) { - output = output.apply(mom); - } - return output.replace('{}', (hours % 12 === 1 ? 'στη' : 'στις')); - }, - relativeTime : { - future : 'σε %s', - past : '%s πριν', - s : 'λίγα δευτερόλεπτα', - ss : '%d δευτερόλεπτα', - m : 'ένα λεπτό', - mm : '%d λεπτά', - h : 'μία ώρα', - hh : '%d ώρες', - d : 'μία μέρα', - dd : '%d μέρες', - M : 'ένας μήνας', - MM : '%d μήνες', - y : 'ένας χρόνος', - yy : '%d χρόνια' - }, - dayOfMonthOrdinalParse: /\d{1,2}η/, - ordinal: '%dη', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-au', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-ca', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'YYYY-MM-DD', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-gb', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-ie', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-il', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('en-nz', { - months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - ss : '%d seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('eo', { - months : 'januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec'.split('_'), - weekdays : 'dimanĉo_lundo_mardo_merkredo_ĵaŭdo_vendredo_sabato'.split('_'), - weekdaysShort : 'dim_lun_mard_merk_ĵaŭ_ven_sab'.split('_'), - weekdaysMin : 'di_lu_ma_me_ĵa_ve_sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D[-a de] MMMM, YYYY', - LLL : 'D[-a de] MMMM, YYYY HH:mm', - LLLL : 'dddd, [la] D[-a de] MMMM, YYYY HH:mm' - }, - meridiemParse: /[ap]\.t\.m/i, - isPM: function (input) { - return input.charAt(0).toLowerCase() === 'p'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'p.t.m.' : 'P.T.M.'; - } else { - return isLower ? 'a.t.m.' : 'A.T.M.'; - } - }, - calendar : { - sameDay : '[Hodiaŭ je] LT', - nextDay : '[Morgaŭ je] LT', - nextWeek : 'dddd [je] LT', - lastDay : '[Hieraŭ je] LT', - lastWeek : '[pasinta] dddd [je] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'post %s', - past : 'antaŭ %s', - s : 'sekundoj', - ss : '%d sekundoj', - m : 'minuto', - mm : '%d minutoj', - h : 'horo', - hh : '%d horoj', - d : 'tago',//ne 'diurno', ĉar estas uzita por proksimumo - dd : '%d tagoj', - M : 'monato', - MM : '%d monatoj', - y : 'jaro', - yy : '%d jaroj' - }, - dayOfMonthOrdinalParse: /\d{1,2}a/, - ordinal : '%da', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortDot = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'), - monthsShort$1 = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); - - var monthsParse = [/^ene/i, /^feb/i, /^mar/i, /^abr/i, /^may/i, /^jun/i, /^jul/i, /^ago/i, /^sep/i, /^oct/i, /^nov/i, /^dic/i]; - var monthsRegex$1 = /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i; - - hooks.defineLocale('es-do', { - months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortDot; - } else if (/-MMM-/.test(format)) { - return monthsShort$1[m.month()]; - } else { - return monthsShortDot[m.month()]; - } - }, - monthsRegex: monthsRegex$1, - monthsShortRegex: monthsRegex$1, - monthsStrictRegex: /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i, - monthsShortStrictRegex: /^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i, - monthsParse: monthsParse, - longMonthsParse: monthsParse, - shortMonthsParse: monthsParse, - weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), - weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), - weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY h:mm A', - LLLL : 'dddd, D [de] MMMM [de] YYYY h:mm A' - }, - calendar : { - sameDay : function () { - return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextDay : function () { - return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastDay : function () { - return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'en %s', - past : 'hace %s', - s : 'unos segundos', - ss : '%d segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'una hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un año', - yy : '%d años' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortDot$1 = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'), - monthsShort$2 = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); - - hooks.defineLocale('es-us', { - months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortDot$1; - } else if (/-MMM-/.test(format)) { - return monthsShort$2[m.month()]; - } else { - return monthsShortDot$1[m.month()]; - } - }, - monthsParseExact : true, - weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), - weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), - weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'MM/DD/YYYY', - LL : 'MMMM [de] D [de] YYYY', - LLL : 'MMMM [de] D [de] YYYY h:mm A', - LLLL : 'dddd, MMMM [de] D [de] YYYY h:mm A' - }, - calendar : { - sameDay : function () { - return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextDay : function () { - return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastDay : function () { - return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'en %s', - past : 'hace %s', - s : 'unos segundos', - ss : '%d segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'una hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un año', - yy : '%d años' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortDot$2 = 'ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.'.split('_'), - monthsShort$3 = 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'); - - var monthsParse$1 = [/^ene/i, /^feb/i, /^mar/i, /^abr/i, /^may/i, /^jun/i, /^jul/i, /^ago/i, /^sep/i, /^oct/i, /^nov/i, /^dic/i]; - var monthsRegex$2 = /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i; - - hooks.defineLocale('es', { - months : 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortDot$2; - } else if (/-MMM-/.test(format)) { - return monthsShort$3[m.month()]; - } else { - return monthsShortDot$2[m.month()]; - } - }, - monthsRegex : monthsRegex$2, - monthsShortRegex : monthsRegex$2, - monthsStrictRegex : /^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i, - monthsShortStrictRegex : /^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i, - monthsParse : monthsParse$1, - longMonthsParse : monthsParse$1, - shortMonthsParse : monthsParse$1, - weekdays : 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'), - weekdaysShort : 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'), - weekdaysMin : 'do_lu_ma_mi_ju_vi_sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY H:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' - }, - calendar : { - sameDay : function () { - return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextDay : function () { - return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - nextWeek : function () { - return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastDay : function () { - return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - lastWeek : function () { - return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'en %s', - past : 'hace %s', - s : 'unos segundos', - ss : '%d segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'una hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un año', - yy : '%d años' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$3(number, withoutSuffix, key, isFuture) { - var format = { - 's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'], - 'ss': [number + 'sekundi', number + 'sekundit'], - 'm' : ['ühe minuti', 'üks minut'], - 'mm': [number + ' minuti', number + ' minutit'], - 'h' : ['ühe tunni', 'tund aega', 'üks tund'], - 'hh': [number + ' tunni', number + ' tundi'], - 'd' : ['ühe päeva', 'üks päev'], - 'M' : ['kuu aja', 'kuu aega', 'üks kuu'], - 'MM': [number + ' kuu', number + ' kuud'], - 'y' : ['ühe aasta', 'aasta', 'üks aasta'], - 'yy': [number + ' aasta', number + ' aastat'] - }; - if (withoutSuffix) { - return format[key][2] ? format[key][2] : format[key][1]; - } - return isFuture ? format[key][0] : format[key][1]; - } - - hooks.defineLocale('et', { - months : 'jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember'.split('_'), - monthsShort : 'jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets'.split('_'), - weekdays : 'pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev'.split('_'), - weekdaysShort : 'P_E_T_K_N_R_L'.split('_'), - weekdaysMin : 'P_E_T_K_N_R_L'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Täna,] LT', - nextDay : '[Homme,] LT', - nextWeek : '[Järgmine] dddd LT', - lastDay : '[Eile,] LT', - lastWeek : '[Eelmine] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s pärast', - past : '%s tagasi', - s : processRelativeTime$3, - ss : processRelativeTime$3, - m : processRelativeTime$3, - mm : processRelativeTime$3, - h : processRelativeTime$3, - hh : processRelativeTime$3, - d : processRelativeTime$3, - dd : '%d päeva', - M : processRelativeTime$3, - MM : processRelativeTime$3, - y : processRelativeTime$3, - yy : processRelativeTime$3 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('eu', { - months : 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'), - monthsShort : 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'), - monthsParseExact : true, - weekdays : 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'), - weekdaysShort : 'ig._al._ar._az._og._ol._lr.'.split('_'), - weekdaysMin : 'ig_al_ar_az_og_ol_lr'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY[ko] MMMM[ren] D[a]', - LLL : 'YYYY[ko] MMMM[ren] D[a] HH:mm', - LLLL : 'dddd, YYYY[ko] MMMM[ren] D[a] HH:mm', - l : 'YYYY-M-D', - ll : 'YYYY[ko] MMM D[a]', - lll : 'YYYY[ko] MMM D[a] HH:mm', - llll : 'ddd, YYYY[ko] MMM D[a] HH:mm' - }, - calendar : { - sameDay : '[gaur] LT[etan]', - nextDay : '[bihar] LT[etan]', - nextWeek : 'dddd LT[etan]', - lastDay : '[atzo] LT[etan]', - lastWeek : '[aurreko] dddd LT[etan]', - sameElse : 'L' - }, - relativeTime : { - future : '%s barru', - past : 'duela %s', - s : 'segundo batzuk', - ss : '%d segundo', - m : 'minutu bat', - mm : '%d minutu', - h : 'ordu bat', - hh : '%d ordu', - d : 'egun bat', - dd : '%d egun', - M : 'hilabete bat', - MM : '%d hilabete', - y : 'urte bat', - yy : '%d urte' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$5 = { - '1': '۱', - '2': '۲', - '3': '۳', - '4': '۴', - '5': '۵', - '6': '۶', - '7': '۷', - '8': '۸', - '9': '۹', - '0': '۰' - }, numberMap$4 = { - '۱': '1', - '۲': '2', - '۳': '3', - '۴': '4', - '۵': '5', - '۶': '6', - '۷': '7', - '۸': '8', - '۹': '9', - '۰': '0' - }; - - hooks.defineLocale('fa', { - months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), - monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'), - weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), - weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'), - weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - meridiemParse: /قبل از ظهر|بعد از ظهر/, - isPM: function (input) { - return /بعد از ظهر/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'قبل از ظهر'; - } else { - return 'بعد از ظهر'; - } - }, - calendar : { - sameDay : '[امروز ساعت] LT', - nextDay : '[فردا ساعت] LT', - nextWeek : 'dddd [ساعت] LT', - lastDay : '[دیروز ساعت] LT', - lastWeek : 'dddd [پیش] [ساعت] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'در %s', - past : '%s پیش', - s : 'چند ثانیه', - ss : 'ثانیه d%', - m : 'یک دقیقه', - mm : '%d دقیقه', - h : 'یک ساعت', - hh : '%d ساعت', - d : 'یک روز', - dd : '%d روز', - M : 'یک ماه', - MM : '%d ماه', - y : 'یک سال', - yy : '%d سال' - }, - preparse: function (string) { - return string.replace(/[۰-۹]/g, function (match) { - return numberMap$4[match]; - }).replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$5[match]; - }).replace(/,/g, '،'); - }, - dayOfMonthOrdinalParse: /\d{1,2}م/, - ordinal : '%dم', - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '), - numbersFuture = [ - 'nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden', - numbersPast[7], numbersPast[8], numbersPast[9] - ]; - function translate$2(number, withoutSuffix, key, isFuture) { - var result = ''; - switch (key) { - case 's': - return isFuture ? 'muutaman sekunnin' : 'muutama sekunti'; - case 'ss': - return isFuture ? 'sekunnin' : 'sekuntia'; - case 'm': - return isFuture ? 'minuutin' : 'minuutti'; - case 'mm': - result = isFuture ? 'minuutin' : 'minuuttia'; - break; - case 'h': - return isFuture ? 'tunnin' : 'tunti'; - case 'hh': - result = isFuture ? 'tunnin' : 'tuntia'; - break; - case 'd': - return isFuture ? 'päivän' : 'päivä'; - case 'dd': - result = isFuture ? 'päivän' : 'päivää'; - break; - case 'M': - return isFuture ? 'kuukauden' : 'kuukausi'; - case 'MM': - result = isFuture ? 'kuukauden' : 'kuukautta'; - break; - case 'y': - return isFuture ? 'vuoden' : 'vuosi'; - case 'yy': - result = isFuture ? 'vuoden' : 'vuotta'; - break; - } - result = verbalNumber(number, isFuture) + ' ' + result; - return result; - } - function verbalNumber(number, isFuture) { - return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number; - } - - hooks.defineLocale('fi', { - months : 'tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu'.split('_'), - monthsShort : 'tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu'.split('_'), - weekdays : 'sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai'.split('_'), - weekdaysShort : 'su_ma_ti_ke_to_pe_la'.split('_'), - weekdaysMin : 'su_ma_ti_ke_to_pe_la'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD.MM.YYYY', - LL : 'Do MMMM[ta] YYYY', - LLL : 'Do MMMM[ta] YYYY, [klo] HH.mm', - LLLL : 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm', - l : 'D.M.YYYY', - ll : 'Do MMM YYYY', - lll : 'Do MMM YYYY, [klo] HH.mm', - llll : 'ddd, Do MMM YYYY, [klo] HH.mm' - }, - calendar : { - sameDay : '[tänään] [klo] LT', - nextDay : '[huomenna] [klo] LT', - nextWeek : 'dddd [klo] LT', - lastDay : '[eilen] [klo] LT', - lastWeek : '[viime] dddd[na] [klo] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s päästä', - past : '%s sitten', - s : translate$2, - ss : translate$2, - m : translate$2, - mm : translate$2, - h : translate$2, - hh : translate$2, - d : translate$2, - dd : translate$2, - M : translate$2, - MM : translate$2, - y : translate$2, - yy : translate$2 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('fo', { - months : 'januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), - weekdays : 'sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur'.split('_'), - weekdaysShort : 'sun_mán_týs_mik_hós_frí_ley'.split('_'), - weekdaysMin : 'su_má_tý_mi_hó_fr_le'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D. MMMM, YYYY HH:mm' - }, - calendar : { - sameDay : '[Í dag kl.] LT', - nextDay : '[Í morgin kl.] LT', - nextWeek : 'dddd [kl.] LT', - lastDay : '[Í gjár kl.] LT', - lastWeek : '[síðstu] dddd [kl] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'um %s', - past : '%s síðani', - s : 'fá sekund', - ss : '%d sekundir', - m : 'ein minutt', - mm : '%d minuttir', - h : 'ein tími', - hh : '%d tímar', - d : 'ein dagur', - dd : '%d dagar', - M : 'ein mánaði', - MM : '%d mánaðir', - y : 'eitt ár', - yy : '%d ár' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('fr-ca', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'di_lu_ma_me_je_ve_sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Aujourd’hui à] LT', - nextDay : '[Demain à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[Hier à] LT', - lastWeek : 'dddd [dernier à] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - ss : '%d secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - dayOfMonthOrdinalParse: /\d{1,2}(er|e)/, - ordinal : function (number, period) { - switch (period) { - // Words with masculine grammatical gender: mois, trimestre, jour - default: - case 'M': - case 'Q': - case 'D': - case 'DDD': - case 'd': - return number + (number === 1 ? 'er' : 'e'); - - // Words with feminine grammatical gender: semaine - case 'w': - case 'W': - return number + (number === 1 ? 're' : 'e'); - } - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('fr-ch', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'di_lu_ma_me_je_ve_sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Aujourd’hui à] LT', - nextDay : '[Demain à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[Hier à] LT', - lastWeek : 'dddd [dernier à] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - ss : '%d secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - dayOfMonthOrdinalParse: /\d{1,2}(er|e)/, - ordinal : function (number, period) { - switch (period) { - // Words with masculine grammatical gender: mois, trimestre, jour - default: - case 'M': - case 'Q': - case 'D': - case 'DDD': - case 'd': - return number + (number === 1 ? 'er' : 'e'); - - // Words with feminine grammatical gender: semaine - case 'w': - case 'W': - return number + (number === 1 ? 're' : 'e'); - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('fr', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'di_lu_ma_me_je_ve_sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Aujourd’hui à] LT', - nextDay : '[Demain à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[Hier à] LT', - lastWeek : 'dddd [dernier à] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - ss : '%d secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' - }, - dayOfMonthOrdinalParse: /\d{1,2}(er|)/, - ordinal : function (number, period) { - switch (period) { - // TODO: Return 'e' when day of month > 1. Move this case inside - // block for masculine words below. - // See https://github.com/moment/moment/issues/3375 - case 'D': - return number + (number === 1 ? 'er' : ''); - - // Words with masculine grammatical gender: mois, trimestre, jour - default: - case 'M': - case 'Q': - case 'DDD': - case 'd': - return number + (number === 1 ? 'er' : 'e'); - - // Words with feminine grammatical gender: semaine - case 'w': - case 'W': - return number + (number === 1 ? 're' : 'e'); - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortWithDots = 'jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.'.split('_'), - monthsShortWithoutDots = 'jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'); - - hooks.defineLocale('fy', { - months : 'jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortWithDots; - } else if (/-MMM-/.test(format)) { - return monthsShortWithoutDots[m.month()]; - } else { - return monthsShortWithDots[m.month()]; - } - }, - monthsParseExact : true, - weekdays : 'snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon'.split('_'), - weekdaysShort : 'si._mo._ti._wo._to._fr._so.'.split('_'), - weekdaysMin : 'Si_Mo_Ti_Wo_To_Fr_So'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[hjoed om] LT', - nextDay: '[moarn om] LT', - nextWeek: 'dddd [om] LT', - lastDay: '[juster om] LT', - lastWeek: '[ôfrûne] dddd [om] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'oer %s', - past : '%s lyn', - s : 'in pear sekonden', - ss : '%d sekonden', - m : 'ien minút', - mm : '%d minuten', - h : 'ien oere', - hh : '%d oeren', - d : 'ien dei', - dd : '%d dagen', - M : 'ien moanne', - MM : '%d moannen', - y : 'ien jier', - yy : '%d jierren' - }, - dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var months$5 = [ - 'Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean', 'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal', 'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd' - ]; - - var monthsShort$4 = ['Faoi', 'Gear', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch', 'Lùn', 'Sult', 'Dàmh', 'Samh', 'Dùbh']; - - var weekdays$1 = ['Didòmhnaich', 'Diluain', 'Dimàirt', 'Diciadain', 'Diardaoin', 'Dihaoine', 'Disathairne']; - - var weekdaysShort = ['Did', 'Dil', 'Dim', 'Dic', 'Dia', 'Dih', 'Dis']; - - var weekdaysMin = ['Dò', 'Lu', 'Mà', 'Ci', 'Ar', 'Ha', 'Sa']; - - hooks.defineLocale('gd', { - months : months$5, - monthsShort : monthsShort$4, - monthsParseExact : true, - weekdays : weekdays$1, - weekdaysShort : weekdaysShort, - weekdaysMin : weekdaysMin, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[An-diugh aig] LT', - nextDay : '[A-màireach aig] LT', - nextWeek : 'dddd [aig] LT', - lastDay : '[An-dè aig] LT', - lastWeek : 'dddd [seo chaidh] [aig] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ann an %s', - past : 'bho chionn %s', - s : 'beagan diogan', - ss : '%d diogan', - m : 'mionaid', - mm : '%d mionaidean', - h : 'uair', - hh : '%d uairean', - d : 'latha', - dd : '%d latha', - M : 'mìos', - MM : '%d mìosan', - y : 'bliadhna', - yy : '%d bliadhna' - }, - dayOfMonthOrdinalParse : /\d{1,2}(d|na|mh)/, - ordinal : function (number) { - var output = number === 1 ? 'd' : number % 10 === 2 ? 'na' : 'mh'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('gl', { - months : 'xaneiro_febreiro_marzo_abril_maio_xuño_xullo_agosto_setembro_outubro_novembro_decembro'.split('_'), - monthsShort : 'xan._feb._mar._abr._mai._xuñ._xul._ago._set._out._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'domingo_luns_martes_mércores_xoves_venres_sábado'.split('_'), - weekdaysShort : 'dom._lun._mar._mér._xov._ven._sáb.'.split('_'), - weekdaysMin : 'do_lu_ma_mé_xo_ve_sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY H:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY H:mm' - }, - calendar : { - sameDay : function () { - return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; - }, - nextDay : function () { - return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT'; - }, - nextWeek : function () { - return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; - }, - lastDay : function () { - return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT'; - }, - lastWeek : function () { - return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT'; - }, - sameElse : 'L' - }, - relativeTime : { - future : function (str) { - if (str.indexOf('un') === 0) { - return 'n' + str; - } - return 'en ' + str; - }, - past : 'hai %s', - s : 'uns segundos', - ss : '%d segundos', - m : 'un minuto', - mm : '%d minutos', - h : 'unha hora', - hh : '%d horas', - d : 'un día', - dd : '%d días', - M : 'un mes', - MM : '%d meses', - y : 'un ano', - yy : '%d anos' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$4(number, withoutSuffix, key, isFuture) { - var format = { - 's': ['thodde secondanim', 'thodde second'], - 'ss': [number + ' secondanim', number + ' second'], - 'm': ['eka mintan', 'ek minute'], - 'mm': [number + ' mintanim', number + ' mintam'], - 'h': ['eka horan', 'ek hor'], - 'hh': [number + ' horanim', number + ' horam'], - 'd': ['eka disan', 'ek dis'], - 'dd': [number + ' disanim', number + ' dis'], - 'M': ['eka mhoinean', 'ek mhoino'], - 'MM': [number + ' mhoineanim', number + ' mhoine'], - 'y': ['eka vorsan', 'ek voros'], - 'yy': [number + ' vorsanim', number + ' vorsam'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; - } - - hooks.defineLocale('gom-latn', { - months : 'Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr'.split('_'), - monthsShort : 'Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays : 'Aitar_Somar_Mongllar_Budvar_Brestar_Sukrar_Son\'var'.split('_'), - weekdaysShort : 'Ait._Som._Mon._Bud._Bre._Suk._Son.'.split('_'), - weekdaysMin : 'Ai_Sm_Mo_Bu_Br_Su_Sn'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'A h:mm [vazta]', - LTS : 'A h:mm:ss [vazta]', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY A h:mm [vazta]', - LLLL : 'dddd, MMMM[achea] Do, YYYY, A h:mm [vazta]', - llll: 'ddd, D MMM YYYY, A h:mm [vazta]' - }, - calendar : { - sameDay: '[Aiz] LT', - nextDay: '[Faleam] LT', - nextWeek: '[Ieta to] dddd[,] LT', - lastDay: '[Kal] LT', - lastWeek: '[Fatlo] dddd[,] LT', - sameElse: 'L' - }, - relativeTime : { - future : '%s', - past : '%s adim', - s : processRelativeTime$4, - ss : processRelativeTime$4, - m : processRelativeTime$4, - mm : processRelativeTime$4, - h : processRelativeTime$4, - hh : processRelativeTime$4, - d : processRelativeTime$4, - dd : processRelativeTime$4, - M : processRelativeTime$4, - MM : processRelativeTime$4, - y : processRelativeTime$4, - yy : processRelativeTime$4 - }, - dayOfMonthOrdinalParse : /\d{1,2}(er)/, - ordinal : function (number, period) { - switch (period) { - // the ordinal 'er' only applies to day of the month - case 'D': - return number + 'er'; - default: - case 'M': - case 'Q': - case 'DDD': - case 'd': - case 'w': - case 'W': - return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - }, - meridiemParse: /rati|sokalli|donparam|sanje/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'rati') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'sokalli') { - return hour; - } else if (meridiem === 'donparam') { - return hour > 12 ? hour : hour + 12; - } else if (meridiem === 'sanje') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'rati'; - } else if (hour < 12) { - return 'sokalli'; - } else if (hour < 16) { - return 'donparam'; - } else if (hour < 20) { - return 'sanje'; - } else { - return 'rati'; - } - } - }); - - //! moment.js locale configuration - - var symbolMap$6 = { - '1': '૧', - '2': '૨', - '3': '૩', - '4': '૪', - '5': '૫', - '6': '૬', - '7': '૭', - '8': '૮', - '9': '૯', - '0': '૦' - }, - numberMap$5 = { - '૧': '1', - '૨': '2', - '૩': '3', - '૪': '4', - '૫': '5', - '૬': '6', - '૭': '7', - '૮': '8', - '૯': '9', - '૦': '0' - }; - - hooks.defineLocale('gu', { - months: 'જાન્યુઆરી_ફેબ્રુઆરી_માર્ચ_એપ્રિલ_મે_જૂન_જુલાઈ_ઑગસ્ટ_સપ્ટેમ્બર_ઑક્ટ્બર_નવેમ્બર_ડિસેમ્બર'.split('_'), - monthsShort: 'જાન્યુ._ફેબ્રુ._માર્ચ_એપ્રિ._મે_જૂન_જુલા._ઑગ._સપ્ટે._ઑક્ટ્._નવે._ડિસે.'.split('_'), - monthsParseExact: true, - weekdays: 'રવિવાર_સોમવાર_મંગળવાર_બુધ્વાર_ગુરુવાર_શુક્રવાર_શનિવાર'.split('_'), - weekdaysShort: 'રવિ_સોમ_મંગળ_બુધ્_ગુરુ_શુક્ર_શનિ'.split('_'), - weekdaysMin: 'ર_સો_મં_બુ_ગુ_શુ_શ'.split('_'), - longDateFormat: { - LT: 'A h:mm વાગ્યે', - LTS: 'A h:mm:ss વાગ્યે', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY, A h:mm વાગ્યે', - LLLL: 'dddd, D MMMM YYYY, A h:mm વાગ્યે' - }, - calendar: { - sameDay: '[આજ] LT', - nextDay: '[કાલે] LT', - nextWeek: 'dddd, LT', - lastDay: '[ગઇકાલે] LT', - lastWeek: '[પાછલા] dddd, LT', - sameElse: 'L' - }, - relativeTime: { - future: '%s મા', - past: '%s પેહલા', - s: 'અમુક પળો', - ss: '%d સેકંડ', - m: 'એક મિનિટ', - mm: '%d મિનિટ', - h: 'એક કલાક', - hh: '%d કલાક', - d: 'એક દિવસ', - dd: '%d દિવસ', - M: 'એક મહિનો', - MM: '%d મહિનો', - y: 'એક વર્ષ', - yy: '%d વર્ષ' - }, - preparse: function (string) { - return string.replace(/[૧૨૩૪૫૬૭૮૯૦]/g, function (match) { - return numberMap$5[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$6[match]; - }); - }, - // Gujarati notation for meridiems are quite fuzzy in practice. While there exists - // a rigid notion of a 'Pahar' it is not used as rigidly in modern Gujarati. - meridiemParse: /રાત|બપોર|સવાર|સાંજ/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'રાત') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'સવાર') { - return hour; - } else if (meridiem === 'બપોર') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'સાંજ') { - return hour + 12; - } - }, - meridiem: function (hour, minute, isLower) { - if (hour < 4) { - return 'રાત'; - } else if (hour < 10) { - return 'સવાર'; - } else if (hour < 17) { - return 'બપોર'; - } else if (hour < 20) { - return 'સાંજ'; - } else { - return 'રાત'; - } - }, - week: { - dow: 0, // Sunday is the first day of the week. - doy: 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('he', { - months : 'ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר'.split('_'), - monthsShort : 'ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳'.split('_'), - weekdays : 'ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת'.split('_'), - weekdaysShort : 'א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳'.split('_'), - weekdaysMin : 'א_ב_ג_ד_ה_ו_ש'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [ב]MMMM YYYY', - LLL : 'D [ב]MMMM YYYY HH:mm', - LLLL : 'dddd, D [ב]MMMM YYYY HH:mm', - l : 'D/M/YYYY', - ll : 'D MMM YYYY', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd, D MMM YYYY HH:mm' - }, - calendar : { - sameDay : '[היום ב־]LT', - nextDay : '[מחר ב־]LT', - nextWeek : 'dddd [בשעה] LT', - lastDay : '[אתמול ב־]LT', - lastWeek : '[ביום] dddd [האחרון בשעה] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'בעוד %s', - past : 'לפני %s', - s : 'מספר שניות', - ss : '%d שניות', - m : 'דקה', - mm : '%d דקות', - h : 'שעה', - hh : function (number) { - if (number === 2) { - return 'שעתיים'; - } - return number + ' שעות'; - }, - d : 'יום', - dd : function (number) { - if (number === 2) { - return 'יומיים'; - } - return number + ' ימים'; - }, - M : 'חודש', - MM : function (number) { - if (number === 2) { - return 'חודשיים'; - } - return number + ' חודשים'; - }, - y : 'שנה', - yy : function (number) { - if (number === 2) { - return 'שנתיים'; - } else if (number % 10 === 0 && number !== 10) { - return number + ' שנה'; - } - return number + ' שנים'; - } - }, - meridiemParse: /אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i, - isPM : function (input) { - return /^(אחה"צ|אחרי הצהריים|בערב)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 5) { - return 'לפנות בוקר'; - } else if (hour < 10) { - return 'בבוקר'; - } else if (hour < 12) { - return isLower ? 'לפנה"צ' : 'לפני הצהריים'; - } else if (hour < 18) { - return isLower ? 'אחה"צ' : 'אחרי הצהריים'; - } else { - return 'בערב'; - } - } - }); - - //! moment.js locale configuration - - var symbolMap$7 = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' - }, - numberMap$6 = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; - - hooks.defineLocale('hi', { - months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split('_'), - monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split('_'), - monthsParseExact: true, - weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), - weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split('_'), - weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), - longDateFormat : { - LT : 'A h:mm बजे', - LTS : 'A h:mm:ss बजे', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm बजे', - LLLL : 'dddd, D MMMM YYYY, A h:mm बजे' - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[कल] LT', - nextWeek : 'dddd, LT', - lastDay : '[कल] LT', - lastWeek : '[पिछले] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s में', - past : '%s पहले', - s : 'कुछ ही क्षण', - ss : '%d सेकंड', - m : 'एक मिनट', - mm : '%d मिनट', - h : 'एक घंटा', - hh : '%d घंटे', - d : 'एक दिन', - dd : '%d दिन', - M : 'एक महीने', - MM : '%d महीने', - y : 'एक वर्ष', - yy : '%d वर्ष' - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap$6[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$7[match]; - }); - }, - // Hindi notation for meridiems are quite fuzzy in practice. While there exists - // a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi. - meridiemParse: /रात|सुबह|दोपहर|शाम/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'रात') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'सुबह') { - return hour; - } else if (meridiem === 'दोपहर') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'शाम') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'रात'; - } else if (hour < 10) { - return 'सुबह'; - } else if (hour < 17) { - return 'दोपहर'; - } else if (hour < 20) { - return 'शाम'; - } else { - return 'रात'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function translate$3(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { - case 'ss': - if (number === 1) { - result += 'sekunda'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'sekunde'; - } else { - result += 'sekundi'; - } - return result; - case 'm': - return withoutSuffix ? 'jedna minuta' : 'jedne minute'; - case 'mm': - if (number === 1) { - result += 'minuta'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'minute'; - } else { - result += 'minuta'; - } - return result; - case 'h': - return withoutSuffix ? 'jedan sat' : 'jednog sata'; - case 'hh': - if (number === 1) { - result += 'sat'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'sata'; - } else { - result += 'sati'; - } - return result; - case 'dd': - if (number === 1) { - result += 'dan'; - } else { - result += 'dana'; - } - return result; - case 'MM': - if (number === 1) { - result += 'mjesec'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'mjeseca'; - } else { - result += 'mjeseci'; - } - return result; - case 'yy': - if (number === 1) { - result += 'godina'; - } else if (number === 2 || number === 3 || number === 4) { - result += 'godine'; - } else { - result += 'godina'; - } - return result; - } - } - - hooks.defineLocale('hr', { - months : { - format: 'siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca'.split('_'), - standalone: 'siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac'.split('_') - }, - monthsShort : 'sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.'.split('_'), - monthsParseExact: true, - weekdays : 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort : 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin : 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danas u] LT', - nextDay : '[sutra u] LT', - nextWeek : function () { - switch (this.day()) { - case 0: - return '[u] [nedjelju] [u] LT'; - case 3: - return '[u] [srijedu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[u] dddd [u] LT'; - } - }, - lastDay : '[jučer u] LT', - lastWeek : function () { - switch (this.day()) { - case 0: - case 3: - return '[prošlu] dddd [u] LT'; - case 6: - return '[prošle] [subote] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[prošli] dddd [u] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'par sekundi', - ss : translate$3, - m : translate$3, - mm : translate$3, - h : translate$3, - hh : translate$3, - d : 'dan', - dd : translate$3, - M : 'mjesec', - MM : translate$3, - y : 'godinu', - yy : translate$3 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' '); - function translate$4(number, withoutSuffix, key, isFuture) { - var num = number; - switch (key) { - case 's': - return (isFuture || withoutSuffix) ? 'néhány másodperc' : 'néhány másodperce'; - case 'ss': - return num + (isFuture || withoutSuffix) ? ' másodperc' : ' másodperce'; - case 'm': - return 'egy' + (isFuture || withoutSuffix ? ' perc' : ' perce'); - case 'mm': - return num + (isFuture || withoutSuffix ? ' perc' : ' perce'); - case 'h': - return 'egy' + (isFuture || withoutSuffix ? ' óra' : ' órája'); - case 'hh': - return num + (isFuture || withoutSuffix ? ' óra' : ' órája'); - case 'd': - return 'egy' + (isFuture || withoutSuffix ? ' nap' : ' napja'); - case 'dd': - return num + (isFuture || withoutSuffix ? ' nap' : ' napja'); - case 'M': - return 'egy' + (isFuture || withoutSuffix ? ' hónap' : ' hónapja'); - case 'MM': - return num + (isFuture || withoutSuffix ? ' hónap' : ' hónapja'); - case 'y': - return 'egy' + (isFuture || withoutSuffix ? ' év' : ' éve'); - case 'yy': - return num + (isFuture || withoutSuffix ? ' év' : ' éve'); - } - return ''; - } - function week(isFuture) { - return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]'; - } - - hooks.defineLocale('hu', { - months : 'január_február_március_április_május_június_július_augusztus_szeptember_október_november_december'.split('_'), - monthsShort : 'jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec'.split('_'), - weekdays : 'vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat'.split('_'), - weekdaysShort : 'vas_hét_kedd_sze_csüt_pén_szo'.split('_'), - weekdaysMin : 'v_h_k_sze_cs_p_szo'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'YYYY.MM.DD.', - LL : 'YYYY. MMMM D.', - LLL : 'YYYY. MMMM D. H:mm', - LLLL : 'YYYY. MMMM D., dddd H:mm' - }, - meridiemParse: /de|du/i, - isPM: function (input) { - return input.charAt(1).toLowerCase() === 'u'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower === true ? 'de' : 'DE'; - } else { - return isLower === true ? 'du' : 'DU'; - } - }, - calendar : { - sameDay : '[ma] LT[-kor]', - nextDay : '[holnap] LT[-kor]', - nextWeek : function () { - return week.call(this, true); - }, - lastDay : '[tegnap] LT[-kor]', - lastWeek : function () { - return week.call(this, false); - }, - sameElse : 'L' - }, - relativeTime : { - future : '%s múlva', - past : '%s', - s : translate$4, - ss : translate$4, - m : translate$4, - mm : translate$4, - h : translate$4, - hh : translate$4, - d : translate$4, - dd : translate$4, - M : translate$4, - MM : translate$4, - y : translate$4, - yy : translate$4 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('hy-am', { - months : { - format: 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_'), - standalone: 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_') - }, - monthsShort : 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_'), - weekdays : 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_'), - weekdaysShort : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), - weekdaysMin : 'կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY թ.', - LLL : 'D MMMM YYYY թ., HH:mm', - LLLL : 'dddd, D MMMM YYYY թ., HH:mm' - }, - calendar : { - sameDay: '[այսօր] LT', - nextDay: '[վաղը] LT', - lastDay: '[երեկ] LT', - nextWeek: function () { - return 'dddd [օրը ժամը] LT'; - }, - lastWeek: function () { - return '[անցած] dddd [օրը ժամը] LT'; - }, - sameElse: 'L' - }, - relativeTime : { - future : '%s հետո', - past : '%s առաջ', - s : 'մի քանի վայրկյան', - ss : '%d վայրկյան', - m : 'րոպե', - mm : '%d րոպե', - h : 'ժամ', - hh : '%d ժամ', - d : 'օր', - dd : '%d օր', - M : 'ամիս', - MM : '%d ամիս', - y : 'տարի', - yy : '%d տարի' - }, - meridiemParse: /գիշերվա|առավոտվա|ցերեկվա|երեկոյան/, - isPM: function (input) { - return /^(ցերեկվա|երեկոյան)$/.test(input); - }, - meridiem : function (hour) { - if (hour < 4) { - return 'գիշերվա'; - } else if (hour < 12) { - return 'առավոտվա'; - } else if (hour < 17) { - return 'ցերեկվա'; - } else { - return 'երեկոյան'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}|\d{1,2}-(ին|րդ)/, - ordinal: function (number, period) { - switch (period) { - case 'DDD': - case 'w': - case 'W': - case 'DDDo': - if (number === 1) { - return number + '-ին'; - } - return number + '-րդ'; - default: - return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('id', { - months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu'.split('_'), - weekdaysShort : 'Min_Sen_Sel_Rab_Kam_Jum_Sab'.split('_'), - weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|siang|sore|malam/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'siang') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'sore' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'siang'; - } else if (hours < 19) { - return 'sore'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Besok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kemarin pukul] LT', - lastWeek : 'dddd [lalu pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lalu', - s : 'beberapa detik', - ss : '%d detik', - m : 'semenit', - mm : '%d menit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function plural$2(n) { - if (n % 100 === 11) { - return true; - } else if (n % 10 === 1) { - return false; - } - return true; - } - function translate$5(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { - case 's': - return withoutSuffix || isFuture ? 'nokkrar sekúndur' : 'nokkrum sekúndum'; - case 'ss': - if (plural$2(number)) { - return result + (withoutSuffix || isFuture ? 'sekúndur' : 'sekúndum'); - } - return result + 'sekúnda'; - case 'm': - return withoutSuffix ? 'mínúta' : 'mínútu'; - case 'mm': - if (plural$2(number)) { - return result + (withoutSuffix || isFuture ? 'mínútur' : 'mínútum'); - } else if (withoutSuffix) { - return result + 'mínúta'; - } - return result + 'mínútu'; - case 'hh': - if (plural$2(number)) { - return result + (withoutSuffix || isFuture ? 'klukkustundir' : 'klukkustundum'); - } - return result + 'klukkustund'; - case 'd': - if (withoutSuffix) { - return 'dagur'; - } - return isFuture ? 'dag' : 'degi'; - case 'dd': - if (plural$2(number)) { - if (withoutSuffix) { - return result + 'dagar'; - } - return result + (isFuture ? 'daga' : 'dögum'); - } else if (withoutSuffix) { - return result + 'dagur'; - } - return result + (isFuture ? 'dag' : 'degi'); - case 'M': - if (withoutSuffix) { - return 'mánuður'; - } - return isFuture ? 'mánuð' : 'mánuði'; - case 'MM': - if (plural$2(number)) { - if (withoutSuffix) { - return result + 'mánuðir'; - } - return result + (isFuture ? 'mánuði' : 'mánuðum'); - } else if (withoutSuffix) { - return result + 'mánuður'; - } - return result + (isFuture ? 'mánuð' : 'mánuði'); - case 'y': - return withoutSuffix || isFuture ? 'ár' : 'ári'; - case 'yy': - if (plural$2(number)) { - return result + (withoutSuffix || isFuture ? 'ár' : 'árum'); - } - return result + (withoutSuffix || isFuture ? 'ár' : 'ári'); - } - } - - hooks.defineLocale('is', { - months : 'janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des'.split('_'), - weekdays : 'sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur'.split('_'), - weekdaysShort : 'sun_mán_þri_mið_fim_fös_lau'.split('_'), - weekdaysMin : 'Su_Má_Þr_Mi_Fi_Fö_La'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] H:mm', - LLLL : 'dddd, D. MMMM YYYY [kl.] H:mm' - }, - calendar : { - sameDay : '[í dag kl.] LT', - nextDay : '[á morgun kl.] LT', - nextWeek : 'dddd [kl.] LT', - lastDay : '[í gær kl.] LT', - lastWeek : '[síðasta] dddd [kl.] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'eftir %s', - past : 'fyrir %s síðan', - s : translate$5, - ss : translate$5, - m : translate$5, - mm : translate$5, - h : 'klukkustund', - hh : translate$5, - d : translate$5, - dd : translate$5, - M : translate$5, - MM : translate$5, - y : translate$5, - yy : translate$5 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('it', { - months : 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'), - monthsShort : 'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'), - weekdays : 'domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato'.split('_'), - weekdaysShort : 'dom_lun_mar_mer_gio_ven_sab'.split('_'), - weekdaysMin : 'do_lu_ma_me_gi_ve_sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Oggi alle] LT', - nextDay: '[Domani alle] LT', - nextWeek: 'dddd [alle] LT', - lastDay: '[Ieri alle] LT', - lastWeek: function () { - switch (this.day()) { - case 0: - return '[la scorsa] dddd [alle] LT'; - default: - return '[lo scorso] dddd [alle] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : function (s) { - return ((/^[0-9].+$/).test(s) ? 'tra' : 'in') + ' ' + s; - }, - past : '%s fa', - s : 'alcuni secondi', - ss : '%d secondi', - m : 'un minuto', - mm : '%d minuti', - h : 'un\'ora', - hh : '%d ore', - d : 'un giorno', - dd : '%d giorni', - M : 'un mese', - MM : '%d mesi', - y : 'un anno', - yy : '%d anni' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal: '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ja', { - months : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日'.split('_'), - weekdaysShort : '日_月_火_水_木_金_土'.split('_'), - weekdaysMin : '日_月_火_水_木_金_土'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY年M月D日', - LLL : 'YYYY年M月D日 HH:mm', - LLLL : 'YYYY年M月D日 dddd HH:mm', - l : 'YYYY/MM/DD', - ll : 'YYYY年M月D日', - lll : 'YYYY年M月D日 HH:mm', - llll : 'YYYY年M月D日(ddd) HH:mm' - }, - meridiemParse: /午前|午後/i, - isPM : function (input) { - return input === '午後'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return '午前'; - } else { - return '午後'; - } - }, - calendar : { - sameDay : '[今日] LT', - nextDay : '[明日] LT', - nextWeek : function (now) { - if (now.week() < this.week()) { - return '[来週]dddd LT'; - } else { - return 'dddd LT'; - } - }, - lastDay : '[昨日] LT', - lastWeek : function (now) { - if (this.week() < now.week()) { - return '[先週]dddd LT'; - } else { - return 'dddd LT'; - } - }, - sameElse : 'L' - }, - dayOfMonthOrdinalParse : /\d{1,2}日/, - ordinal : function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'DDD': - return number + '日'; - default: - return number; - } - }, - relativeTime : { - future : '%s後', - past : '%s前', - s : '数秒', - ss : '%d秒', - m : '1分', - mm : '%d分', - h : '1時間', - hh : '%d時間', - d : '1日', - dd : '%d日', - M : '1ヶ月', - MM : '%dヶ月', - y : '1年', - yy : '%d年' - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('jv', { - months : 'Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember'.split('_'), - monthsShort : 'Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des'.split('_'), - weekdays : 'Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu'.split('_'), - weekdaysShort : 'Min_Sen_Sel_Reb_Kem_Jem_Sep'.split('_'), - weekdaysMin : 'Mg_Sn_Sl_Rb_Km_Jm_Sp'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /enjing|siyang|sonten|ndalu/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'enjing') { - return hour; - } else if (meridiem === 'siyang') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'sonten' || meridiem === 'ndalu') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'enjing'; - } else if (hours < 15) { - return 'siyang'; - } else if (hours < 19) { - return 'sonten'; - } else { - return 'ndalu'; - } - }, - calendar : { - sameDay : '[Dinten puniko pukul] LT', - nextDay : '[Mbenjang pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kala wingi pukul] LT', - lastWeek : 'dddd [kepengker pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'wonten ing %s', - past : '%s ingkang kepengker', - s : 'sawetawis detik', - ss : '%d detik', - m : 'setunggal menit', - mm : '%d menit', - h : 'setunggal jam', - hh : '%d jam', - d : 'sedinten', - dd : '%d dinten', - M : 'sewulan', - MM : '%d wulan', - y : 'setaun', - yy : '%d taun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ka', { - months : { - standalone: 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'), - format: 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_') - }, - monthsShort : 'იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ'.split('_'), - weekdays : { - standalone: 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'), - format: 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_'), - isFormat: /(წინა|შემდეგ)/ - }, - weekdaysShort : 'კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ'.split('_'), - weekdaysMin : 'კვ_ორ_სა_ოთ_ხუ_პა_შა'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[დღეს] LT[-ზე]', - nextDay : '[ხვალ] LT[-ზე]', - lastDay : '[გუშინ] LT[-ზე]', - nextWeek : '[შემდეგ] dddd LT[-ზე]', - lastWeek : '[წინა] dddd LT-ზე', - sameElse : 'L' - }, - relativeTime : { - future : function (s) { - return (/(წამი|წუთი|საათი|წელი)/).test(s) ? - s.replace(/ი$/, 'ში') : - s + 'ში'; - }, - past : function (s) { - if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) { - return s.replace(/(ი|ე)$/, 'ის წინ'); - } - if ((/წელი/).test(s)) { - return s.replace(/წელი$/, 'წლის წინ'); - } - }, - s : 'რამდენიმე წამი', - ss : '%d წამი', - m : 'წუთი', - mm : '%d წუთი', - h : 'საათი', - hh : '%d საათი', - d : 'დღე', - dd : '%d დღე', - M : 'თვე', - MM : '%d თვე', - y : 'წელი', - yy : '%d წელი' - }, - dayOfMonthOrdinalParse: /0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/, - ordinal : function (number) { - if (number === 0) { - return number; - } - if (number === 1) { - return number + '-ლი'; - } - if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) { - return 'მე-' + number; - } - return number + '-ე'; - }, - week : { - dow : 1, - doy : 7 - } - }); - - //! moment.js locale configuration - - var suffixes$1 = { - 0: '-ші', - 1: '-ші', - 2: '-ші', - 3: '-ші', - 4: '-ші', - 5: '-ші', - 6: '-шы', - 7: '-ші', - 8: '-ші', - 9: '-шы', - 10: '-шы', - 20: '-шы', - 30: '-шы', - 40: '-шы', - 50: '-ші', - 60: '-шы', - 70: '-ші', - 80: '-ші', - 90: '-шы', - 100: '-ші' - }; - - hooks.defineLocale('kk', { - months : 'қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан'.split('_'), - monthsShort : 'қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел'.split('_'), - weekdays : 'жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі'.split('_'), - weekdaysShort : 'жек_дүй_сей_сәр_бей_жұм_сен'.split('_'), - weekdaysMin : 'жк_дй_сй_ср_бй_жм_сн'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Бүгін сағат] LT', - nextDay : '[Ертең сағат] LT', - nextWeek : 'dddd [сағат] LT', - lastDay : '[Кеше сағат] LT', - lastWeek : '[Өткен аптаның] dddd [сағат] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ішінде', - past : '%s бұрын', - s : 'бірнеше секунд', - ss : '%d секунд', - m : 'бір минут', - mm : '%d минут', - h : 'бір сағат', - hh : '%d сағат', - d : 'бір күн', - dd : '%d күн', - M : 'бір ай', - MM : '%d ай', - y : 'бір жыл', - yy : '%d жыл' - }, - dayOfMonthOrdinalParse: /\d{1,2}-(ші|шы)/, - ordinal : function (number) { - var a = number % 10, - b = number >= 100 ? 100 : null; - return number + (suffixes$1[number] || suffixes$1[a] || suffixes$1[b]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$8 = { - '1': '១', - '2': '២', - '3': '៣', - '4': '៤', - '5': '៥', - '6': '៦', - '7': '៧', - '8': '៨', - '9': '៩', - '0': '០' - }, numberMap$7 = { - '១': '1', - '២': '2', - '៣': '3', - '៤': '4', - '៥': '5', - '៦': '6', - '៧': '7', - '៨': '8', - '៩': '9', - '០': '0' - }; - - hooks.defineLocale('km', { - months: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split( - '_' - ), - monthsShort: 'មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ'.split( - '_' - ), - weekdays: 'អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍'.split('_'), - weekdaysShort: 'អា_ច_អ_ព_ព្រ_សុ_ស'.split('_'), - weekdaysMin: 'អា_ច_អ_ព_ព្រ_សុ_ស'.split('_'), - weekdaysParseExact: true, - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd, D MMMM YYYY HH:mm' - }, - meridiemParse: /ព្រឹក|ល្ងាច/, - isPM: function (input) { - return input === 'ល្ងាច'; - }, - meridiem: function (hour, minute, isLower) { - if (hour < 12) { - return 'ព្រឹក'; - } else { - return 'ល្ងាច'; - } - }, - calendar: { - sameDay: '[ថ្ងៃនេះ ម៉ោង] LT', - nextDay: '[ស្អែក ម៉ោង] LT', - nextWeek: 'dddd [ម៉ោង] LT', - lastDay: '[ម្សិលមិញ ម៉ោង] LT', - lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT', - sameElse: 'L' - }, - relativeTime: { - future: '%sទៀត', - past: '%sមុន', - s: 'ប៉ុន្មានវិនាទី', - ss: '%d វិនាទី', - m: 'មួយនាទី', - mm: '%d នាទី', - h: 'មួយម៉ោង', - hh: '%d ម៉ោង', - d: 'មួយថ្ងៃ', - dd: '%d ថ្ងៃ', - M: 'មួយខែ', - MM: '%d ខែ', - y: 'មួយឆ្នាំ', - yy: '%d ឆ្នាំ' - }, - dayOfMonthOrdinalParse : /ទី\d{1,2}/, - ordinal : 'ទី%d', - preparse: function (string) { - return string.replace(/[១២៣៤៥៦៧៨៩០]/g, function (match) { - return numberMap$7[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$8[match]; - }); - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$9 = { - '1': '೧', - '2': '೨', - '3': '೩', - '4': '೪', - '5': '೫', - '6': '೬', - '7': '೭', - '8': '೮', - '9': '೯', - '0': '೦' - }, - numberMap$8 = { - '೧': '1', - '೨': '2', - '೩': '3', - '೪': '4', - '೫': '5', - '೬': '6', - '೭': '7', - '೮': '8', - '೯': '9', - '೦': '0' - }; - - hooks.defineLocale('kn', { - months : 'ಜನವರಿ_ಫೆಬ್ರವರಿ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬರ್_ಅಕ್ಟೋಬರ್_ನವೆಂಬರ್_ಡಿಸೆಂಬರ್'.split('_'), - monthsShort : 'ಜನ_ಫೆಬ್ರ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂ_ಅಕ್ಟೋ_ನವೆಂ_ಡಿಸೆಂ'.split('_'), - monthsParseExact: true, - weekdays : 'ಭಾನುವಾರ_ಸೋಮವಾರ_ಮಂಗಳವಾರ_ಬುಧವಾರ_ಗುರುವಾರ_ಶುಕ್ರವಾರ_ಶನಿವಾರ'.split('_'), - weekdaysShort : 'ಭಾನು_ಸೋಮ_ಮಂಗಳ_ಬುಧ_ಗುರು_ಶುಕ್ರ_ಶನಿ'.split('_'), - weekdaysMin : 'ಭಾ_ಸೋ_ಮಂ_ಬು_ಗು_ಶು_ಶ'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm', - LLLL : 'dddd, D MMMM YYYY, A h:mm' - }, - calendar : { - sameDay : '[ಇಂದು] LT', - nextDay : '[ನಾಳೆ] LT', - nextWeek : 'dddd, LT', - lastDay : '[ನಿನ್ನೆ] LT', - lastWeek : '[ಕೊನೆಯ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ನಂತರ', - past : '%s ಹಿಂದೆ', - s : 'ಕೆಲವು ಕ್ಷಣಗಳು', - ss : '%d ಸೆಕೆಂಡುಗಳು', - m : 'ಒಂದು ನಿಮಿಷ', - mm : '%d ನಿಮಿಷ', - h : 'ಒಂದು ಗಂಟೆ', - hh : '%d ಗಂಟೆ', - d : 'ಒಂದು ದಿನ', - dd : '%d ದಿನ', - M : 'ಒಂದು ತಿಂಗಳು', - MM : '%d ತಿಂಗಳು', - y : 'ಒಂದು ವರ್ಷ', - yy : '%d ವರ್ಷ' - }, - preparse: function (string) { - return string.replace(/[೧೨೩೪೫೬೭೮೯೦]/g, function (match) { - return numberMap$8[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$9[match]; - }); - }, - meridiemParse: /ರಾತ್ರಿ|ಬೆಳಿಗ್ಗೆ|ಮಧ್ಯಾಹ್ನ|ಸಂಜೆ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'ರಾತ್ರಿ') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'ಬೆಳಿಗ್ಗೆ') { - return hour; - } else if (meridiem === 'ಮಧ್ಯಾಹ್ನ') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'ಸಂಜೆ') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ರಾತ್ರಿ'; - } else if (hour < 10) { - return 'ಬೆಳಿಗ್ಗೆ'; - } else if (hour < 17) { - return 'ಮಧ್ಯಾಹ್ನ'; - } else if (hour < 20) { - return 'ಸಂಜೆ'; - } else { - return 'ರಾತ್ರಿ'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}(ನೇ)/, - ordinal : function (number) { - return number + 'ನೇ'; - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ko', { - months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), - monthsShort : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), - weekdays : '일요일_월요일_화요일_수요일_목요일_금요일_토요일'.split('_'), - weekdaysShort : '일_월_화_수_목_금_토'.split('_'), - weekdaysMin : '일_월_화_수_목_금_토'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'YYYY.MM.DD.', - LL : 'YYYY년 MMMM D일', - LLL : 'YYYY년 MMMM D일 A h:mm', - LLLL : 'YYYY년 MMMM D일 dddd A h:mm', - l : 'YYYY.MM.DD.', - ll : 'YYYY년 MMMM D일', - lll : 'YYYY년 MMMM D일 A h:mm', - llll : 'YYYY년 MMMM D일 dddd A h:mm' - }, - calendar : { - sameDay : '오늘 LT', - nextDay : '내일 LT', - nextWeek : 'dddd LT', - lastDay : '어제 LT', - lastWeek : '지난주 dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s 후', - past : '%s 전', - s : '몇 초', - ss : '%d초', - m : '1분', - mm : '%d분', - h : '한 시간', - hh : '%d시간', - d : '하루', - dd : '%d일', - M : '한 달', - MM : '%d달', - y : '일 년', - yy : '%d년' - }, - dayOfMonthOrdinalParse : /\d{1,2}(일|월|주)/, - ordinal : function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'DDD': - return number + '일'; - case 'M': - return number + '월'; - case 'w': - case 'W': - return number + '주'; - default: - return number; - } - }, - meridiemParse : /오전|오후/, - isPM : function (token) { - return token === '오후'; - }, - meridiem : function (hour, minute, isUpper) { - return hour < 12 ? '오전' : '오후'; - } - }); - - //! moment.js locale configuration - - var suffixes$2 = { - 0: '-чү', - 1: '-чи', - 2: '-чи', - 3: '-чү', - 4: '-чү', - 5: '-чи', - 6: '-чы', - 7: '-чи', - 8: '-чи', - 9: '-чу', - 10: '-чу', - 20: '-чы', - 30: '-чу', - 40: '-чы', - 50: '-чү', - 60: '-чы', - 70: '-чи', - 80: '-чи', - 90: '-чу', - 100: '-чү' - }; - - hooks.defineLocale('ky', { - months : 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'), - monthsShort : 'янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'), - weekdays : 'Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби'.split('_'), - weekdaysShort : 'Жек_Дүй_Шей_Шар_Бей_Жум_Ише'.split('_'), - weekdaysMin : 'Жк_Дй_Шй_Шр_Бй_Жм_Иш'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Бүгүн саат] LT', - nextDay : '[Эртең саат] LT', - nextWeek : 'dddd [саат] LT', - lastDay : '[Кече саат] LT', - lastWeek : '[Өткен аптанын] dddd [күнү] [саат] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ичинде', - past : '%s мурун', - s : 'бирнече секунд', - ss : '%d секунд', - m : 'бир мүнөт', - mm : '%d мүнөт', - h : 'бир саат', - hh : '%d саат', - d : 'бир күн', - dd : '%d күн', - M : 'бир ай', - MM : '%d ай', - y : 'бир жыл', - yy : '%d жыл' - }, - dayOfMonthOrdinalParse: /\d{1,2}-(чи|чы|чү|чу)/, - ordinal : function (number) { - var a = number % 10, - b = number >= 100 ? 100 : null; - return number + (suffixes$2[number] || suffixes$2[a] || suffixes$2[b]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$5(number, withoutSuffix, key, isFuture) { - var format = { - 'm': ['eng Minutt', 'enger Minutt'], - 'h': ['eng Stonn', 'enger Stonn'], - 'd': ['een Dag', 'engem Dag'], - 'M': ['ee Mount', 'engem Mount'], - 'y': ['ee Joer', 'engem Joer'] - }; - return withoutSuffix ? format[key][0] : format[key][1]; - } - function processFutureTime(string) { - var number = string.substr(0, string.indexOf(' ')); - if (eifelerRegelAppliesToNumber(number)) { - return 'a ' + string; - } - return 'an ' + string; - } - function processPastTime(string) { - var number = string.substr(0, string.indexOf(' ')); - if (eifelerRegelAppliesToNumber(number)) { - return 'viru ' + string; - } - return 'virun ' + string; - } - /** - * Returns true if the word before the given number loses the '-n' ending. - * e.g. 'an 10 Deeg' but 'a 5 Deeg' - * - * @param number {integer} - * @returns {boolean} - */ - function eifelerRegelAppliesToNumber(number) { - number = parseInt(number, 10); - if (isNaN(number)) { - return false; - } - if (number < 0) { - // Negative Number --> always true - return true; - } else if (number < 10) { - // Only 1 digit - if (4 <= number && number <= 7) { - return true; - } - return false; - } else if (number < 100) { - // 2 digits - var lastDigit = number % 10, firstDigit = number / 10; - if (lastDigit === 0) { - return eifelerRegelAppliesToNumber(firstDigit); - } - return eifelerRegelAppliesToNumber(lastDigit); - } else if (number < 10000) { - // 3 or 4 digits --> recursively check first digit - while (number >= 10) { - number = number / 10; - } - return eifelerRegelAppliesToNumber(number); - } else { - // Anything larger than 4 digits: recursively check first n-3 digits - number = number / 1000; - return eifelerRegelAppliesToNumber(number); - } - } - - hooks.defineLocale('lb', { - months: 'Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember'.split('_'), - monthsShort: 'Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.'.split('_'), - monthsParseExact : true, - weekdays: 'Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg'.split('_'), - weekdaysShort: 'So._Mé._Dë._Më._Do._Fr._Sa.'.split('_'), - weekdaysMin: 'So_Mé_Dë_Më_Do_Fr_Sa'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm [Auer]', - LTS: 'H:mm:ss [Auer]', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm [Auer]', - LLLL: 'dddd, D. MMMM YYYY H:mm [Auer]' - }, - calendar: { - sameDay: '[Haut um] LT', - sameElse: 'L', - nextDay: '[Muer um] LT', - nextWeek: 'dddd [um] LT', - lastDay: '[Gëschter um] LT', - lastWeek: function () { - // Different date string for 'Dënschdeg' (Tuesday) and 'Donneschdeg' (Thursday) due to phonological rule - switch (this.day()) { - case 2: - case 4: - return '[Leschten] dddd [um] LT'; - default: - return '[Leschte] dddd [um] LT'; - } - } - }, - relativeTime : { - future : processFutureTime, - past : processPastTime, - s : 'e puer Sekonnen', - ss : '%d Sekonnen', - m : processRelativeTime$5, - mm : '%d Minutten', - h : processRelativeTime$5, - hh : '%d Stonnen', - d : processRelativeTime$5, - dd : '%d Deeg', - M : processRelativeTime$5, - MM : '%d Méint', - y : processRelativeTime$5, - yy : '%d Joer' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal: '%d.', - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('lo', { - months : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), - monthsShort : 'ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ'.split('_'), - weekdays : 'ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), - weekdaysShort : 'ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ'.split('_'), - weekdaysMin : 'ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'ວັນdddd D MMMM YYYY HH:mm' - }, - meridiemParse: /ຕອນເຊົ້າ|ຕອນແລງ/, - isPM: function (input) { - return input === 'ຕອນແລງ'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ຕອນເຊົ້າ'; - } else { - return 'ຕອນແລງ'; - } - }, - calendar : { - sameDay : '[ມື້ນີ້ເວລາ] LT', - nextDay : '[ມື້ອື່ນເວລາ] LT', - nextWeek : '[ວັນ]dddd[ໜ້າເວລາ] LT', - lastDay : '[ມື້ວານນີ້ເວລາ] LT', - lastWeek : '[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ອີກ %s', - past : '%sຜ່ານມາ', - s : 'ບໍ່ເທົ່າໃດວິນາທີ', - ss : '%d ວິນາທີ' , - m : '1 ນາທີ', - mm : '%d ນາທີ', - h : '1 ຊົ່ວໂມງ', - hh : '%d ຊົ່ວໂມງ', - d : '1 ມື້', - dd : '%d ມື້', - M : '1 ເດືອນ', - MM : '%d ເດືອນ', - y : '1 ປີ', - yy : '%d ປີ' - }, - dayOfMonthOrdinalParse: /(ທີ່)\d{1,2}/, - ordinal : function (number) { - return 'ທີ່' + number; - } - }); - - //! moment.js locale configuration - - var units = { - 'ss' : 'sekundė_sekundžių_sekundes', - 'm' : 'minutė_minutės_minutę', - 'mm': 'minutės_minučių_minutes', - 'h' : 'valanda_valandos_valandą', - 'hh': 'valandos_valandų_valandas', - 'd' : 'diena_dienos_dieną', - 'dd': 'dienos_dienų_dienas', - 'M' : 'mėnuo_mėnesio_mėnesį', - 'MM': 'mėnesiai_mėnesių_mėnesius', - 'y' : 'metai_metų_metus', - 'yy': 'metai_metų_metus' - }; - function translateSeconds(number, withoutSuffix, key, isFuture) { - if (withoutSuffix) { - return 'kelios sekundės'; - } else { - return isFuture ? 'kelių sekundžių' : 'kelias sekundes'; - } - } - function translateSingular(number, withoutSuffix, key, isFuture) { - return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]); - } - function special(number) { - return number % 10 === 0 || (number > 10 && number < 20); - } - function forms(key) { - return units[key].split('_'); - } - function translate$6(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - if (number === 1) { - return result + translateSingular(number, withoutSuffix, key[0], isFuture); - } else if (withoutSuffix) { - return result + (special(number) ? forms(key)[1] : forms(key)[0]); - } else { - if (isFuture) { - return result + forms(key)[1]; - } else { - return result + (special(number) ? forms(key)[1] : forms(key)[2]); - } - } - } - hooks.defineLocale('lt', { - months : { - format: 'sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio'.split('_'), - standalone: 'sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis'.split('_'), - isFormat: /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/ - }, - monthsShort : 'sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd'.split('_'), - weekdays : { - format: 'sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį'.split('_'), - standalone: 'sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis'.split('_'), - isFormat: /dddd HH:mm/ - }, - weekdaysShort : 'Sek_Pir_Ant_Tre_Ket_Pen_Šeš'.split('_'), - weekdaysMin : 'S_P_A_T_K_Pn_Š'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY [m.] MMMM D [d.]', - LLL : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', - LLLL : 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]', - l : 'YYYY-MM-DD', - ll : 'YYYY [m.] MMMM D [d.]', - lll : 'YYYY [m.] MMMM D [d.], HH:mm [val.]', - llll : 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]' - }, - calendar : { - sameDay : '[Šiandien] LT', - nextDay : '[Rytoj] LT', - nextWeek : 'dddd LT', - lastDay : '[Vakar] LT', - lastWeek : '[Praėjusį] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : 'po %s', - past : 'prieš %s', - s : translateSeconds, - ss : translate$6, - m : translateSingular, - mm : translate$6, - h : translateSingular, - hh : translate$6, - d : translateSingular, - dd : translate$6, - M : translateSingular, - MM : translate$6, - y : translateSingular, - yy : translate$6 - }, - dayOfMonthOrdinalParse: /\d{1,2}-oji/, - ordinal : function (number) { - return number + '-oji'; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var units$1 = { - 'ss': 'sekundes_sekundēm_sekunde_sekundes'.split('_'), - 'm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), - 'mm': 'minūtes_minūtēm_minūte_minūtes'.split('_'), - 'h': 'stundas_stundām_stunda_stundas'.split('_'), - 'hh': 'stundas_stundām_stunda_stundas'.split('_'), - 'd': 'dienas_dienām_diena_dienas'.split('_'), - 'dd': 'dienas_dienām_diena_dienas'.split('_'), - 'M': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), - 'MM': 'mēneša_mēnešiem_mēnesis_mēneši'.split('_'), - 'y': 'gada_gadiem_gads_gadi'.split('_'), - 'yy': 'gada_gadiem_gads_gadi'.split('_') - }; - /** - * @param withoutSuffix boolean true = a length of time; false = before/after a period of time. - */ - function format$1(forms, number, withoutSuffix) { - if (withoutSuffix) { - // E.g. "21 minūte", "3 minūtes". - return number % 10 === 1 && number % 100 !== 11 ? forms[2] : forms[3]; - } else { - // E.g. "21 minūtes" as in "pēc 21 minūtes". - // E.g. "3 minūtēm" as in "pēc 3 minūtēm". - return number % 10 === 1 && number % 100 !== 11 ? forms[0] : forms[1]; - } - } - function relativeTimeWithPlural$1(number, withoutSuffix, key) { - return number + ' ' + format$1(units$1[key], number, withoutSuffix); - } - function relativeTimeWithSingular(number, withoutSuffix, key) { - return format$1(units$1[key], number, withoutSuffix); - } - function relativeSeconds(number, withoutSuffix) { - return withoutSuffix ? 'dažas sekundes' : 'dažām sekundēm'; - } - - hooks.defineLocale('lv', { - months : 'janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena'.split('_'), - weekdaysShort : 'Sv_P_O_T_C_Pk_S'.split('_'), - weekdaysMin : 'Sv_P_O_T_C_Pk_S'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY.', - LL : 'YYYY. [gada] D. MMMM', - LLL : 'YYYY. [gada] D. MMMM, HH:mm', - LLLL : 'YYYY. [gada] D. MMMM, dddd, HH:mm' - }, - calendar : { - sameDay : '[Šodien pulksten] LT', - nextDay : '[Rīt pulksten] LT', - nextWeek : 'dddd [pulksten] LT', - lastDay : '[Vakar pulksten] LT', - lastWeek : '[Pagājušā] dddd [pulksten] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'pēc %s', - past : 'pirms %s', - s : relativeSeconds, - ss : relativeTimeWithPlural$1, - m : relativeTimeWithSingular, - mm : relativeTimeWithPlural$1, - h : relativeTimeWithSingular, - hh : relativeTimeWithPlural$1, - d : relativeTimeWithSingular, - dd : relativeTimeWithPlural$1, - M : relativeTimeWithSingular, - MM : relativeTimeWithPlural$1, - y : relativeTimeWithSingular, - yy : relativeTimeWithPlural$1 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var translator = { - words: { //Different grammatical cases - ss: ['sekund', 'sekunda', 'sekundi'], - m: ['jedan minut', 'jednog minuta'], - mm: ['minut', 'minuta', 'minuta'], - h: ['jedan sat', 'jednog sata'], - hh: ['sat', 'sata', 'sati'], - dd: ['dan', 'dana', 'dana'], - MM: ['mjesec', 'mjeseca', 'mjeseci'], - yy: ['godina', 'godine', 'godina'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator.correctGrammaticalCase(number, wordKey); - } - } - }; - - hooks.defineLocale('me', { - months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact : true, - weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'), - weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'), - weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[danas u] LT', - nextDay: '[sjutra u] LT', - - nextWeek: function () { - switch (this.day()) { - case 0: - return '[u] [nedjelju] [u] LT'; - case 3: - return '[u] [srijedu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[u] dddd [u] LT'; - } - }, - lastDay : '[juče u] LT', - lastWeek : function () { - var lastWeekDays = [ - '[prošle] [nedjelje] [u] LT', - '[prošlog] [ponedjeljka] [u] LT', - '[prošlog] [utorka] [u] LT', - '[prošle] [srijede] [u] LT', - '[prošlog] [četvrtka] [u] LT', - '[prošlog] [petka] [u] LT', - '[prošle] [subote] [u] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'za %s', - past : 'prije %s', - s : 'nekoliko sekundi', - ss : translator.translate, - m : translator.translate, - mm : translator.translate, - h : translator.translate, - hh : translator.translate, - d : 'dan', - dd : translator.translate, - M : 'mjesec', - MM : translator.translate, - y : 'godinu', - yy : translator.translate - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('mi', { - months: 'Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea'.split('_'), - monthsShort: 'Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki'.split('_'), - monthsRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, - monthsStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, - monthsShortRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i, - monthsShortStrictRegex: /(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i, - weekdays: 'Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei'.split('_'), - weekdaysShort: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'), - weekdaysMin: 'Ta_Ma_Tū_We_Tāi_Pa_Hā'.split('_'), - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY [i] HH:mm', - LLLL: 'dddd, D MMMM YYYY [i] HH:mm' - }, - calendar: { - sameDay: '[i teie mahana, i] LT', - nextDay: '[apopo i] LT', - nextWeek: 'dddd [i] LT', - lastDay: '[inanahi i] LT', - lastWeek: 'dddd [whakamutunga i] LT', - sameElse: 'L' - }, - relativeTime: { - future: 'i roto i %s', - past: '%s i mua', - s: 'te hēkona ruarua', - ss: '%d hēkona', - m: 'he meneti', - mm: '%d meneti', - h: 'te haora', - hh: '%d haora', - d: 'he ra', - dd: '%d ra', - M: 'he marama', - MM: '%d marama', - y: 'he tau', - yy: '%d tau' - }, - dayOfMonthOrdinalParse: /\d{1,2}º/, - ordinal: '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('mk', { - months : 'јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември'.split('_'), - monthsShort : 'јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек'.split('_'), - weekdays : 'недела_понеделник_вторник_среда_четврток_петок_сабота'.split('_'), - weekdaysShort : 'нед_пон_вто_сре_чет_пет_саб'.split('_'), - weekdaysMin : 'нe_пo_вт_ср_че_пе_сa'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'D.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[Денес во] LT', - nextDay : '[Утре во] LT', - nextWeek : '[Во] dddd [во] LT', - lastDay : '[Вчера во] LT', - lastWeek : function () { - switch (this.day()) { - case 0: - case 3: - case 6: - return '[Изминатата] dddd [во] LT'; - case 1: - case 2: - case 4: - case 5: - return '[Изминатиот] dddd [во] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'после %s', - past : 'пред %s', - s : 'неколку секунди', - ss : '%d секунди', - m : 'минута', - mm : '%d минути', - h : 'час', - hh : '%d часа', - d : 'ден', - dd : '%d дена', - M : 'месец', - MM : '%d месеци', - y : 'година', - yy : '%d години' - }, - dayOfMonthOrdinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/, - ordinal : function (number) { - var lastDigit = number % 10, - last2Digits = number % 100; - if (number === 0) { - return number + '-ев'; - } else if (last2Digits === 0) { - return number + '-ен'; - } else if (last2Digits > 10 && last2Digits < 20) { - return number + '-ти'; - } else if (lastDigit === 1) { - return number + '-ви'; - } else if (lastDigit === 2) { - return number + '-ри'; - } else if (lastDigit === 7 || lastDigit === 8) { - return number + '-ми'; - } else { - return number + '-ти'; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ml', { - months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split('_'), - monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split('_'), - monthsParseExact : true, - weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split('_'), - weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split('_'), - weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split('_'), - longDateFormat : { - LT : 'A h:mm -നു', - LTS : 'A h:mm:ss -നു', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm -നു', - LLLL : 'dddd, D MMMM YYYY, A h:mm -നു' - }, - calendar : { - sameDay : '[ഇന്ന്] LT', - nextDay : '[നാളെ] LT', - nextWeek : 'dddd, LT', - lastDay : '[ഇന്നലെ] LT', - lastWeek : '[കഴിഞ്ഞ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s കഴിഞ്ഞ്', - past : '%s മുൻപ്', - s : 'അൽപ നിമിഷങ്ങൾ', - ss : '%d സെക്കൻഡ്', - m : 'ഒരു മിനിറ്റ്', - mm : '%d മിനിറ്റ്', - h : 'ഒരു മണിക്കൂർ', - hh : '%d മണിക്കൂർ', - d : 'ഒരു ദിവസം', - dd : '%d ദിവസം', - M : 'ഒരു മാസം', - MM : '%d മാസം', - y : 'ഒരു വർഷം', - yy : '%d വർഷം' - }, - meridiemParse: /രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ((meridiem === 'രാത്രി' && hour >= 4) || - meridiem === 'ഉച്ച കഴിഞ്ഞ്' || - meridiem === 'വൈകുന്നേരം') { - return hour + 12; - } else { - return hour; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'രാത്രി'; - } else if (hour < 12) { - return 'രാവിലെ'; - } else if (hour < 17) { - return 'ഉച്ച കഴിഞ്ഞ്'; - } else if (hour < 20) { - return 'വൈകുന്നേരം'; - } else { - return 'രാത്രി'; - } - } - }); - - //! moment.js locale configuration - - function translate$7(number, withoutSuffix, key, isFuture) { - switch (key) { - case 's': - return withoutSuffix ? 'хэдхэн секунд' : 'хэдхэн секундын'; - case 'ss': - return number + (withoutSuffix ? ' секунд' : ' секундын'); - case 'm': - case 'mm': - return number + (withoutSuffix ? ' минут' : ' минутын'); - case 'h': - case 'hh': - return number + (withoutSuffix ? ' цаг' : ' цагийн'); - case 'd': - case 'dd': - return number + (withoutSuffix ? ' өдөр' : ' өдрийн'); - case 'M': - case 'MM': - return number + (withoutSuffix ? ' сар' : ' сарын'); - case 'y': - case 'yy': - return number + (withoutSuffix ? ' жил' : ' жилийн'); - default: - return number; - } - } - - hooks.defineLocale('mn', { - months : 'Нэгдүгээр сар_Хоёрдугаар сар_Гуравдугаар сар_Дөрөвдүгээр сар_Тавдугаар сар_Зургадугаар сар_Долдугаар сар_Наймдугаар сар_Есдүгээр сар_Аравдугаар сар_Арван нэгдүгээр сар_Арван хоёрдугаар сар'.split('_'), - monthsShort : '1 сар_2 сар_3 сар_4 сар_5 сар_6 сар_7 сар_8 сар_9 сар_10 сар_11 сар_12 сар'.split('_'), - monthsParseExact : true, - weekdays : 'Ням_Даваа_Мягмар_Лхагва_Пүрэв_Баасан_Бямба'.split('_'), - weekdaysShort : 'Ням_Дав_Мяг_Лха_Пүр_Баа_Бям'.split('_'), - weekdaysMin : 'Ня_Да_Мя_Лх_Пү_Ба_Бя'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'YYYY оны MMMMын D', - LLL : 'YYYY оны MMMMын D HH:mm', - LLLL : 'dddd, YYYY оны MMMMын D HH:mm' - }, - meridiemParse: /ҮӨ|ҮХ/i, - isPM : function (input) { - return input === 'ҮХ'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ҮӨ'; - } else { - return 'ҮХ'; - } - }, - calendar : { - sameDay : '[Өнөөдөр] LT', - nextDay : '[Маргааш] LT', - nextWeek : '[Ирэх] dddd LT', - lastDay : '[Өчигдөр] LT', - lastWeek : '[Өнгөрсөн] dddd LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s дараа', - past : '%s өмнө', - s : translate$7, - ss : translate$7, - m : translate$7, - mm : translate$7, - h : translate$7, - hh : translate$7, - d : translate$7, - dd : translate$7, - M : translate$7, - MM : translate$7, - y : translate$7, - yy : translate$7 - }, - dayOfMonthOrdinalParse: /\d{1,2} өдөр/, - ordinal : function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'DDD': - return number + ' өдөр'; - default: - return number; - } - } - }); - - //! moment.js locale configuration - - var symbolMap$a = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' - }, - numberMap$9 = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; - - function relativeTimeMr(number, withoutSuffix, string, isFuture) - { - var output = ''; - if (withoutSuffix) { - switch (string) { - case 's': output = 'काही सेकंद'; break; - case 'ss': output = '%d सेकंद'; break; - case 'm': output = 'एक मिनिट'; break; - case 'mm': output = '%d मिनिटे'; break; - case 'h': output = 'एक तास'; break; - case 'hh': output = '%d तास'; break; - case 'd': output = 'एक दिवस'; break; - case 'dd': output = '%d दिवस'; break; - case 'M': output = 'एक महिना'; break; - case 'MM': output = '%d महिने'; break; - case 'y': output = 'एक वर्ष'; break; - case 'yy': output = '%d वर्षे'; break; - } - } - else { - switch (string) { - case 's': output = 'काही सेकंदां'; break; - case 'ss': output = '%d सेकंदां'; break; - case 'm': output = 'एका मिनिटा'; break; - case 'mm': output = '%d मिनिटां'; break; - case 'h': output = 'एका तासा'; break; - case 'hh': output = '%d तासां'; break; - case 'd': output = 'एका दिवसा'; break; - case 'dd': output = '%d दिवसां'; break; - case 'M': output = 'एका महिन्या'; break; - case 'MM': output = '%d महिन्यां'; break; - case 'y': output = 'एका वर्षा'; break; - case 'yy': output = '%d वर्षां'; break; - } - } - return output.replace(/%d/i, number); - } - - hooks.defineLocale('mr', { - months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split('_'), - monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split('_'), - monthsParseExact : true, - weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split('_'), - weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split('_'), - weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split('_'), - longDateFormat : { - LT : 'A h:mm वाजता', - LTS : 'A h:mm:ss वाजता', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm वाजता', - LLLL : 'dddd, D MMMM YYYY, A h:mm वाजता' - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[उद्या] LT', - nextWeek : 'dddd, LT', - lastDay : '[काल] LT', - lastWeek: '[मागील] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future: '%sमध्ये', - past: '%sपूर्वी', - s: relativeTimeMr, - ss: relativeTimeMr, - m: relativeTimeMr, - mm: relativeTimeMr, - h: relativeTimeMr, - hh: relativeTimeMr, - d: relativeTimeMr, - dd: relativeTimeMr, - M: relativeTimeMr, - MM: relativeTimeMr, - y: relativeTimeMr, - yy: relativeTimeMr - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap$9[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$a[match]; - }); - }, - meridiemParse: /रात्री|सकाळी|दुपारी|सायंकाळी/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'रात्री') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'सकाळी') { - return hour; - } else if (meridiem === 'दुपारी') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'सायंकाळी') { - return hour + 12; - } - }, - meridiem: function (hour, minute, isLower) { - if (hour < 4) { - return 'रात्री'; - } else if (hour < 10) { - return 'सकाळी'; - } else if (hour < 17) { - return 'दुपारी'; - } else if (hour < 20) { - return 'सायंकाळी'; - } else { - return 'रात्री'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ms-my', { - months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), - weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), - weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), - weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|tengahari|petang|malam/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'tengahari') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'petang' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'tengahari'; - } else if (hours < 19) { - return 'petang'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Esok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kelmarin pukul] LT', - lastWeek : 'dddd [lepas pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lepas', - s : 'beberapa saat', - ss : '%d saat', - m : 'seminit', - mm : '%d minit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ms', { - months : 'Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis'.split('_'), - weekdays : 'Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu'.split('_'), - weekdaysShort : 'Ahd_Isn_Sel_Rab_Kha_Jum_Sab'.split('_'), - weekdaysMin : 'Ah_Is_Sl_Rb_Km_Jm_Sb'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [pukul] HH.mm', - LLLL : 'dddd, D MMMM YYYY [pukul] HH.mm' - }, - meridiemParse: /pagi|tengahari|petang|malam/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'pagi') { - return hour; - } else if (meridiem === 'tengahari') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'petang' || meridiem === 'malam') { - return hour + 12; - } - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'pagi'; - } else if (hours < 15) { - return 'tengahari'; - } else if (hours < 19) { - return 'petang'; - } else { - return 'malam'; - } - }, - calendar : { - sameDay : '[Hari ini pukul] LT', - nextDay : '[Esok pukul] LT', - nextWeek : 'dddd [pukul] LT', - lastDay : '[Kelmarin pukul] LT', - lastWeek : 'dddd [lepas pukul] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'dalam %s', - past : '%s yang lepas', - s : 'beberapa saat', - ss : '%d saat', - m : 'seminit', - mm : '%d minit', - h : 'sejam', - hh : '%d jam', - d : 'sehari', - dd : '%d hari', - M : 'sebulan', - MM : '%d bulan', - y : 'setahun', - yy : '%d tahun' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('mt', { - months : 'Jannar_Frar_Marzu_April_Mejju_Ġunju_Lulju_Awwissu_Settembru_Ottubru_Novembru_Diċembru'.split('_'), - monthsShort : 'Jan_Fra_Mar_Apr_Mej_Ġun_Lul_Aww_Set_Ott_Nov_Diċ'.split('_'), - weekdays : 'Il-Ħadd_It-Tnejn_It-Tlieta_L-Erbgħa_Il-Ħamis_Il-Ġimgħa_Is-Sibt'.split('_'), - weekdaysShort : 'Ħad_Tne_Tli_Erb_Ħam_Ġim_Sib'.split('_'), - weekdaysMin : 'Ħa_Tn_Tl_Er_Ħa_Ġi_Si'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Illum fil-]LT', - nextDay : '[Għada fil-]LT', - nextWeek : 'dddd [fil-]LT', - lastDay : '[Il-bieraħ fil-]LT', - lastWeek : 'dddd [li għadda] [fil-]LT', - sameElse : 'L' - }, - relativeTime : { - future : 'f’ %s', - past : '%s ilu', - s : 'ftit sekondi', - ss : '%d sekondi', - m : 'minuta', - mm : '%d minuti', - h : 'siegħa', - hh : '%d siegħat', - d : 'ġurnata', - dd : '%d ġranet', - M : 'xahar', - MM : '%d xhur', - y : 'sena', - yy : '%d sni' - }, - dayOfMonthOrdinalParse : /\d{1,2}º/, - ordinal: '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$b = { - '1': '၁', - '2': '၂', - '3': '၃', - '4': '၄', - '5': '၅', - '6': '၆', - '7': '၇', - '8': '၈', - '9': '၉', - '0': '၀' - }, numberMap$a = { - '၁': '1', - '၂': '2', - '၃': '3', - '၄': '4', - '၅': '5', - '၆': '6', - '၇': '7', - '၈': '8', - '၉': '9', - '၀': '0' - }; - - hooks.defineLocale('my', { - months: 'ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ'.split('_'), - monthsShort: 'ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ'.split('_'), - weekdays: 'တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ'.split('_'), - weekdaysShort: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), - weekdaysMin: 'နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ'.split('_'), - - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd D MMMM YYYY HH:mm' - }, - calendar: { - sameDay: '[ယနေ.] LT [မှာ]', - nextDay: '[မနက်ဖြန်] LT [မှာ]', - nextWeek: 'dddd LT [မှာ]', - lastDay: '[မနေ.က] LT [မှာ]', - lastWeek: '[ပြီးခဲ့သော] dddd LT [မှာ]', - sameElse: 'L' - }, - relativeTime: { - future: 'လာမည့် %s မှာ', - past: 'လွန်ခဲ့သော %s က', - s: 'စက္ကန်.အနည်းငယ်', - ss : '%d စက္ကန့်', - m: 'တစ်မိနစ်', - mm: '%d မိနစ်', - h: 'တစ်နာရီ', - hh: '%d နာရီ', - d: 'တစ်ရက်', - dd: '%d ရက်', - M: 'တစ်လ', - MM: '%d လ', - y: 'တစ်နှစ်', - yy: '%d နှစ်' - }, - preparse: function (string) { - return string.replace(/[၁၂၃၄၅၆၇၈၉၀]/g, function (match) { - return numberMap$a[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$b[match]; - }); - }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('nb', { - months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.'.split('_'), - monthsParseExact : true, - weekdays : 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'), - weekdaysShort : 'sø._ma._ti._on._to._fr._lø.'.split('_'), - weekdaysMin : 'sø_ma_ti_on_to_fr_lø'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] HH:mm', - LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' - }, - calendar : { - sameDay: '[i dag kl.] LT', - nextDay: '[i morgen kl.] LT', - nextWeek: 'dddd [kl.] LT', - lastDay: '[i går kl.] LT', - lastWeek: '[forrige] dddd [kl.] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s siden', - s : 'noen sekunder', - ss : '%d sekunder', - m : 'ett minutt', - mm : '%d minutter', - h : 'en time', - hh : '%d timer', - d : 'en dag', - dd : '%d dager', - M : 'en måned', - MM : '%d måneder', - y : 'ett år', - yy : '%d år' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$c = { - '1': '१', - '2': '२', - '3': '३', - '4': '४', - '5': '५', - '6': '६', - '7': '७', - '8': '८', - '9': '९', - '0': '०' - }, - numberMap$b = { - '१': '1', - '२': '2', - '३': '3', - '४': '4', - '५': '5', - '६': '6', - '७': '7', - '८': '8', - '९': '9', - '०': '0' - }; - - hooks.defineLocale('ne', { - months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split('_'), - monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split('_'), - monthsParseExact : true, - weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split('_'), - weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split('_'), - weekdaysMin : 'आ._सो._मं._बु._बि._शु._श.'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'Aको h:mm बजे', - LTS : 'Aको h:mm:ss बजे', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, Aको h:mm बजे', - LLLL : 'dddd, D MMMM YYYY, Aको h:mm बजे' - }, - preparse: function (string) { - return string.replace(/[१२३४५६७८९०]/g, function (match) { - return numberMap$b[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$c[match]; - }); - }, - meridiemParse: /राति|बिहान|दिउँसो|साँझ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'राति') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'बिहान') { - return hour; - } else if (meridiem === 'दिउँसो') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'साँझ') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 3) { - return 'राति'; - } else if (hour < 12) { - return 'बिहान'; - } else if (hour < 16) { - return 'दिउँसो'; - } else if (hour < 20) { - return 'साँझ'; - } else { - return 'राति'; - } - }, - calendar : { - sameDay : '[आज] LT', - nextDay : '[भोलि] LT', - nextWeek : '[आउँदो] dddd[,] LT', - lastDay : '[हिजो] LT', - lastWeek : '[गएको] dddd[,] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%sमा', - past : '%s अगाडि', - s : 'केही क्षण', - ss : '%d सेकेण्ड', - m : 'एक मिनेट', - mm : '%d मिनेट', - h : 'एक घण्टा', - hh : '%d घण्टा', - d : 'एक दिन', - dd : '%d दिन', - M : 'एक महिना', - MM : '%d महिना', - y : 'एक बर्ष', - yy : '%d बर्ष' - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortWithDots$1 = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'), - monthsShortWithoutDots$1 = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'); - - var monthsParse$2 = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i]; - var monthsRegex$3 = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i; - - hooks.defineLocale('nl-be', { - months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortWithDots$1; - } else if (/-MMM-/.test(format)) { - return monthsShortWithoutDots$1[m.month()]; - } else { - return monthsShortWithDots$1[m.month()]; - } - }, - - monthsRegex: monthsRegex$3, - monthsShortRegex: monthsRegex$3, - monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i, - monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i, - - monthsParse : monthsParse$2, - longMonthsParse : monthsParse$2, - shortMonthsParse : monthsParse$2, - - weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'), - weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'), - weekdaysMin : 'zo_ma_di_wo_do_vr_za'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[vandaag om] LT', - nextDay: '[morgen om] LT', - nextWeek: 'dddd [om] LT', - lastDay: '[gisteren om] LT', - lastWeek: '[afgelopen] dddd [om] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'over %s', - past : '%s geleden', - s : 'een paar seconden', - ss : '%d seconden', - m : 'één minuut', - mm : '%d minuten', - h : 'één uur', - hh : '%d uur', - d : 'één dag', - dd : '%d dagen', - M : 'één maand', - MM : '%d maanden', - y : 'één jaar', - yy : '%d jaar' - }, - dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsShortWithDots$2 = 'jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.'.split('_'), - monthsShortWithoutDots$2 = 'jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec'.split('_'); - - var monthsParse$3 = [/^jan/i, /^feb/i, /^maart|mrt.?$/i, /^apr/i, /^mei$/i, /^jun[i.]?$/i, /^jul[i.]?$/i, /^aug/i, /^sep/i, /^okt/i, /^nov/i, /^dec/i]; - var monthsRegex$4 = /^(januari|februari|maart|april|mei|april|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i; - - hooks.defineLocale('nl', { - months : 'januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december'.split('_'), - monthsShort : function (m, format) { - if (!m) { - return monthsShortWithDots$2; - } else if (/-MMM-/.test(format)) { - return monthsShortWithoutDots$2[m.month()]; - } else { - return monthsShortWithDots$2[m.month()]; - } - }, - - monthsRegex: monthsRegex$4, - monthsShortRegex: monthsRegex$4, - monthsStrictRegex: /^(januari|februari|maart|mei|ju[nl]i|april|augustus|september|oktober|november|december)/i, - monthsShortStrictRegex: /^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i, - - monthsParse : monthsParse$3, - longMonthsParse : monthsParse$3, - shortMonthsParse : monthsParse$3, - - weekdays : 'zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag'.split('_'), - weekdaysShort : 'zo._ma._di._wo._do._vr._za.'.split('_'), - weekdaysMin : 'zo_ma_di_wo_do_vr_za'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD-MM-YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[vandaag om] LT', - nextDay: '[morgen om] LT', - nextWeek: 'dddd [om] LT', - lastDay: '[gisteren om] LT', - lastWeek: '[afgelopen] dddd [om] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'over %s', - past : '%s geleden', - s : 'een paar seconden', - ss : '%d seconden', - m : 'één minuut', - mm : '%d minuten', - h : 'één uur', - hh : '%d uur', - d : 'één dag', - dd : '%d dagen', - M : 'één maand', - MM : '%d maanden', - y : 'één jaar', - yy : '%d jaar' - }, - dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/, - ordinal : function (number) { - return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('nn', { - months : 'januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember'.split('_'), - monthsShort : 'jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des'.split('_'), - weekdays : 'sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag'.split('_'), - weekdaysShort : 'sun_mån_tys_ons_tor_fre_lau'.split('_'), - weekdaysMin : 'su_må_ty_on_to_fr_lø'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY [kl.] H:mm', - LLLL : 'dddd D. MMMM YYYY [kl.] HH:mm' - }, - calendar : { - sameDay: '[I dag klokka] LT', - nextDay: '[I morgon klokka] LT', - nextWeek: 'dddd [klokka] LT', - lastDay: '[I går klokka] LT', - lastWeek: '[Føregåande] dddd [klokka] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : '%s sidan', - s : 'nokre sekund', - ss : '%d sekund', - m : 'eit minutt', - mm : '%d minutt', - h : 'ein time', - hh : '%d timar', - d : 'ein dag', - dd : '%d dagar', - M : 'ein månad', - MM : '%d månader', - y : 'eit år', - yy : '%d år' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$d = { - '1': '੧', - '2': '੨', - '3': '੩', - '4': '੪', - '5': '੫', - '6': '੬', - '7': '੭', - '8': '੮', - '9': '੯', - '0': '੦' - }, - numberMap$c = { - '੧': '1', - '੨': '2', - '੩': '3', - '੪': '4', - '੫': '5', - '੬': '6', - '੭': '7', - '੮': '8', - '੯': '9', - '੦': '0' - }; - - hooks.defineLocale('pa-in', { - // There are months name as per Nanakshahi Calender but they are not used as rigidly in modern Punjabi. - months : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'), - monthsShort : 'ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ'.split('_'), - weekdays : 'ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ'.split('_'), - weekdaysShort : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'), - weekdaysMin : 'ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ'.split('_'), - longDateFormat : { - LT : 'A h:mm ਵਜੇ', - LTS : 'A h:mm:ss ਵਜੇ', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm ਵਜੇ', - LLLL : 'dddd, D MMMM YYYY, A h:mm ਵਜੇ' - }, - calendar : { - sameDay : '[ਅਜ] LT', - nextDay : '[ਕਲ] LT', - nextWeek : '[ਅਗਲਾ] dddd, LT', - lastDay : '[ਕਲ] LT', - lastWeek : '[ਪਿਛਲੇ] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s ਵਿੱਚ', - past : '%s ਪਿਛਲੇ', - s : 'ਕੁਝ ਸਕਿੰਟ', - ss : '%d ਸਕਿੰਟ', - m : 'ਇਕ ਮਿੰਟ', - mm : '%d ਮਿੰਟ', - h : 'ਇੱਕ ਘੰਟਾ', - hh : '%d ਘੰਟੇ', - d : 'ਇੱਕ ਦਿਨ', - dd : '%d ਦਿਨ', - M : 'ਇੱਕ ਮਹੀਨਾ', - MM : '%d ਮਹੀਨੇ', - y : 'ਇੱਕ ਸਾਲ', - yy : '%d ਸਾਲ' - }, - preparse: function (string) { - return string.replace(/[੧੨੩੪੫੬੭੮੯੦]/g, function (match) { - return numberMap$c[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$d[match]; - }); - }, - // Punjabi notation for meridiems are quite fuzzy in practice. While there exists - // a rigid notion of a 'Pahar' it is not used as rigidly in modern Punjabi. - meridiemParse: /ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'ਰਾਤ') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'ਸਵੇਰ') { - return hour; - } else if (meridiem === 'ਦੁਪਹਿਰ') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'ਸ਼ਾਮ') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ਰਾਤ'; - } else if (hour < 10) { - return 'ਸਵੇਰ'; - } else if (hour < 17) { - return 'ਦੁਪਹਿਰ'; - } else if (hour < 20) { - return 'ਸ਼ਾਮ'; - } else { - return 'ਰਾਤ'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var monthsNominative = 'styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień'.split('_'), - monthsSubjective = 'stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia'.split('_'); - function plural$3(n) { - return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1); - } - function translate$8(number, withoutSuffix, key) { - var result = number + ' '; - switch (key) { - case 'ss': - return result + (plural$3(number) ? 'sekundy' : 'sekund'); - case 'm': - return withoutSuffix ? 'minuta' : 'minutę'; - case 'mm': - return result + (plural$3(number) ? 'minuty' : 'minut'); - case 'h': - return withoutSuffix ? 'godzina' : 'godzinę'; - case 'hh': - return result + (plural$3(number) ? 'godziny' : 'godzin'); - case 'MM': - return result + (plural$3(number) ? 'miesiące' : 'miesięcy'); - case 'yy': - return result + (plural$3(number) ? 'lata' : 'lat'); - } - } - - hooks.defineLocale('pl', { - months : function (momentToFormat, format) { - if (!momentToFormat) { - return monthsNominative; - } else if (format === '') { - // Hack: if format empty we know this is used to generate - // RegExp by moment. Give then back both valid forms of months - // in RegExp ready format. - return '(' + monthsSubjective[momentToFormat.month()] + '|' + monthsNominative[momentToFormat.month()] + ')'; - } else if (/D MMMM/.test(format)) { - return monthsSubjective[momentToFormat.month()]; - } else { - return monthsNominative[momentToFormat.month()]; - } - }, - monthsShort : 'sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru'.split('_'), - weekdays : 'niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota'.split('_'), - weekdaysShort : 'ndz_pon_wt_śr_czw_pt_sob'.split('_'), - weekdaysMin : 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Dziś o] LT', - nextDay: '[Jutro o] LT', - nextWeek: function () { - switch (this.day()) { - case 0: - return '[W niedzielę o] LT'; - - case 2: - return '[We wtorek o] LT'; - - case 3: - return '[W środę o] LT'; - - case 6: - return '[W sobotę o] LT'; - - default: - return '[W] dddd [o] LT'; - } - }, - lastDay: '[Wczoraj o] LT', - lastWeek: function () { - switch (this.day()) { - case 0: - return '[W zeszłą niedzielę o] LT'; - case 3: - return '[W zeszłą środę o] LT'; - case 6: - return '[W zeszłą sobotę o] LT'; - default: - return '[W zeszły] dddd [o] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'za %s', - past : '%s temu', - s : 'kilka sekund', - ss : translate$8, - m : translate$8, - mm : translate$8, - h : translate$8, - hh : translate$8, - d : '1 dzień', - dd : '%d dni', - M : 'miesiąc', - MM : translate$8, - y : 'rok', - yy : translate$8 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('pt-br', { - months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'), - monthsShort : 'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'), - weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'), - weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), - weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY [às] HH:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY [às] HH:mm' - }, - calendar : { - sameDay: '[Hoje às] LT', - nextDay: '[Amanhã às] LT', - nextWeek: 'dddd [às] LT', - lastDay: '[Ontem às] LT', - lastWeek: function () { - return (this.day() === 0 || this.day() === 6) ? - '[Último] dddd [às] LT' : // Saturday + Sunday - '[Última] dddd [às] LT'; // Monday - Friday - }, - sameElse: 'L' - }, - relativeTime : { - future : 'em %s', - past : 'há %s', - s : 'poucos segundos', - ss : '%d segundos', - m : 'um minuto', - mm : '%d minutos', - h : 'uma hora', - hh : '%d horas', - d : 'um dia', - dd : '%d dias', - M : 'um mês', - MM : '%d meses', - y : 'um ano', - yy : '%d anos' - }, - dayOfMonthOrdinalParse: /\d{1,2}º/, - ordinal : '%dº' - }); - - //! moment.js locale configuration - - hooks.defineLocale('pt', { - months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'), - monthsShort : 'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'), - weekdays : 'Domingo_Segunda-feira_Terça-feira_Quarta-feira_Quinta-feira_Sexta-feira_Sábado'.split('_'), - weekdaysShort : 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'), - weekdaysMin : 'Do_2ª_3ª_4ª_5ª_6ª_Sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D [de] MMMM [de] YYYY', - LLL : 'D [de] MMMM [de] YYYY HH:mm', - LLLL : 'dddd, D [de] MMMM [de] YYYY HH:mm' - }, - calendar : { - sameDay: '[Hoje às] LT', - nextDay: '[Amanhã às] LT', - nextWeek: 'dddd [às] LT', - lastDay: '[Ontem às] LT', - lastWeek: function () { - return (this.day() === 0 || this.day() === 6) ? - '[Último] dddd [às] LT' : // Saturday + Sunday - '[Última] dddd [às] LT'; // Monday - Friday - }, - sameElse: 'L' - }, - relativeTime : { - future : 'em %s', - past : 'há %s', - s : 'segundos', - ss : '%d segundos', - m : 'um minuto', - mm : '%d minutos', - h : 'uma hora', - hh : '%d horas', - d : 'um dia', - dd : '%d dias', - M : 'um mês', - MM : '%d meses', - y : 'um ano', - yy : '%d anos' - }, - dayOfMonthOrdinalParse: /\d{1,2}º/, - ordinal : '%dº', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function relativeTimeWithPlural$2(number, withoutSuffix, key) { - var format = { - 'ss': 'secunde', - 'mm': 'minute', - 'hh': 'ore', - 'dd': 'zile', - 'MM': 'luni', - 'yy': 'ani' - }, - separator = ' '; - if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) { - separator = ' de '; - } - return number + separator + format[key]; - } - - hooks.defineLocale('ro', { - months : 'ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie'.split('_'), - monthsShort : 'ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'duminică_luni_marți_miercuri_joi_vineri_sâmbătă'.split('_'), - weekdaysShort : 'Dum_Lun_Mar_Mie_Joi_Vin_Sâm'.split('_'), - weekdaysMin : 'Du_Lu_Ma_Mi_Jo_Vi_Sâ'.split('_'), - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY H:mm', - LLLL : 'dddd, D MMMM YYYY H:mm' - }, - calendar : { - sameDay: '[azi la] LT', - nextDay: '[mâine la] LT', - nextWeek: 'dddd [la] LT', - lastDay: '[ieri la] LT', - lastWeek: '[fosta] dddd [la] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'peste %s', - past : '%s în urmă', - s : 'câteva secunde', - ss : relativeTimeWithPlural$2, - m : 'un minut', - mm : relativeTimeWithPlural$2, - h : 'o oră', - hh : relativeTimeWithPlural$2, - d : 'o zi', - dd : relativeTimeWithPlural$2, - M : 'o lună', - MM : relativeTimeWithPlural$2, - y : 'un an', - yy : relativeTimeWithPlural$2 - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function plural$4(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); - } - function relativeTimeWithPlural$3(number, withoutSuffix, key) { - var format = { - 'ss': withoutSuffix ? 'секунда_секунды_секунд' : 'секунду_секунды_секунд', - 'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут', - 'hh': 'час_часа_часов', - 'dd': 'день_дня_дней', - 'MM': 'месяц_месяца_месяцев', - 'yy': 'год_года_лет' - }; - if (key === 'm') { - return withoutSuffix ? 'минута' : 'минуту'; - } - else { - return number + ' ' + plural$4(format[key], +number); - } - } - var monthsParse$4 = [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[йя]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i]; - - // http://new.gramota.ru/spravka/rules/139-prop : § 103 - // Сокращения месяцев: http://new.gramota.ru/spravka/buro/search-answer?s=242637 - // CLDR data: http://www.unicode.org/cldr/charts/28/summary/ru.html#1753 - hooks.defineLocale('ru', { - months : { - format: 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_'), - standalone: 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_') - }, - monthsShort : { - // по CLDR именно "июл." и "июн.", но какой смысл менять букву на точку ? - format: 'янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.'.split('_'), - standalone: 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_') - }, - weekdays : { - standalone: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), - format: 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_'), - isFormat: /\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?\] ?dddd/ - }, - weekdaysShort : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), - weekdaysMin : 'вс_пн_вт_ср_чт_пт_сб'.split('_'), - monthsParse : monthsParse$4, - longMonthsParse : monthsParse$4, - shortMonthsParse : monthsParse$4, - - // полные названия с падежами, по три буквы, для некоторых, по 4 буквы, сокращения с точкой и без точки - monthsRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, - - // копия предыдущего - monthsShortRegex: /^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i, - - // полные названия с падежами - monthsStrictRegex: /^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i, - - // Выражение, которое соотвествует только сокращённым формам - monthsShortStrictRegex: /^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY г.', - LLL : 'D MMMM YYYY г., H:mm', - LLLL : 'dddd, D MMMM YYYY г., H:mm' - }, - calendar : { - sameDay: '[Сегодня, в] LT', - nextDay: '[Завтра, в] LT', - lastDay: '[Вчера, в] LT', - nextWeek: function (now) { - if (now.week() !== this.week()) { - switch (this.day()) { - case 0: - return '[В следующее] dddd, [в] LT'; - case 1: - case 2: - case 4: - return '[В следующий] dddd, [в] LT'; - case 3: - case 5: - case 6: - return '[В следующую] dddd, [в] LT'; - } - } else { - if (this.day() === 2) { - return '[Во] dddd, [в] LT'; - } else { - return '[В] dddd, [в] LT'; - } - } - }, - lastWeek: function (now) { - if (now.week() !== this.week()) { - switch (this.day()) { - case 0: - return '[В прошлое] dddd, [в] LT'; - case 1: - case 2: - case 4: - return '[В прошлый] dddd, [в] LT'; - case 3: - case 5: - case 6: - return '[В прошлую] dddd, [в] LT'; - } - } else { - if (this.day() === 2) { - return '[Во] dddd, [в] LT'; - } else { - return '[В] dddd, [в] LT'; - } - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'через %s', - past : '%s назад', - s : 'несколько секунд', - ss : relativeTimeWithPlural$3, - m : relativeTimeWithPlural$3, - mm : relativeTimeWithPlural$3, - h : 'час', - hh : relativeTimeWithPlural$3, - d : 'день', - dd : relativeTimeWithPlural$3, - M : 'месяц', - MM : relativeTimeWithPlural$3, - y : 'год', - yy : relativeTimeWithPlural$3 - }, - meridiemParse: /ночи|утра|дня|вечера/i, - isPM : function (input) { - return /^(дня|вечера)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночи'; - } else if (hour < 12) { - return 'утра'; - } else if (hour < 17) { - return 'дня'; - } else { - return 'вечера'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}-(й|го|я)/, - ordinal: function (number, period) { - switch (period) { - case 'M': - case 'd': - case 'DDD': - return number + '-й'; - case 'D': - return number + '-го'; - case 'w': - case 'W': - return number + '-я'; - default: - return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var months$6 = [ - 'جنوري', - 'فيبروري', - 'مارچ', - 'اپريل', - 'مئي', - 'جون', - 'جولاءِ', - 'آگسٽ', - 'سيپٽمبر', - 'آڪٽوبر', - 'نومبر', - 'ڊسمبر' - ]; - var days$1 = [ - 'آچر', - 'سومر', - 'اڱارو', - 'اربع', - 'خميس', - 'جمع', - 'ڇنڇر' - ]; - - hooks.defineLocale('sd', { - months : months$6, - monthsShort : months$6, - weekdays : days$1, - weekdaysShort : days$1, - weekdaysMin : days$1, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd، D MMMM YYYY HH:mm' - }, - meridiemParse: /صبح|شام/, - isPM : function (input) { - return 'شام' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'صبح'; - } - return 'شام'; - }, - calendar : { - sameDay : '[اڄ] LT', - nextDay : '[سڀاڻي] LT', - nextWeek : 'dddd [اڳين هفتي تي] LT', - lastDay : '[ڪالهه] LT', - lastWeek : '[گزريل هفتي] dddd [تي] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s پوء', - past : '%s اڳ', - s : 'چند سيڪنڊ', - ss : '%d سيڪنڊ', - m : 'هڪ منٽ', - mm : '%d منٽ', - h : 'هڪ ڪلاڪ', - hh : '%d ڪلاڪ', - d : 'هڪ ڏينهن', - dd : '%d ڏينهن', - M : 'هڪ مهينو', - MM : '%d مهينا', - y : 'هڪ سال', - yy : '%d سال' - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/,/g, '،'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('se', { - months : 'ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu'.split('_'), - monthsShort : 'ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov'.split('_'), - weekdays : 'sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat'.split('_'), - weekdaysShort : 'sotn_vuos_maŋ_gask_duor_bear_láv'.split('_'), - weekdaysMin : 's_v_m_g_d_b_L'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'MMMM D. [b.] YYYY', - LLL : 'MMMM D. [b.] YYYY [ti.] HH:mm', - LLLL : 'dddd, MMMM D. [b.] YYYY [ti.] HH:mm' - }, - calendar : { - sameDay: '[otne ti] LT', - nextDay: '[ihttin ti] LT', - nextWeek: 'dddd [ti] LT', - lastDay: '[ikte ti] LT', - lastWeek: '[ovddit] dddd [ti] LT', - sameElse: 'L' - }, - relativeTime : { - future : '%s geažes', - past : 'maŋit %s', - s : 'moadde sekunddat', - ss: '%d sekunddat', - m : 'okta minuhta', - mm : '%d minuhtat', - h : 'okta diimmu', - hh : '%d diimmut', - d : 'okta beaivi', - dd : '%d beaivvit', - M : 'okta mánnu', - MM : '%d mánut', - y : 'okta jahki', - yy : '%d jagit' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - /*jshint -W100*/ - hooks.defineLocale('si', { - months : 'ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්'.split('_'), - monthsShort : 'ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ'.split('_'), - weekdays : 'ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා'.split('_'), - weekdaysShort : 'ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන'.split('_'), - weekdaysMin : 'ඉ_ස_අ_බ_බ්‍ර_සි_සෙ'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'a h:mm', - LTS : 'a h:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY MMMM D', - LLL : 'YYYY MMMM D, a h:mm', - LLLL : 'YYYY MMMM D [වැනි] dddd, a h:mm:ss' - }, - calendar : { - sameDay : '[අද] LT[ට]', - nextDay : '[හෙට] LT[ට]', - nextWeek : 'dddd LT[ට]', - lastDay : '[ඊයේ] LT[ට]', - lastWeek : '[පසුගිය] dddd LT[ට]', - sameElse : 'L' - }, - relativeTime : { - future : '%sකින්', - past : '%sකට පෙර', - s : 'තත්පර කිහිපය', - ss : 'තත්පර %d', - m : 'මිනිත්තුව', - mm : 'මිනිත්තු %d', - h : 'පැය', - hh : 'පැය %d', - d : 'දිනය', - dd : 'දින %d', - M : 'මාසය', - MM : 'මාස %d', - y : 'වසර', - yy : 'වසර %d' - }, - dayOfMonthOrdinalParse: /\d{1,2} වැනි/, - ordinal : function (number) { - return number + ' වැනි'; - }, - meridiemParse : /පෙර වරු|පස් වරු|පෙ.ව|ප.ව./, - isPM : function (input) { - return input === 'ප.ව.' || input === 'පස් වරු'; - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'ප.ව.' : 'පස් වරු'; - } else { - return isLower ? 'පෙ.ව.' : 'පෙර වරු'; - } - } - }); - - //! moment.js locale configuration - - var months$7 = 'január_február_marec_apríl_máj_jún_júl_august_september_október_november_december'.split('_'), - monthsShort$5 = 'jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec'.split('_'); - function plural$5(n) { - return (n > 1) && (n < 5); - } - function translate$9(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { - case 's': // a few seconds / in a few seconds / a few seconds ago - return (withoutSuffix || isFuture) ? 'pár sekúnd' : 'pár sekundami'; - case 'ss': // 9 seconds / in 9 seconds / 9 seconds ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'sekundy' : 'sekúnd'); - } else { - return result + 'sekundami'; - } - break; - case 'm': // a minute / in a minute / a minute ago - return withoutSuffix ? 'minúta' : (isFuture ? 'minútu' : 'minútou'); - case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'minúty' : 'minút'); - } else { - return result + 'minútami'; - } - break; - case 'h': // an hour / in an hour / an hour ago - return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou'); - case 'hh': // 9 hours / in 9 hours / 9 hours ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'hodiny' : 'hodín'); - } else { - return result + 'hodinami'; - } - break; - case 'd': // a day / in a day / a day ago - return (withoutSuffix || isFuture) ? 'deň' : 'dňom'; - case 'dd': // 9 days / in 9 days / 9 days ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'dni' : 'dní'); - } else { - return result + 'dňami'; - } - break; - case 'M': // a month / in a month / a month ago - return (withoutSuffix || isFuture) ? 'mesiac' : 'mesiacom'; - case 'MM': // 9 months / in 9 months / 9 months ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'mesiace' : 'mesiacov'); - } else { - return result + 'mesiacmi'; - } - break; - case 'y': // a year / in a year / a year ago - return (withoutSuffix || isFuture) ? 'rok' : 'rokom'; - case 'yy': // 9 years / in 9 years / 9 years ago - if (withoutSuffix || isFuture) { - return result + (plural$5(number) ? 'roky' : 'rokov'); - } else { - return result + 'rokmi'; - } - break; - } - } - - hooks.defineLocale('sk', { - months : months$7, - monthsShort : monthsShort$5, - weekdays : 'nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota'.split('_'), - weekdaysShort : 'ne_po_ut_st_št_pi_so'.split('_'), - weekdaysMin : 'ne_po_ut_st_št_pi_so'.split('_'), - longDateFormat : { - LT: 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd D. MMMM YYYY H:mm' - }, - calendar : { - sameDay: '[dnes o] LT', - nextDay: '[zajtra o] LT', - nextWeek: function () { - switch (this.day()) { - case 0: - return '[v nedeľu o] LT'; - case 1: - case 2: - return '[v] dddd [o] LT'; - case 3: - return '[v stredu o] LT'; - case 4: - return '[vo štvrtok o] LT'; - case 5: - return '[v piatok o] LT'; - case 6: - return '[v sobotu o] LT'; - } - }, - lastDay: '[včera o] LT', - lastWeek: function () { - switch (this.day()) { - case 0: - return '[minulú nedeľu o] LT'; - case 1: - case 2: - return '[minulý] dddd [o] LT'; - case 3: - return '[minulú stredu o] LT'; - case 4: - case 5: - return '[minulý] dddd [o] LT'; - case 6: - return '[minulú sobotu o] LT'; - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'za %s', - past : 'pred %s', - s : translate$9, - ss : translate$9, - m : translate$9, - mm : translate$9, - h : translate$9, - hh : translate$9, - d : translate$9, - dd : translate$9, - M : translate$9, - MM : translate$9, - y : translate$9, - yy : translate$9 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - function processRelativeTime$6(number, withoutSuffix, key, isFuture) { - var result = number + ' '; - switch (key) { - case 's': - return withoutSuffix || isFuture ? 'nekaj sekund' : 'nekaj sekundami'; - case 'ss': - if (number === 1) { - result += withoutSuffix ? 'sekundo' : 'sekundi'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'sekundi' : 'sekundah'; - } else if (number < 5) { - result += withoutSuffix || isFuture ? 'sekunde' : 'sekundah'; - } else { - result += withoutSuffix || isFuture ? 'sekund' : 'sekund'; - } - return result; - case 'm': - return withoutSuffix ? 'ena minuta' : 'eno minuto'; - case 'mm': - if (number === 1) { - result += withoutSuffix ? 'minuta' : 'minuto'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'minuti' : 'minutama'; - } else if (number < 5) { - result += withoutSuffix || isFuture ? 'minute' : 'minutami'; - } else { - result += withoutSuffix || isFuture ? 'minut' : 'minutami'; - } - return result; - case 'h': - return withoutSuffix ? 'ena ura' : 'eno uro'; - case 'hh': - if (number === 1) { - result += withoutSuffix ? 'ura' : 'uro'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'uri' : 'urama'; - } else if (number < 5) { - result += withoutSuffix || isFuture ? 'ure' : 'urami'; - } else { - result += withoutSuffix || isFuture ? 'ur' : 'urami'; - } - return result; - case 'd': - return withoutSuffix || isFuture ? 'en dan' : 'enim dnem'; - case 'dd': - if (number === 1) { - result += withoutSuffix || isFuture ? 'dan' : 'dnem'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'dni' : 'dnevoma'; - } else { - result += withoutSuffix || isFuture ? 'dni' : 'dnevi'; - } - return result; - case 'M': - return withoutSuffix || isFuture ? 'en mesec' : 'enim mesecem'; - case 'MM': - if (number === 1) { - result += withoutSuffix || isFuture ? 'mesec' : 'mesecem'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'meseca' : 'mesecema'; - } else if (number < 5) { - result += withoutSuffix || isFuture ? 'mesece' : 'meseci'; - } else { - result += withoutSuffix || isFuture ? 'mesecev' : 'meseci'; - } - return result; - case 'y': - return withoutSuffix || isFuture ? 'eno leto' : 'enim letom'; - case 'yy': - if (number === 1) { - result += withoutSuffix || isFuture ? 'leto' : 'letom'; - } else if (number === 2) { - result += withoutSuffix || isFuture ? 'leti' : 'letoma'; - } else if (number < 5) { - result += withoutSuffix || isFuture ? 'leta' : 'leti'; - } else { - result += withoutSuffix || isFuture ? 'let' : 'leti'; - } - return result; - } - } - - hooks.defineLocale('sl', { - months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'), - monthsShort : 'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays : 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'), - weekdaysShort : 'ned._pon._tor._sre._čet._pet._sob.'.split('_'), - weekdaysMin : 'ne_po_to_sr_če_pe_so'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM YYYY', - LLL : 'D. MMMM YYYY H:mm', - LLLL : 'dddd, D. MMMM YYYY H:mm' - }, - calendar : { - sameDay : '[danes ob] LT', - nextDay : '[jutri ob] LT', - - nextWeek : function () { - switch (this.day()) { - case 0: - return '[v] [nedeljo] [ob] LT'; - case 3: - return '[v] [sredo] [ob] LT'; - case 6: - return '[v] [soboto] [ob] LT'; - case 1: - case 2: - case 4: - case 5: - return '[v] dddd [ob] LT'; - } - }, - lastDay : '[včeraj ob] LT', - lastWeek : function () { - switch (this.day()) { - case 0: - return '[prejšnjo] [nedeljo] [ob] LT'; - case 3: - return '[prejšnjo] [sredo] [ob] LT'; - case 6: - return '[prejšnjo] [soboto] [ob] LT'; - case 1: - case 2: - case 4: - case 5: - return '[prejšnji] dddd [ob] LT'; - } - }, - sameElse : 'L' - }, - relativeTime : { - future : 'čez %s', - past : 'pred %s', - s : processRelativeTime$6, - ss : processRelativeTime$6, - m : processRelativeTime$6, - mm : processRelativeTime$6, - h : processRelativeTime$6, - hh : processRelativeTime$6, - d : processRelativeTime$6, - dd : processRelativeTime$6, - M : processRelativeTime$6, - MM : processRelativeTime$6, - y : processRelativeTime$6, - yy : processRelativeTime$6 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('sq', { - months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'), - monthsShort : 'Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj'.split('_'), - weekdays : 'E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë'.split('_'), - weekdaysShort : 'Die_Hën_Mar_Mër_Enj_Pre_Sht'.split('_'), - weekdaysMin : 'D_H_Ma_Më_E_P_Sh'.split('_'), - weekdaysParseExact : true, - meridiemParse: /PD|MD/, - isPM: function (input) { - return input.charAt(0) === 'M'; - }, - meridiem : function (hours, minutes, isLower) { - return hours < 12 ? 'PD' : 'MD'; - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Sot në] LT', - nextDay : '[Nesër në] LT', - nextWeek : 'dddd [në] LT', - lastDay : '[Dje në] LT', - lastWeek : 'dddd [e kaluar në] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'në %s', - past : '%s më parë', - s : 'disa sekonda', - ss : '%d sekonda', - m : 'një minutë', - mm : '%d minuta', - h : 'një orë', - hh : '%d orë', - d : 'një ditë', - dd : '%d ditë', - M : 'një muaj', - MM : '%d muaj', - y : 'një vit', - yy : '%d vite' - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var translator$1 = { - words: { //Different grammatical cases - ss: ['секунда', 'секунде', 'секунди'], - m: ['један минут', 'једне минуте'], - mm: ['минут', 'минуте', 'минута'], - h: ['један сат', 'једног сата'], - hh: ['сат', 'сата', 'сати'], - dd: ['дан', 'дана', 'дана'], - MM: ['месец', 'месеца', 'месеци'], - yy: ['година', 'године', 'година'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator$1.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator$1.correctGrammaticalCase(number, wordKey); - } - } - }; - - hooks.defineLocale('sr-cyrl', { - months: 'јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар'.split('_'), - monthsShort: 'јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.'.split('_'), - monthsParseExact: true, - weekdays: 'недеља_понедељак_уторак_среда_четвртак_петак_субота'.split('_'), - weekdaysShort: 'нед._пон._уто._сре._чет._пет._суб.'.split('_'), - weekdaysMin: 'не_по_ут_ср_че_пе_су'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[данас у] LT', - nextDay: '[сутра у] LT', - nextWeek: function () { - switch (this.day()) { - case 0: - return '[у] [недељу] [у] LT'; - case 3: - return '[у] [среду] [у] LT'; - case 6: - return '[у] [суботу] [у] LT'; - case 1: - case 2: - case 4: - case 5: - return '[у] dddd [у] LT'; - } - }, - lastDay : '[јуче у] LT', - lastWeek : function () { - var lastWeekDays = [ - '[прошле] [недеље] [у] LT', - '[прошлог] [понедељка] [у] LT', - '[прошлог] [уторка] [у] LT', - '[прошле] [среде] [у] LT', - '[прошлог] [четвртка] [у] LT', - '[прошлог] [петка] [у] LT', - '[прошле] [суботе] [у] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'за %s', - past : 'пре %s', - s : 'неколико секунди', - ss : translator$1.translate, - m : translator$1.translate, - mm : translator$1.translate, - h : translator$1.translate, - hh : translator$1.translate, - d : 'дан', - dd : translator$1.translate, - M : 'месец', - MM : translator$1.translate, - y : 'годину', - yy : translator$1.translate - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var translator$2 = { - words: { //Different grammatical cases - ss: ['sekunda', 'sekunde', 'sekundi'], - m: ['jedan minut', 'jedne minute'], - mm: ['minut', 'minute', 'minuta'], - h: ['jedan sat', 'jednog sata'], - hh: ['sat', 'sata', 'sati'], - dd: ['dan', 'dana', 'dana'], - MM: ['mesec', 'meseca', 'meseci'], - yy: ['godina', 'godine', 'godina'] - }, - correctGrammaticalCase: function (number, wordKey) { - return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]); - }, - translate: function (number, withoutSuffix, key) { - var wordKey = translator$2.words[key]; - if (key.length === 1) { - return withoutSuffix ? wordKey[0] : wordKey[1]; - } else { - return number + ' ' + translator$2.correctGrammaticalCase(number, wordKey); - } - } - }; - - hooks.defineLocale('sr', { - months: 'januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar'.split('_'), - monthsShort: 'jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.'.split('_'), - monthsParseExact: true, - weekdays: 'nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota'.split('_'), - weekdaysShort: 'ned._pon._uto._sre._čet._pet._sub.'.split('_'), - weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'), - weekdaysParseExact : true, - longDateFormat: { - LT: 'H:mm', - LTS : 'H:mm:ss', - L: 'DD.MM.YYYY', - LL: 'D. MMMM YYYY', - LLL: 'D. MMMM YYYY H:mm', - LLLL: 'dddd, D. MMMM YYYY H:mm' - }, - calendar: { - sameDay: '[danas u] LT', - nextDay: '[sutra u] LT', - nextWeek: function () { - switch (this.day()) { - case 0: - return '[u] [nedelju] [u] LT'; - case 3: - return '[u] [sredu] [u] LT'; - case 6: - return '[u] [subotu] [u] LT'; - case 1: - case 2: - case 4: - case 5: - return '[u] dddd [u] LT'; - } - }, - lastDay : '[juče u] LT', - lastWeek : function () { - var lastWeekDays = [ - '[prošle] [nedelje] [u] LT', - '[prošlog] [ponedeljka] [u] LT', - '[prošlog] [utorka] [u] LT', - '[prošle] [srede] [u] LT', - '[prošlog] [četvrtka] [u] LT', - '[prošlog] [petka] [u] LT', - '[prošle] [subote] [u] LT' - ]; - return lastWeekDays[this.day()]; - }, - sameElse : 'L' - }, - relativeTime : { - future : 'za %s', - past : 'pre %s', - s : 'nekoliko sekundi', - ss : translator$2.translate, - m : translator$2.translate, - mm : translator$2.translate, - h : translator$2.translate, - hh : translator$2.translate, - d : 'dan', - dd : translator$2.translate, - M : 'mesec', - MM : translator$2.translate, - y : 'godinu', - yy : translator$2.translate - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('ss', { - months : "Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split('_'), - monthsShort : 'Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo'.split('_'), - weekdays : 'Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo'.split('_'), - weekdaysShort : 'Lis_Umb_Lsb_Les_Lsi_Lsh_Umg'.split('_'), - weekdaysMin : 'Li_Us_Lb_Lt_Ls_Lh_Ug'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Namuhla nga] LT', - nextDay : '[Kusasa nga] LT', - nextWeek : 'dddd [nga] LT', - lastDay : '[Itolo nga] LT', - lastWeek : 'dddd [leliphelile] [nga] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'nga %s', - past : 'wenteka nga %s', - s : 'emizuzwana lomcane', - ss : '%d mzuzwana', - m : 'umzuzu', - mm : '%d emizuzu', - h : 'lihora', - hh : '%d emahora', - d : 'lilanga', - dd : '%d emalanga', - M : 'inyanga', - MM : '%d tinyanga', - y : 'umnyaka', - yy : '%d iminyaka' - }, - meridiemParse: /ekuseni|emini|entsambama|ebusuku/, - meridiem : function (hours, minutes, isLower) { - if (hours < 11) { - return 'ekuseni'; - } else if (hours < 15) { - return 'emini'; - } else if (hours < 19) { - return 'entsambama'; - } else { - return 'ebusuku'; - } - }, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'ekuseni') { - return hour; - } else if (meridiem === 'emini') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'entsambama' || meridiem === 'ebusuku') { - if (hour === 0) { - return 0; - } - return hour + 12; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}/, - ordinal : '%d', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('sv', { - months : 'januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'), - weekdays : 'söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag'.split('_'), - weekdaysShort : 'sön_mån_tis_ons_tor_fre_lör'.split('_'), - weekdaysMin : 'sö_må_ti_on_to_fr_lö'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY-MM-DD', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY [kl.] HH:mm', - LLLL : 'dddd D MMMM YYYY [kl.] HH:mm', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd D MMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Idag] LT', - nextDay: '[Imorgon] LT', - lastDay: '[Igår] LT', - nextWeek: '[På] dddd LT', - lastWeek: '[I] dddd[s] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'om %s', - past : 'för %s sedan', - s : 'några sekunder', - ss : '%d sekunder', - m : 'en minut', - mm : '%d minuter', - h : 'en timme', - hh : '%d timmar', - d : 'en dag', - dd : '%d dagar', - M : 'en månad', - MM : '%d månader', - y : 'ett år', - yy : '%d år' - }, - dayOfMonthOrdinalParse: /\d{1,2}(e|a)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'e' : - (b === 1) ? 'a' : - (b === 2) ? 'a' : - (b === 3) ? 'e' : 'e'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('sw', { - months : 'Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba'.split('_'), - monthsShort : 'Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des'.split('_'), - weekdays : 'Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi'.split('_'), - weekdaysShort : 'Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos'.split('_'), - weekdaysMin : 'J2_J3_J4_J5_Al_Ij_J1'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[leo saa] LT', - nextDay : '[kesho saa] LT', - nextWeek : '[wiki ijayo] dddd [saat] LT', - lastDay : '[jana] LT', - lastWeek : '[wiki iliyopita] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s baadaye', - past : 'tokea %s', - s : 'hivi punde', - ss : 'sekunde %d', - m : 'dakika moja', - mm : 'dakika %d', - h : 'saa limoja', - hh : 'masaa %d', - d : 'siku moja', - dd : 'masiku %d', - M : 'mwezi mmoja', - MM : 'miezi %d', - y : 'mwaka mmoja', - yy : 'miaka %d' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var symbolMap$e = { - '1': '௧', - '2': '௨', - '3': '௩', - '4': '௪', - '5': '௫', - '6': '௬', - '7': '௭', - '8': '௮', - '9': '௯', - '0': '௦' - }, numberMap$d = { - '௧': '1', - '௨': '2', - '௩': '3', - '௪': '4', - '௫': '5', - '௬': '6', - '௭': '7', - '௮': '8', - '௯': '9', - '௦': '0' - }; - - hooks.defineLocale('ta', { - months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), - monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split('_'), - weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split('_'), - weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split('_'), - weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, HH:mm', - LLLL : 'dddd, D MMMM YYYY, HH:mm' - }, - calendar : { - sameDay : '[இன்று] LT', - nextDay : '[நாளை] LT', - nextWeek : 'dddd, LT', - lastDay : '[நேற்று] LT', - lastWeek : '[கடந்த வாரம்] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s இல்', - past : '%s முன்', - s : 'ஒரு சில விநாடிகள்', - ss : '%d விநாடிகள்', - m : 'ஒரு நிமிடம்', - mm : '%d நிமிடங்கள்', - h : 'ஒரு மணி நேரம்', - hh : '%d மணி நேரம்', - d : 'ஒரு நாள்', - dd : '%d நாட்கள்', - M : 'ஒரு மாதம்', - MM : '%d மாதங்கள்', - y : 'ஒரு வருடம்', - yy : '%d ஆண்டுகள்' - }, - dayOfMonthOrdinalParse: /\d{1,2}வது/, - ordinal : function (number) { - return number + 'வது'; - }, - preparse: function (string) { - return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) { - return numberMap$d[match]; - }); - }, - postformat: function (string) { - return string.replace(/\d/g, function (match) { - return symbolMap$e[match]; - }); - }, - // refer http://ta.wikipedia.org/s/1er1 - meridiemParse: /யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/, - meridiem : function (hour, minute, isLower) { - if (hour < 2) { - return ' யாமம்'; - } else if (hour < 6) { - return ' வைகறை'; // வைகறை - } else if (hour < 10) { - return ' காலை'; // காலை - } else if (hour < 14) { - return ' நண்பகல்'; // நண்பகல் - } else if (hour < 18) { - return ' எற்பாடு'; // எற்பாடு - } else if (hour < 22) { - return ' மாலை'; // மாலை - } else { - return ' யாமம்'; - } - }, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'யாமம்') { - return hour < 2 ? hour : hour + 12; - } else if (meridiem === 'வைகறை' || meridiem === 'காலை') { - return hour; - } else if (meridiem === 'நண்பகல்') { - return hour >= 10 ? hour : hour + 12; - } else { - return hour + 12; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('te', { - months : 'జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జూలై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్'.split('_'), - monthsShort : 'జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జూలై_ఆగ._సెప్._అక్టో._నవ._డిసె.'.split('_'), - monthsParseExact : true, - weekdays : 'ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం'.split('_'), - weekdaysShort : 'ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని'.split('_'), - weekdaysMin : 'ఆ_సో_మం_బు_గు_శు_శ'.split('_'), - longDateFormat : { - LT : 'A h:mm', - LTS : 'A h:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY, A h:mm', - LLLL : 'dddd, D MMMM YYYY, A h:mm' - }, - calendar : { - sameDay : '[నేడు] LT', - nextDay : '[రేపు] LT', - nextWeek : 'dddd, LT', - lastDay : '[నిన్న] LT', - lastWeek : '[గత] dddd, LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s లో', - past : '%s క్రితం', - s : 'కొన్ని క్షణాలు', - ss : '%d సెకన్లు', - m : 'ఒక నిమిషం', - mm : '%d నిమిషాలు', - h : 'ఒక గంట', - hh : '%d గంటలు', - d : 'ఒక రోజు', - dd : '%d రోజులు', - M : 'ఒక నెల', - MM : '%d నెలలు', - y : 'ఒక సంవత్సరం', - yy : '%d సంవత్సరాలు' - }, - dayOfMonthOrdinalParse : /\d{1,2}వ/, - ordinal : '%dవ', - meridiemParse: /రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'రాత్రి') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'ఉదయం') { - return hour; - } else if (meridiem === 'మధ్యాహ్నం') { - return hour >= 10 ? hour : hour + 12; - } else if (meridiem === 'సాయంత్రం') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'రాత్రి'; - } else if (hour < 10) { - return 'ఉదయం'; - } else if (hour < 17) { - return 'మధ్యాహ్నం'; - } else if (hour < 20) { - return 'సాయంత్రం'; - } else { - return 'రాత్రి'; - } - }, - week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('tet', { - months : 'Janeiru_Fevereiru_Marsu_Abril_Maiu_Juñu_Jullu_Agustu_Setembru_Outubru_Novembru_Dezembru'.split('_'), - monthsShort : 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'), - weekdays : 'Domingu_Segunda_Tersa_Kuarta_Kinta_Sesta_Sabadu'.split('_'), - weekdaysShort : 'Dom_Seg_Ters_Kua_Kint_Sest_Sab'.split('_'), - weekdaysMin : 'Do_Seg_Te_Ku_Ki_Ses_Sa'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Ohin iha] LT', - nextDay: '[Aban iha] LT', - nextWeek: 'dddd [iha] LT', - lastDay: '[Horiseik iha] LT', - lastWeek: 'dddd [semana kotuk] [iha] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'iha %s', - past : '%s liuba', - s : 'minutu balun', - ss : 'minutu %d', - m : 'minutu ida', - mm : 'minutu %d', - h : 'oras ida', - hh : 'oras %d', - d : 'loron ida', - dd : 'loron %d', - M : 'fulan ida', - MM : 'fulan %d', - y : 'tinan ida', - yy : 'tinan %d' - }, - dayOfMonthOrdinalParse: /\d{1,2}(st|nd|rd|th)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var suffixes$3 = { - 0: '-ум', - 1: '-ум', - 2: '-юм', - 3: '-юм', - 4: '-ум', - 5: '-ум', - 6: '-ум', - 7: '-ум', - 8: '-ум', - 9: '-ум', - 10: '-ум', - 12: '-ум', - 13: '-ум', - 20: '-ум', - 30: '-юм', - 40: '-ум', - 50: '-ум', - 60: '-ум', - 70: '-ум', - 80: '-ум', - 90: '-ум', - 100: '-ум' - }; - - hooks.defineLocale('tg', { - months : 'январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр'.split('_'), - monthsShort : 'янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек'.split('_'), - weekdays : 'якшанбе_душанбе_сешанбе_чоршанбе_панҷшанбе_ҷумъа_шанбе'.split('_'), - weekdaysShort : 'яшб_дшб_сшб_чшб_пшб_ҷум_шнб'.split('_'), - weekdaysMin : 'яш_дш_сш_чш_пш_ҷм_шб'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[Имрӯз соати] LT', - nextDay : '[Пагоҳ соати] LT', - lastDay : '[Дирӯз соати] LT', - nextWeek : 'dddd[и] [ҳафтаи оянда соати] LT', - lastWeek : 'dddd[и] [ҳафтаи гузашта соати] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'баъди %s', - past : '%s пеш', - s : 'якчанд сония', - m : 'як дақиқа', - mm : '%d дақиқа', - h : 'як соат', - hh : '%d соат', - d : 'як рӯз', - dd : '%d рӯз', - M : 'як моҳ', - MM : '%d моҳ', - y : 'як сол', - yy : '%d сол' - }, - meridiemParse: /шаб|субҳ|рӯз|бегоҳ/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === 'шаб') { - return hour < 4 ? hour : hour + 12; - } else if (meridiem === 'субҳ') { - return hour; - } else if (meridiem === 'рӯз') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === 'бегоҳ') { - return hour + 12; - } - }, - meridiem: function (hour, minute, isLower) { - if (hour < 4) { - return 'шаб'; - } else if (hour < 11) { - return 'субҳ'; - } else if (hour < 16) { - return 'рӯз'; - } else if (hour < 19) { - return 'бегоҳ'; - } else { - return 'шаб'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}-(ум|юм)/, - ordinal: function (number) { - var a = number % 10, - b = number >= 100 ? 100 : null; - return number + (suffixes$3[number] || suffixes$3[a] || suffixes$3[b]); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('th', { - months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'), - monthsShort : 'ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.'.split('_'), - monthsParseExact: true, - weekdays : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์'.split('_'), - weekdaysShort : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์'.split('_'), // yes, three characters difference - weekdaysMin : 'อา._จ._อ._พ._พฤ._ศ._ส.'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'H:mm', - LTS : 'H:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY เวลา H:mm', - LLLL : 'วันddddที่ D MMMM YYYY เวลา H:mm' - }, - meridiemParse: /ก่อนเที่ยง|หลังเที่ยง/, - isPM: function (input) { - return input === 'หลังเที่ยง'; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'ก่อนเที่ยง'; - } else { - return 'หลังเที่ยง'; - } - }, - calendar : { - sameDay : '[วันนี้ เวลา] LT', - nextDay : '[พรุ่งนี้ เวลา] LT', - nextWeek : 'dddd[หน้า เวลา] LT', - lastDay : '[เมื่อวานนี้ เวลา] LT', - lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'อีก %s', - past : '%sที่แล้ว', - s : 'ไม่กี่วินาที', - ss : '%d วินาที', - m : '1 นาที', - mm : '%d นาที', - h : '1 ชั่วโมง', - hh : '%d ชั่วโมง', - d : '1 วัน', - dd : '%d วัน', - M : '1 เดือน', - MM : '%d เดือน', - y : '1 ปี', - yy : '%d ปี' - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('tl-ph', { - months : 'Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre'.split('_'), - monthsShort : 'Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis'.split('_'), - weekdays : 'Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado'.split('_'), - weekdaysShort : 'Lin_Lun_Mar_Miy_Huw_Biy_Sab'.split('_'), - weekdaysMin : 'Li_Lu_Ma_Mi_Hu_Bi_Sab'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'MM/D/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY HH:mm', - LLLL : 'dddd, MMMM DD, YYYY HH:mm' - }, - calendar : { - sameDay: 'LT [ngayong araw]', - nextDay: '[Bukas ng] LT', - nextWeek: 'LT [sa susunod na] dddd', - lastDay: 'LT [kahapon]', - lastWeek: 'LT [noong nakaraang] dddd', - sameElse: 'L' - }, - relativeTime : { - future : 'sa loob ng %s', - past : '%s ang nakalipas', - s : 'ilang segundo', - ss : '%d segundo', - m : 'isang minuto', - mm : '%d minuto', - h : 'isang oras', - hh : '%d oras', - d : 'isang araw', - dd : '%d araw', - M : 'isang buwan', - MM : '%d buwan', - y : 'isang taon', - yy : '%d taon' - }, - dayOfMonthOrdinalParse: /\d{1,2}/, - ordinal : function (number) { - return number; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - var numbersNouns = 'pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut'.split('_'); - - function translateFuture(output) { - var time = output; - time = (output.indexOf('jaj') !== -1) ? - time.slice(0, -3) + 'leS' : - (output.indexOf('jar') !== -1) ? - time.slice(0, -3) + 'waQ' : - (output.indexOf('DIS') !== -1) ? - time.slice(0, -3) + 'nem' : - time + ' pIq'; - return time; - } - - function translatePast(output) { - var time = output; - time = (output.indexOf('jaj') !== -1) ? - time.slice(0, -3) + 'Hu’' : - (output.indexOf('jar') !== -1) ? - time.slice(0, -3) + 'wen' : - (output.indexOf('DIS') !== -1) ? - time.slice(0, -3) + 'ben' : - time + ' ret'; - return time; - } - - function translate$a(number, withoutSuffix, string, isFuture) { - var numberNoun = numberAsNoun(number); - switch (string) { - case 'ss': - return numberNoun + ' lup'; - case 'mm': - return numberNoun + ' tup'; - case 'hh': - return numberNoun + ' rep'; - case 'dd': - return numberNoun + ' jaj'; - case 'MM': - return numberNoun + ' jar'; - case 'yy': - return numberNoun + ' DIS'; - } - } - - function numberAsNoun(number) { - var hundred = Math.floor((number % 1000) / 100), - ten = Math.floor((number % 100) / 10), - one = number % 10, - word = ''; - if (hundred > 0) { - word += numbersNouns[hundred] + 'vatlh'; - } - if (ten > 0) { - word += ((word !== '') ? ' ' : '') + numbersNouns[ten] + 'maH'; - } - if (one > 0) { - word += ((word !== '') ? ' ' : '') + numbersNouns[one]; - } - return (word === '') ? 'pagh' : word; - } - - hooks.defineLocale('tlh', { - months : 'tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’'.split('_'), - monthsShort : 'jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’'.split('_'), - monthsParseExact : true, - weekdays : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - weekdaysShort : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - weekdaysMin : 'lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[DaHjaj] LT', - nextDay: '[wa’leS] LT', - nextWeek: 'LLL', - lastDay: '[wa’Hu’] LT', - lastWeek: 'LLL', - sameElse: 'L' - }, - relativeTime : { - future : translateFuture, - past : translatePast, - s : 'puS lup', - ss : translate$a, - m : 'wa’ tup', - mm : translate$a, - h : 'wa’ rep', - hh : translate$a, - d : 'wa’ jaj', - dd : translate$a, - M : 'wa’ jar', - MM : translate$a, - y : 'wa’ DIS', - yy : translate$a - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - var suffixes$4 = { - 1: '\'inci', - 5: '\'inci', - 8: '\'inci', - 70: '\'inci', - 80: '\'inci', - 2: '\'nci', - 7: '\'nci', - 20: '\'nci', - 50: '\'nci', - 3: '\'üncü', - 4: '\'üncü', - 100: '\'üncü', - 6: '\'ncı', - 9: '\'uncu', - 10: '\'uncu', - 30: '\'uncu', - 60: '\'ıncı', - 90: '\'ıncı' - }; - - hooks.defineLocale('tr', { - months : 'Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık'.split('_'), - monthsShort : 'Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara'.split('_'), - weekdays : 'Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi'.split('_'), - weekdaysShort : 'Paz_Pts_Sal_Çar_Per_Cum_Cts'.split('_'), - weekdaysMin : 'Pz_Pt_Sa_Ça_Pe_Cu_Ct'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[bugün saat] LT', - nextDay : '[yarın saat] LT', - nextWeek : '[gelecek] dddd [saat] LT', - lastDay : '[dün] LT', - lastWeek : '[geçen] dddd [saat] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s sonra', - past : '%s önce', - s : 'birkaç saniye', - ss : '%d saniye', - m : 'bir dakika', - mm : '%d dakika', - h : 'bir saat', - hh : '%d saat', - d : 'bir gün', - dd : '%d gün', - M : 'bir ay', - MM : '%d ay', - y : 'bir yıl', - yy : '%d yıl' - }, - ordinal: function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'Do': - case 'DD': - return number; - default: - if (number === 0) { // special case for zero - return number + '\'ıncı'; - } - var a = number % 10, - b = number % 100 - a, - c = number >= 100 ? 100 : null; - return number + (suffixes$4[a] || suffixes$4[b] || suffixes$4[c]); - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - // After the year there should be a slash and the amount of years since December 26, 1979 in Roman numerals. - // This is currently too difficult (maybe even impossible) to add. - hooks.defineLocale('tzl', { - months : 'Januar_Fevraglh_Març_Avrïu_Mai_Gün_Julia_Guscht_Setemvar_Listopäts_Noemvar_Zecemvar'.split('_'), - monthsShort : 'Jan_Fev_Mar_Avr_Mai_Gün_Jul_Gus_Set_Lis_Noe_Zec'.split('_'), - weekdays : 'Súladi_Lúneçi_Maitzi_Márcuri_Xhúadi_Viénerçi_Sáturi'.split('_'), - weekdaysShort : 'Súl_Lún_Mai_Már_Xhú_Vié_Sát'.split('_'), - weekdaysMin : 'Sú_Lú_Ma_Má_Xh_Vi_Sá'.split('_'), - longDateFormat : { - LT : 'HH.mm', - LTS : 'HH.mm.ss', - L : 'DD.MM.YYYY', - LL : 'D. MMMM [dallas] YYYY', - LLL : 'D. MMMM [dallas] YYYY HH.mm', - LLLL : 'dddd, [li] D. MMMM [dallas] YYYY HH.mm' - }, - meridiemParse: /d\'o|d\'a/i, - isPM : function (input) { - return 'd\'o' === input.toLowerCase(); - }, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'd\'o' : 'D\'O'; - } else { - return isLower ? 'd\'a' : 'D\'A'; - } - }, - calendar : { - sameDay : '[oxhi à] LT', - nextDay : '[demà à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[ieiri à] LT', - lastWeek : '[sür el] dddd [lasteu à] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'osprei %s', - past : 'ja%s', - s : processRelativeTime$7, - ss : processRelativeTime$7, - m : processRelativeTime$7, - mm : processRelativeTime$7, - h : processRelativeTime$7, - hh : processRelativeTime$7, - d : processRelativeTime$7, - dd : processRelativeTime$7, - M : processRelativeTime$7, - MM : processRelativeTime$7, - y : processRelativeTime$7, - yy : processRelativeTime$7 - }, - dayOfMonthOrdinalParse: /\d{1,2}\./, - ordinal : '%d.', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - function processRelativeTime$7(number, withoutSuffix, key, isFuture) { - var format = { - 's': ['viensas secunds', '\'iensas secunds'], - 'ss': [number + ' secunds', '' + number + ' secunds'], - 'm': ['\'n míut', '\'iens míut'], - 'mm': [number + ' míuts', '' + number + ' míuts'], - 'h': ['\'n þora', '\'iensa þora'], - 'hh': [number + ' þoras', '' + number + ' þoras'], - 'd': ['\'n ziua', '\'iensa ziua'], - 'dd': [number + ' ziuas', '' + number + ' ziuas'], - 'M': ['\'n mes', '\'iens mes'], - 'MM': [number + ' mesen', '' + number + ' mesen'], - 'y': ['\'n ar', '\'iens ar'], - 'yy': [number + ' ars', '' + number + ' ars'] - }; - return isFuture ? format[key][0] : (withoutSuffix ? format[key][0] : format[key][1]); - } - - //! moment.js locale configuration - - hooks.defineLocale('tzm-latn', { - months : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), - monthsShort : 'innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir'.split('_'), - weekdays : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - weekdaysShort : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - weekdaysMin : 'asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[asdkh g] LT', - nextDay: '[aska g] LT', - nextWeek: 'dddd [g] LT', - lastDay: '[assant g] LT', - lastWeek: 'dddd [g] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'dadkh s yan %s', - past : 'yan %s', - s : 'imik', - ss : '%d imik', - m : 'minuḍ', - mm : '%d minuḍ', - h : 'saɛa', - hh : '%d tassaɛin', - d : 'ass', - dd : '%d ossan', - M : 'ayowr', - MM : '%d iyyirn', - y : 'asgas', - yy : '%d isgasn' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('tzm', { - months : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), - monthsShort : 'ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ'.split('_'), - weekdays : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - weekdaysShort : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - weekdaysMin : 'ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS: 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' - }, - calendar : { - sameDay: '[ⴰⵙⴷⵅ ⴴ] LT', - nextDay: '[ⴰⵙⴽⴰ ⴴ] LT', - nextWeek: 'dddd [ⴴ] LT', - lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT', - lastWeek: 'dddd [ⴴ] LT', - sameElse: 'L' - }, - relativeTime : { - future : 'ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s', - past : 'ⵢⴰⵏ %s', - s : 'ⵉⵎⵉⴽ', - ss : '%d ⵉⵎⵉⴽ', - m : 'ⵎⵉⵏⵓⴺ', - mm : '%d ⵎⵉⵏⵓⴺ', - h : 'ⵙⴰⵄⴰ', - hh : '%d ⵜⴰⵙⵙⴰⵄⵉⵏ', - d : 'ⴰⵙⵙ', - dd : '%d oⵙⵙⴰⵏ', - M : 'ⴰⵢoⵓⵔ', - MM : '%d ⵉⵢⵢⵉⵔⵏ', - y : 'ⴰⵙⴳⴰⵙ', - yy : '%d ⵉⵙⴳⴰⵙⵏ' - }, - week : { - dow : 6, // Saturday is the first day of the week. - doy : 12 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js language configuration - - hooks.defineLocale('ug-cn', { - months: 'يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر'.split( - '_' - ), - monthsShort: 'يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر'.split( - '_' - ), - weekdays: 'يەكشەنبە_دۈشەنبە_سەيشەنبە_چارشەنبە_پەيشەنبە_جۈمە_شەنبە'.split( - '_' - ), - weekdaysShort: 'يە_دۈ_سە_چا_پە_جۈ_شە'.split('_'), - weekdaysMin: 'يە_دۈ_سە_چا_پە_جۈ_شە'.split('_'), - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'YYYY-MM-DD', - LL: 'YYYY-يىلىM-ئاينىڭD-كۈنى', - LLL: 'YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm', - LLLL: 'dddd، YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm' - }, - meridiemParse: /يېرىم كېچە|سەھەر|چۈشتىن بۇرۇن|چۈش|چۈشتىن كېيىن|كەچ/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if ( - meridiem === 'يېرىم كېچە' || - meridiem === 'سەھەر' || - meridiem === 'چۈشتىن بۇرۇن' - ) { - return hour; - } else if (meridiem === 'چۈشتىن كېيىن' || meridiem === 'كەچ') { - return hour + 12; - } else { - return hour >= 11 ? hour : hour + 12; - } - }, - meridiem: function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 600) { - return 'يېرىم كېچە'; - } else if (hm < 900) { - return 'سەھەر'; - } else if (hm < 1130) { - return 'چۈشتىن بۇرۇن'; - } else if (hm < 1230) { - return 'چۈش'; - } else if (hm < 1800) { - return 'چۈشتىن كېيىن'; - } else { - return 'كەچ'; - } - }, - calendar: { - sameDay: '[بۈگۈن سائەت] LT', - nextDay: '[ئەتە سائەت] LT', - nextWeek: '[كېلەركى] dddd [سائەت] LT', - lastDay: '[تۆنۈگۈن] LT', - lastWeek: '[ئالدىنقى] dddd [سائەت] LT', - sameElse: 'L' - }, - relativeTime: { - future: '%s كېيىن', - past: '%s بۇرۇن', - s: 'نەچچە سېكونت', - ss: '%d سېكونت', - m: 'بىر مىنۇت', - mm: '%d مىنۇت', - h: 'بىر سائەت', - hh: '%d سائەت', - d: 'بىر كۈن', - dd: '%d كۈن', - M: 'بىر ئاي', - MM: '%d ئاي', - y: 'بىر يىل', - yy: '%d يىل' - }, - - dayOfMonthOrdinalParse: /\d{1,2}(-كۈنى|-ئاي|-ھەپتە)/, - ordinal: function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'DDD': - return number + '-كۈنى'; - case 'w': - case 'W': - return number + '-ھەپتە'; - default: - return number; - } - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/,/g, '،'); - }, - week: { - // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 - dow: 1, // Monday is the first day of the week. - doy: 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - function plural$6(word, num) { - var forms = word.split('_'); - return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); - } - function relativeTimeWithPlural$4(number, withoutSuffix, key) { - var format = { - 'ss': withoutSuffix ? 'секунда_секунди_секунд' : 'секунду_секунди_секунд', - 'mm': withoutSuffix ? 'хвилина_хвилини_хвилин' : 'хвилину_хвилини_хвилин', - 'hh': withoutSuffix ? 'година_години_годин' : 'годину_години_годин', - 'dd': 'день_дні_днів', - 'MM': 'місяць_місяці_місяців', - 'yy': 'рік_роки_років' - }; - if (key === 'm') { - return withoutSuffix ? 'хвилина' : 'хвилину'; - } - else if (key === 'h') { - return withoutSuffix ? 'година' : 'годину'; - } - else { - return number + ' ' + plural$6(format[key], +number); - } - } - function weekdaysCaseReplace(m, format) { - var weekdays = { - 'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'), - 'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'), - 'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_') - }; - - if (!m) { - return weekdays['nominative']; - } - - var nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ? - 'accusative' : - ((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ? - 'genitive' : - 'nominative'); - return weekdays[nounCase][m.day()]; - } - function processHoursFunction(str) { - return function () { - return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT'; - }; - } - - hooks.defineLocale('uk', { - months : { - 'format': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_'), - 'standalone': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_') - }, - monthsShort : 'січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд'.split('_'), - weekdays : weekdaysCaseReplace, - weekdaysShort : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - weekdaysMin : 'нд_пн_вт_ср_чт_пт_сб'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD.MM.YYYY', - LL : 'D MMMM YYYY р.', - LLL : 'D MMMM YYYY р., HH:mm', - LLLL : 'dddd, D MMMM YYYY р., HH:mm' - }, - calendar : { - sameDay: processHoursFunction('[Сьогодні '), - nextDay: processHoursFunction('[Завтра '), - lastDay: processHoursFunction('[Вчора '), - nextWeek: processHoursFunction('[У] dddd ['), - lastWeek: function () { - switch (this.day()) { - case 0: - case 3: - case 5: - case 6: - return processHoursFunction('[Минулої] dddd [').call(this); - case 1: - case 2: - case 4: - return processHoursFunction('[Минулого] dddd [').call(this); - } - }, - sameElse: 'L' - }, - relativeTime : { - future : 'за %s', - past : '%s тому', - s : 'декілька секунд', - ss : relativeTimeWithPlural$4, - m : relativeTimeWithPlural$4, - mm : relativeTimeWithPlural$4, - h : 'годину', - hh : relativeTimeWithPlural$4, - d : 'день', - dd : relativeTimeWithPlural$4, - M : 'місяць', - MM : relativeTimeWithPlural$4, - y : 'рік', - yy : relativeTimeWithPlural$4 - }, - // M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason - meridiemParse: /ночі|ранку|дня|вечора/, - isPM: function (input) { - return /^(дня|вечора)$/.test(input); - }, - meridiem : function (hour, minute, isLower) { - if (hour < 4) { - return 'ночі'; - } else if (hour < 12) { - return 'ранку'; - } else if (hour < 17) { - return 'дня'; - } else { - return 'вечора'; - } - }, - dayOfMonthOrdinalParse: /\d{1,2}-(й|го)/, - ordinal: function (number, period) { - switch (period) { - case 'M': - case 'd': - case 'DDD': - case 'w': - case 'W': - return number + '-й'; - case 'D': - return number + '-го'; - default: - return number; - } - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - var months$8 = [ - 'جنوری', - 'فروری', - 'مارچ', - 'اپریل', - 'مئی', - 'جون', - 'جولائی', - 'اگست', - 'ستمبر', - 'اکتوبر', - 'نومبر', - 'دسمبر' - ]; - var days$2 = [ - 'اتوار', - 'پیر', - 'منگل', - 'بدھ', - 'جمعرات', - 'جمعہ', - 'ہفتہ' - ]; - - hooks.defineLocale('ur', { - months : months$8, - monthsShort : months$8, - weekdays : days$2, - weekdaysShort : days$2, - weekdaysMin : days$2, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd، D MMMM YYYY HH:mm' - }, - meridiemParse: /صبح|شام/, - isPM : function (input) { - return 'شام' === input; - }, - meridiem : function (hour, minute, isLower) { - if (hour < 12) { - return 'صبح'; - } - return 'شام'; - }, - calendar : { - sameDay : '[آج بوقت] LT', - nextDay : '[کل بوقت] LT', - nextWeek : 'dddd [بوقت] LT', - lastDay : '[گذشتہ روز بوقت] LT', - lastWeek : '[گذشتہ] dddd [بوقت] LT', - sameElse : 'L' - }, - relativeTime : { - future : '%s بعد', - past : '%s قبل', - s : 'چند سیکنڈ', - ss : '%d سیکنڈ', - m : 'ایک منٹ', - mm : '%d منٹ', - h : 'ایک گھنٹہ', - hh : '%d گھنٹے', - d : 'ایک دن', - dd : '%d دن', - M : 'ایک ماہ', - MM : '%d ماہ', - y : 'ایک سال', - yy : '%d سال' - }, - preparse: function (string) { - return string.replace(/،/g, ','); - }, - postformat: function (string) { - return string.replace(/,/g, '،'); - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('uz-latn', { - months : 'Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr'.split('_'), - monthsShort : 'Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek'.split('_'), - weekdays : 'Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba'.split('_'), - weekdaysShort : 'Yak_Dush_Sesh_Chor_Pay_Jum_Shan'.split('_'), - weekdaysMin : 'Ya_Du_Se_Cho_Pa_Ju_Sha'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'D MMMM YYYY, dddd HH:mm' - }, - calendar : { - sameDay : '[Bugun soat] LT [da]', - nextDay : '[Ertaga] LT [da]', - nextWeek : 'dddd [kuni soat] LT [da]', - lastDay : '[Kecha soat] LT [da]', - lastWeek : '[O\'tgan] dddd [kuni soat] LT [da]', - sameElse : 'L' - }, - relativeTime : { - future : 'Yaqin %s ichida', - past : 'Bir necha %s oldin', - s : 'soniya', - ss : '%d soniya', - m : 'bir daqiqa', - mm : '%d daqiqa', - h : 'bir soat', - hh : '%d soat', - d : 'bir kun', - dd : '%d kun', - M : 'bir oy', - MM : '%d oy', - y : 'bir yil', - yy : '%d yil' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 1st is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('uz', { - months : 'январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр'.split('_'), - monthsShort : 'янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек'.split('_'), - weekdays : 'Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба'.split('_'), - weekdaysShort : 'Якш_Душ_Сеш_Чор_Пай_Жум_Шан'.split('_'), - weekdaysMin : 'Як_Ду_Се_Чо_Па_Жу_Ша'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'D MMMM YYYY, dddd HH:mm' - }, - calendar : { - sameDay : '[Бугун соат] LT [да]', - nextDay : '[Эртага] LT [да]', - nextWeek : 'dddd [куни соат] LT [да]', - lastDay : '[Кеча соат] LT [да]', - lastWeek : '[Утган] dddd [куни соат] LT [да]', - sameElse : 'L' - }, - relativeTime : { - future : 'Якин %s ичида', - past : 'Бир неча %s олдин', - s : 'фурсат', - ss : '%d фурсат', - m : 'бир дакика', - mm : '%d дакика', - h : 'бир соат', - hh : '%d соат', - d : 'бир кун', - dd : '%d кун', - M : 'бир ой', - MM : '%d ой', - y : 'бир йил', - yy : '%d йил' - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 7 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('vi', { - months : 'tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12'.split('_'), - monthsShort : 'Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12'.split('_'), - monthsParseExact : true, - weekdays : 'chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy'.split('_'), - weekdaysShort : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), - weekdaysMin : 'CN_T2_T3_T4_T5_T6_T7'.split('_'), - weekdaysParseExact : true, - meridiemParse: /sa|ch/i, - isPM : function (input) { - return /^ch$/i.test(input); - }, - meridiem : function (hours, minutes, isLower) { - if (hours < 12) { - return isLower ? 'sa' : 'SA'; - } else { - return isLower ? 'ch' : 'CH'; - } - }, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM [năm] YYYY', - LLL : 'D MMMM [năm] YYYY HH:mm', - LLLL : 'dddd, D MMMM [năm] YYYY HH:mm', - l : 'DD/M/YYYY', - ll : 'D MMM YYYY', - lll : 'D MMM YYYY HH:mm', - llll : 'ddd, D MMM YYYY HH:mm' - }, - calendar : { - sameDay: '[Hôm nay lúc] LT', - nextDay: '[Ngày mai lúc] LT', - nextWeek: 'dddd [tuần tới lúc] LT', - lastDay: '[Hôm qua lúc] LT', - lastWeek: 'dddd [tuần rồi lúc] LT', - sameElse: 'L' - }, - relativeTime : { - future : '%s tới', - past : '%s trước', - s : 'vài giây', - ss : '%d giây' , - m : 'một phút', - mm : '%d phút', - h : 'một giờ', - hh : '%d giờ', - d : 'một ngày', - dd : '%d ngày', - M : 'một tháng', - MM : '%d tháng', - y : 'một năm', - yy : '%d năm' - }, - dayOfMonthOrdinalParse: /\d{1,2}/, - ordinal : function (number) { - return number; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('x-pseudo', { - months : 'J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér'.split('_'), - monthsShort : 'J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc'.split('_'), - monthsParseExact : true, - weekdays : 'S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý'.split('_'), - weekdaysShort : 'S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát'.split('_'), - weekdaysMin : 'S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd, D MMMM YYYY HH:mm' - }, - calendar : { - sameDay : '[T~ódá~ý át] LT', - nextDay : '[T~ómó~rró~w át] LT', - nextWeek : 'dddd [át] LT', - lastDay : '[Ý~ést~érdá~ý át] LT', - lastWeek : '[L~ást] dddd [át] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'í~ñ %s', - past : '%s á~gó', - s : 'á ~féw ~sécó~ñds', - ss : '%d s~écóñ~ds', - m : 'á ~míñ~úté', - mm : '%d m~íñú~tés', - h : 'á~ñ hó~úr', - hh : '%d h~óúrs', - d : 'á ~dáý', - dd : '%d d~áýs', - M : 'á ~móñ~th', - MM : '%d m~óñt~hs', - y : 'á ~ýéár', - yy : '%d ý~éárs' - }, - dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (~~(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('yo', { - months : 'Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀'.split('_'), - monthsShort : 'Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀'.split('_'), - weekdays : 'Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta'.split('_'), - weekdaysShort : 'Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá'.split('_'), - weekdaysMin : 'Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb'.split('_'), - longDateFormat : { - LT : 'h:mm A', - LTS : 'h:mm:ss A', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY h:mm A', - LLLL : 'dddd, D MMMM YYYY h:mm A' - }, - calendar : { - sameDay : '[Ònì ni] LT', - nextDay : '[Ọ̀la ni] LT', - nextWeek : 'dddd [Ọsẹ̀ tón\'bọ] [ni] LT', - lastDay : '[Àna ni] LT', - lastWeek : 'dddd [Ọsẹ̀ tólọ́] [ni] LT', - sameElse : 'L' - }, - relativeTime : { - future : 'ní %s', - past : '%s kọjá', - s : 'ìsẹjú aayá die', - ss :'aayá %d', - m : 'ìsẹjú kan', - mm : 'ìsẹjú %d', - h : 'wákati kan', - hh : 'wákati %d', - d : 'ọjọ́ kan', - dd : 'ọjọ́ %d', - M : 'osù kan', - MM : 'osù %d', - y : 'ọdún kan', - yy : 'ọdún %d' - }, - dayOfMonthOrdinalParse : /ọjọ́\s\d{1,2}/, - ordinal : 'ọjọ́ %d', - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('zh-cn', { - months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), - weekdaysShort : '周日_周一_周二_周三_周四_周五_周六'.split('_'), - weekdaysMin : '日_一_二_三_四_五_六'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY年M月D日', - LLL : 'YYYY年M月D日Ah点mm分', - LLLL : 'YYYY年M月D日ddddAh点mm分', - l : 'YYYY/M/D', - ll : 'YYYY年M月D日', - lll : 'YYYY年M月D日 HH:mm', - llll : 'YYYY年M月D日dddd HH:mm' - }, - meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, - meridiemHour: function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === '凌晨' || meridiem === '早上' || - meridiem === '上午') { - return hour; - } else if (meridiem === '下午' || meridiem === '晚上') { - return hour + 12; - } else { - // '中午' - return hour >= 11 ? hour : hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 600) { - return '凌晨'; - } else if (hm < 900) { - return '早上'; - } else if (hm < 1130) { - return '上午'; - } else if (hm < 1230) { - return '中午'; - } else if (hm < 1800) { - return '下午'; - } else { - return '晚上'; - } - }, - calendar : { - sameDay : '[今天]LT', - nextDay : '[明天]LT', - nextWeek : '[下]ddddLT', - lastDay : '[昨天]LT', - lastWeek : '[上]ddddLT', - sameElse : 'L' - }, - dayOfMonthOrdinalParse: /\d{1,2}(日|月|周)/, - ordinal : function (number, period) { - switch (period) { - case 'd': - case 'D': - case 'DDD': - return number + '日'; - case 'M': - return number + '月'; - case 'w': - case 'W': - return number + '周'; - default: - return number; - } - }, - relativeTime : { - future : '%s内', - past : '%s前', - s : '几秒', - ss : '%d 秒', - m : '1 分钟', - mm : '%d 分钟', - h : '1 小时', - hh : '%d 小时', - d : '1 天', - dd : '%d 天', - M : '1 个月', - MM : '%d 个月', - y : '1 年', - yy : '%d 年' - }, - week : { - // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('zh-hk', { - months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), - weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'), - weekdaysMin : '日_一_二_三_四_五_六'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY年M月D日', - LLL : 'YYYY年M月D日 HH:mm', - LLLL : 'YYYY年M月D日dddd HH:mm', - l : 'YYYY/M/D', - ll : 'YYYY年M月D日', - lll : 'YYYY年M月D日 HH:mm', - llll : 'YYYY年M月D日dddd HH:mm' - }, - meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') { - return hour; - } else if (meridiem === '中午') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === '下午' || meridiem === '晚上') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 600) { - return '凌晨'; - } else if (hm < 900) { - return '早上'; - } else if (hm < 1130) { - return '上午'; - } else if (hm < 1230) { - return '中午'; - } else if (hm < 1800) { - return '下午'; - } else { - return '晚上'; - } - }, - calendar : { - sameDay : '[今天]LT', - nextDay : '[明天]LT', - nextWeek : '[下]ddddLT', - lastDay : '[昨天]LT', - lastWeek : '[上]ddddLT', - sameElse : 'L' - }, - dayOfMonthOrdinalParse: /\d{1,2}(日|月|週)/, - ordinal : function (number, period) { - switch (period) { - case 'd' : - case 'D' : - case 'DDD' : - return number + '日'; - case 'M' : - return number + '月'; - case 'w' : - case 'W' : - return number + '週'; - default : - return number; - } - }, - relativeTime : { - future : '%s內', - past : '%s前', - s : '幾秒', - ss : '%d 秒', - m : '1 分鐘', - mm : '%d 分鐘', - h : '1 小時', - hh : '%d 小時', - d : '1 天', - dd : '%d 天', - M : '1 個月', - MM : '%d 個月', - y : '1 年', - yy : '%d 年' - } - }); - - //! moment.js locale configuration - - hooks.defineLocale('zh-tw', { - months : '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), - monthsShort : '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), - weekdays : '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), - weekdaysShort : '週日_週一_週二_週三_週四_週五_週六'.split('_'), - weekdaysMin : '日_一_二_三_四_五_六'.split('_'), - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'YYYY/MM/DD', - LL : 'YYYY年M月D日', - LLL : 'YYYY年M月D日 HH:mm', - LLLL : 'YYYY年M月D日dddd HH:mm', - l : 'YYYY/M/D', - ll : 'YYYY年M月D日', - lll : 'YYYY年M月D日 HH:mm', - llll : 'YYYY年M月D日dddd HH:mm' - }, - meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, - meridiemHour : function (hour, meridiem) { - if (hour === 12) { - hour = 0; - } - if (meridiem === '凌晨' || meridiem === '早上' || meridiem === '上午') { - return hour; - } else if (meridiem === '中午') { - return hour >= 11 ? hour : hour + 12; - } else if (meridiem === '下午' || meridiem === '晚上') { - return hour + 12; - } - }, - meridiem : function (hour, minute, isLower) { - var hm = hour * 100 + minute; - if (hm < 600) { - return '凌晨'; - } else if (hm < 900) { - return '早上'; - } else if (hm < 1130) { - return '上午'; - } else if (hm < 1230) { - return '中午'; - } else if (hm < 1800) { - return '下午'; - } else { - return '晚上'; - } - }, - calendar : { - sameDay : '[今天] LT', - nextDay : '[明天] LT', - nextWeek : '[下]dddd LT', - lastDay : '[昨天] LT', - lastWeek : '[上]dddd LT', - sameElse : 'L' - }, - dayOfMonthOrdinalParse: /\d{1,2}(日|月|週)/, - ordinal : function (number, period) { - switch (period) { - case 'd' : - case 'D' : - case 'DDD' : - return number + '日'; - case 'M' : - return number + '月'; - case 'w' : - case 'W' : - return number + '週'; - default : - return number; - } - }, - relativeTime : { - future : '%s內', - past : '%s前', - s : '幾秒', - ss : '%d 秒', - m : '1 分鐘', - mm : '%d 分鐘', - h : '1 小時', - hh : '%d 小時', - d : '1 天', - dd : '%d 天', - M : '1 個月', - MM : '%d 個月', - y : '1 年', - yy : '%d 年' - } - }); - - hooks.locale('en'); - - return hooks; - -}))); \ No newline at end of file diff --git a/web/js/index.ts b/web/js/index.ts index fa00a902..ab15445c 100644 --- a/web/js/index.ts +++ b/web/js/index.ts @@ -1 +1,4 @@ -const tc = require("tc-shared/main"); \ No newline at end of file +const webrtc_adapter = require("webrtc-adapter"); +const tc = require("tc-shared/main"); + +console.log(webrtc_adapter); \ No newline at end of file From 51d135aab2373e288d03a5844dc12feaeb4416f1 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 31 Mar 2020 16:43:21 +0200 Subject: [PATCH 17/23] Removed DOMPurify and fixed the opus web worker --- file.ts | 9 ++++ loader/app/app.ts | 6 +-- package-lock.json | 15 +++++++ package.json | 5 ++- shared/css/static/general.scss | 1 - shared/js/MessageFormatter.ts | 1 + shared/js/proto.ts | 8 ---- vendor/DOMPurify/purify.min.js | 2 - web/js/workers/codec/CodecWorker.ts | 14 +++++-- web/js/workers/codec/OpusCodec.ts | 65 +++++++++++++++++++++-------- 10 files changed, 87 insertions(+), 39 deletions(-) delete mode 100644 vendor/DOMPurify/purify.min.js diff --git a/file.ts b/file.ts index 376a4547..5a0a578f 100644 --- a/file.ts +++ b/file.ts @@ -555,6 +555,15 @@ const WEB_APP_FILE_LIST = [ /* web specific */ + { /* generated assembly files */ + "web-only": true, + "type": "wasm", + "search-pattern": /.*\.(wasm)/, + "build-target": "dev|rel", + + "path": "wasm/", + "local-path": "./asm/generated/" + }, { /* web css files */ "web-only": true, "type": "css", diff --git a/loader/app/app.ts b/loader/app/app.ts index f5cb23f4..40966be4 100644 --- a/loader/app/app.ts +++ b/loader/app/app.ts @@ -98,11 +98,11 @@ const loader_javascript = { }); */ } - await loader.scripts.load(["vendor/DOMPurify/purify.min.js"], { cache_tag: cache_tag() }); - await loader.scripts.load("vendor/jsrender/jsrender.min.js", { cache_tag: cache_tag() }); await loader.scripts.load_multiple([ + ["vendor/jsrender/jsrender.min.js"], ["vendor/xbbcode/src/parser.js"], + ["vendor/emoji-picker/src/jquery.lsxemojipicker.js"], ["vendor/twemoji/twemoji.min.js", ""], /* empty string means not required */ ["vendor/highlight/highlight.pack.js", ""], /* empty string means not required */ ["vendor/remarkable/remarkable.min.js", ""], /* empty string means not required */ @@ -111,8 +111,6 @@ const loader_javascript = { max_parallel_requests: -1 }); - await loader.scripts.load("vendor/emoji-picker/src/jquery.lsxemojipicker.js", { cache_tag: cache_tag() }); - let manifest: Manifest; try { const response = await fetch("js/manifest.json"); diff --git a/package-lock.json b/package-lock.json index 09bf9591..0f5e54ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,15 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/dompurify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.0.1.tgz", + "integrity": "sha512-OQ16dECrRv/I//woKkVUxyVGYR94W3qp3Wy//B63awHVe3h/1/URFqP5a/V2m4k01DEvWs1+z7FWW3xfM1lH3Q==", + "dev": true, + "requires": { + "@types/trusted-types": "*" + } + }, "@types/emscripten": { "version": "1.39.2", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.2.tgz", @@ -144,6 +153,12 @@ "integrity": "sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==", "dev": true }, + "@types/trusted-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.4.tgz", + "integrity": "sha512-6jtHrHpmiXOXoJ31Cg9R+iEVwuEKPf0XHwFUI93eEPXx492/J2JHyafkleKE2EYzZprayk9FSjTyK1GDqcwDng==", + "dev": true + }, "@types/uglify-js": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz", diff --git a/package.json b/package.json index 30177f40..6ea49564 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "author": "TeaSpeak (WolverinDEV)", "license": "ISC", "devDependencies": { + "@types/dompurify": "^2.0.1", "@types/emscripten": "^1.38.0", "@types/fs-extra": "^8.0.1", "@types/jquery": "^3.3.34", @@ -56,6 +57,7 @@ "sha256": "^0.2.0", "style-loader": "^1.1.3", "terser": "^4.2.1", + "terser-webpack-plugin": "latest", "ts-loader": "^6.2.2", "ttypescript": "^1.5.10", "typescript": "3.6.5", @@ -63,8 +65,7 @@ "webpack": "^4.42.1", "webpack-bundle-analyzer": "^3.6.1", "webpack-cli": "^3.3.11", - "worker-plugin": "^4.0.2", - "terser-webpack-plugin": "latest" + "worker-plugin": "^4.0.2" }, "repository": { "type": "git", diff --git a/shared/css/static/general.scss b/shared/css/static/general.scss index 56bee158..1f333d71 100644 --- a/shared/css/static/general.scss +++ b/shared/css/static/general.scss @@ -123,7 +123,6 @@ fieldset { } code { - background-color: lightgray; padding: 2px; } diff --git a/shared/js/MessageFormatter.ts b/shared/js/MessageFormatter.ts index 462423c0..6aa6fe2f 100644 --- a/shared/js/MessageFormatter.ts +++ b/shared/js/MessageFormatter.ts @@ -4,6 +4,7 @@ import {copy_to_clipboard} from "tc-shared/utils/helpers"; import {guid} from "tc-shared/crypto/uid"; import * as loader from "tc-loader"; import * as image_preview from "./ui/frames/image_preview" +import * as DOMPurify from "dompurify"; declare const xbbcode; export namespace bbcode { diff --git a/shared/js/proto.ts b/shared/js/proto.ts index 9d732c33..c21b8a2c 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -63,14 +63,6 @@ declare global { second_best?: any; } - interface DOMPurify { - sanitize(html: string, config?: { - ADD_ATTR?: string[] - ADD_TAGS?: string[]; - }) : string; - } - let DOMPurify: DOMPurify; - let remarkable: typeof window.remarkable; class webkitAudioContext extends AudioContext {} diff --git a/vendor/DOMPurify/purify.min.js b/vendor/DOMPurify/purify.min.js deleted file mode 100644 index 618e7b60..00000000 --- a/vendor/DOMPurify/purify.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.DOMPurify=t()}(this,function(){"use strict";function e(e,t){y&&y(e,null);for(var n=t.length;n--;){var r=t[n];if("string"==typeof r){var o=r.toLowerCase();o!==r&&(Object.isFrozen(t)||(t[n]=o),r=o)}e[r]=!0}return e}function t(e){var t={},n=void 0;for(n in e)g(h,e,[n])&&(t[n]=e[n]);return t}function n(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:_(),u=function(e){return r(e)};if(u.version="1.0.11",u.removed=[],!o||!o.document||9!==o.document.nodeType)return u.isSupported=!1,u;var h=o.document,y=!1,g=!1,v=o.document,D=o.DocumentFragment,R=o.HTMLTemplateElement,C=o.Node,H=o.NodeFilter,F=o.NamedNodeMap,z=void 0===F?o.NamedNodeMap||o.MozNamedAttrMap:F,I=o.Text,j=o.Comment,P=o.DOMParser,U=o.TrustedTypes;if("function"==typeof R){var W=v.createElement("template");W.content&&W.content.ownerDocument&&(v=W.content.ownerDocument)}var B=N(U,h),G=B?B.createHTML(""):"",q=v,V=q.implementation,Y=q.createNodeIterator,K=q.getElementsByTagName,X=q.createDocumentFragment,$=h.importNode,J={};u.isSupported=V&&void 0!==V.createHTMLDocument&&9!==v.documentMode;var Q=b,Z=T,ee=A,te=x,ne=S,re=M,oe=L,ie=null,ae=e({},[].concat(n(i),n(a),n(l),n(c),n(s))),le=null,ce=e({},[].concat(n(d),n(f),n(p),n(m))),se=null,ue=null,de=!0,fe=!0,pe=!1,me=!1,he=!1,ye=!1,ge=!1,ve=!1,be=!1,Te=!1,Ae=!1,xe=!0,Le=!0,Se=!1,Me={},ke=e({},["audio","head","math","script","style","template","svg","video"]),we=e({},["audio","video","img","source","image"]),Ee=null,Oe=e({},["alt","class","for","id","label","name","pattern","placeholder","summary","title","value","style","xmlns"]),_e=null,Ne=v.createElement("form"),De=function(r){_e&&_e===r||(r&&"object"===(void 0===r?"undefined":k(r))||(r={}),ie="ALLOWED_TAGS"in r?e({},r.ALLOWED_TAGS):ae,le="ALLOWED_ATTR"in r?e({},r.ALLOWED_ATTR):ce,Ee="ADD_URI_SAFE_ATTR"in r?e({},r.ADD_URI_SAFE_ATTR):Oe,se="FORBID_TAGS"in r?e({},r.FORBID_TAGS):{},ue="FORBID_ATTR"in r?e({},r.FORBID_ATTR):{},Me="USE_PROFILES"in r&&r.USE_PROFILES,de=!1!==r.ALLOW_ARIA_ATTR,fe=!1!==r.ALLOW_DATA_ATTR,pe=r.ALLOW_UNKNOWN_PROTOCOLS||!1,me=r.SAFE_FOR_JQUERY||!1,he=r.SAFE_FOR_TEMPLATES||!1,ye=r.WHOLE_DOCUMENT||!1,be=r.RETURN_DOM||!1,Te=r.RETURN_DOM_FRAGMENT||!1,Ae=r.RETURN_DOM_IMPORT||!1,ve=r.FORCE_BODY||!1,xe=!1!==r.SANITIZE_DOM,Le=!1!==r.KEEP_CONTENT,Se=r.IN_PLACE||!1,oe=r.ALLOWED_URI_REGEXP||oe,he&&(fe=!1),Te&&(be=!0),Me&&(ie=e({},[].concat(n(s))),le=[],!0===Me.html&&(e(ie,i),e(le,d)),!0===Me.svg&&(e(ie,a),e(le,f),e(le,m)),!0===Me.svgFilters&&(e(ie,l),e(le,f),e(le,m)),!0===Me.mathMl&&(e(ie,c),e(le,p),e(le,m))),r.ADD_TAGS&&(ie===ae&&(ie=t(ie)),e(ie,r.ADD_TAGS)),r.ADD_ATTR&&(le===ce&&(le=t(le)),e(le,r.ADD_ATTR)),r.ADD_URI_SAFE_ATTR&&e(Ee,r.ADD_URI_SAFE_ATTR),Le&&(ie["#text"]=!0),ye&&e(ie,["html","head","body"]),ie.table&&e(ie,["tbody"]),O&&O(r),_e=r)},Re=function(e){u.removed.push({element:e});try{e.parentNode.removeChild(e)}catch(t){e.outerHTML=G}},Ce=function(e,t){try{u.removed.push({attribute:t.getAttributeNode(e),from:t})}catch(e){u.removed.push({attribute:null,from:t})}t.removeAttribute(e)},He=function(t){var n=void 0,r=void 0;if(ve)t=""+t;else{var o=t.match(/^[\s]+/);(r=o&&o[0])&&(t=t.slice(r.length))}if(y)try{n=(new P).parseFromString(t,"text/html")}catch(e){}if(g&&e(se,["title"]),!n||!n.documentElement){var i=(n=V.createHTMLDocument("")).body;i.parentNode.removeChild(i.parentNode.firstElementChild),i.outerHTML=B?B.createHTML(t):t}return r&&n.body.insertBefore(v.createTextNode(r),n.body.childNodes[0]||null),K.call(n,ye?"html":"body")[0]};u.isSupported&&(function(){try{He('

').querySelector("svg img")&&(y=!0)}catch(e){}}(),function(){try{He("</title><img>").querySelector("title").innerHTML.match(/<\/title/)&&(g=!0)}catch(e){}}());var Fe=function(e){return Y.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT,function(){return H.FILTER_ACCEPT},!1)},ze=function(e){return!(e instanceof I||e instanceof j)&&!("string"==typeof e.nodeName&&"string"==typeof e.textContent&&"function"==typeof e.removeChild&&e.attributes instanceof z&&"function"==typeof e.removeAttribute&&"function"==typeof e.setAttribute)},Ie=function(e){return"object"===(void 0===C?"undefined":k(C))?e instanceof C:e&&"object"===(void 0===e?"undefined":k(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},je=function(e,t,n){J[e]&&J[e].forEach(function(e){e.call(u,t,n,_e)})},Pe=function(e){var t=void 0;if(je("beforeSanitizeElements",e,null),ze(e))return Re(e),!0;var n=e.nodeName.toLowerCase();if(je("uponSanitizeElement",e,{tagName:n,allowedTags:ie}),!ie[n]||se[n]){if(Le&&!ke[n]&&"function"==typeof e.insertAdjacentHTML)try{var r=e.innerHTML;e.insertAdjacentHTML("AfterEnd",B?B.createHTML(r):r)}catch(e){}return Re(e),!0}return"noscript"===n&&e.innerHTML.match(/<\/noscript/i)?(Re(e),!0):"noembed"===n&&e.innerHTML.match(/<\/noembed/i)?(Re(e),!0):(!me||e.firstElementChild||e.content&&e.content.firstElementChild||!/</g.test(e.textContent)||(u.removed.push({element:e.cloneNode()}),e.innerHTML?e.innerHTML=e.innerHTML.replace(/</g,"<"):e.innerHTML=e.textContent.replace(/</g,"<")),he&&3===e.nodeType&&(t=(t=(t=e.textContent).replace(Q," ")).replace(Z," "),e.textContent!==t&&(u.removed.push({element:e.cloneNode()}),e.textContent=t)),je("afterSanitizeElements",e,null),!1)},Ue=function(e,t,n){if(xe&&("id"===t||"name"===t)&&(n in v||n in Ne))return!1;if(fe&&ee.test(t));else if(de&&te.test(t));else{if(!le[t]||ue[t])return!1;if(Ee[t]);else if(oe.test(n.replace(re,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==n.indexOf("data:")||!we[e]){if(pe&&!ne.test(n.replace(re,"")));else if(n)return!1}else;}return!0},We=function(e){var t=void 0,n=void 0,r=void 0,o=void 0,i=void 0;je("beforeSanitizeAttributes",e,null);var a=e.attributes;if(a){var l={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:le};for(i=a.length;i--;){var c=t=a[i],s=c.name,d=c.namespaceURI;if(n=t.value.trim(),r=s.toLowerCase(),l.attrName=r,l.attrValue=n,l.keepAttr=!0,je("uponSanitizeAttribute",e,l),n=l.attrValue,"name"===r&&"IMG"===e.nodeName&&a.id)o=a.id,a=w(E,a,[]),Ce("id",e),Ce(s,e),a.indexOf(o)>i&&e.setAttribute("id",o.value);else{if("INPUT"===e.nodeName&&"type"===r&&"file"===n&&l.keepAttr&&(le[r]||!ue[r]))continue;"id"===s&&e.setAttribute(s,""),Ce(s,e)}if(l.keepAttr){he&&(n=(n=n.replace(Q," ")).replace(Z," "));var f=e.nodeName.toLowerCase();if(Ue(f,r,n))try{d?e.setAttributeNS(d,s,n):e.setAttribute(s,n),u.removed.pop()}catch(e){}}}je("afterSanitizeAttributes",e,null)}},Be=function e(t){var n=void 0,r=Fe(t);for(je("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)je("uponSanitizeShadowNode",n,null),Pe(n)||(n.content instanceof D&&e(n.content),We(n));je("afterSanitizeShadowDOM",t,null)};return u.sanitize=function(e,t){var n=void 0,r=void 0,i=void 0,a=void 0,l=void 0;if(e||(e="\x3c!--\x3e"),"string"!=typeof e&&!Ie(e)){if("function"!=typeof e.toString)throw new TypeError("toString is not a function");if("string"!=typeof(e=e.toString()))throw new TypeError("dirty is not a string, aborting")}if(!u.isSupported){if("object"===k(o.toStaticHTML)||"function"==typeof o.toStaticHTML){if("string"==typeof e)return o.toStaticHTML(e);if(Ie(e))return o.toStaticHTML(e.outerHTML)}return e}if(ge||De(t),u.removed=[],Se);else if(e instanceof C)1===(r=(n=He("\x3c!--\x3e")).ownerDocument.importNode(e,!0)).nodeType&&"BODY"===r.nodeName?n=r:"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!be&&!he&&!ye&&-1===e.indexOf("<"))return B?B.createHTML(e):e;if(!(n=He(e)))return be?null:G}n&&ve&&Re(n.firstChild);for(var c=Fe(Se?e:n);i=c.nextNode();)3===i.nodeType&&i===a||Pe(i)||(i.content instanceof D&&Be(i.content),We(i),a=i);if(a=null,Se)return e;if(be){if(Te)for(l=X.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return Ae&&(l=$.call(h,l,!0)),l}var s=ye?n.outerHTML:n.innerHTML;return he&&(s=(s=s.replace(Q," ")).replace(Z," ")),B?B.createHTML(s):s},u.setConfig=function(e){De(e),ge=!0},u.clearConfig=function(){_e=null,ge=!1},u.isValidAttribute=function(e,t,n){_e||De({});var r=e.toLowerCase(),o=t.toLowerCase();return Ue(r,o,n)},u.addHook=function(e,t){"function"==typeof t&&(J[e]=J[e]||[],J[e].push(t))},u.removeHook=function(e){J[e]&&J[e].pop()},u.removeHooks=function(e){J[e]&&(J[e]=[])},u.removeAllHooks=function(){J={}},u}var o=Object.freeze||function(e){return e},i=o(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),a=o(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","audio","canvas","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","video","view","vkern"]),l=o(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),c=o(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),s=o(["#text"]),u=Object.freeze||function(e){return e},d=u(["accept","action","align","alt","autocomplete","background","bgcolor","border","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","coords","crossorigin","datetime","default","dir","disabled","download","enctype","face","for","headers","height","hidden","high","href","hreflang","id","integrity","ismap","label","lang","list","loop","low","max","maxlength","media","method","min","multiple","name","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","type","usemap","valign","value","width","xmlns"]),f=u(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","tabindex","targetx","targety","transform","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),p=u(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),m=u(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),h=Object.hasOwnProperty,y=Object.setPrototypeOf,g=("undefined"!=typeof Reflect&&Reflect).apply;g||(g=function(e,t,n){return e.apply(t,n)});var v=Object.seal||function(e){return e},b=v(/\{\{[\s\S]*|[\s\S]*\}\}/gm),T=v(/<%[\s\S]*|[\s\S]*%>/gm),A=v(/^data-[\-\w.\u00B7-\uFFFF]/),x=v(/^aria-[\-\w]+$/),L=v(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),S=v(/^(?:\w+script|data):/i),M=v(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g),k="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},w=("undefined"!=typeof Reflect&&Reflect).apply,E=Array.prototype.slice,O=Object.freeze,_=function(){return"undefined"==typeof window?null:window};w||(w=function(e,t,n){return e.apply(t,n)});var N=function(e,t){if("object"!==(void 0===e?"undefined":k(e))||"function"!=typeof e.createPolicy)return null;var n=null;t.currentScript&&t.currentScript.hasAttribute("data-tt-policy-suffix")&&(n=t.currentScript.getAttribute("data-tt-policy-suffix"));var r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:function(e){return e}})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};return r()}); -//# sourceMappingURL=purify.min.js.map \ No newline at end of file diff --git a/web/js/workers/codec/CodecWorker.ts b/web/js/workers/codec/CodecWorker.ts index 2184d58c..9ef301b4 100644 --- a/web/js/workers/codec/CodecWorker.ts +++ b/web/js/workers/codec/CodecWorker.ts @@ -34,11 +34,17 @@ let global_initialize_result; async function handle_message(command: string, data: any) : Promise<string | object> { switch (command) { case "global-initialize": - const init_result = globally_initialized ? global_initialize_result : await initialize_callback(); - globally_initialized = true; + try { + const init_result = globally_initialized ? global_initialize_result : await initialize_callback(); + globally_initialized = true; - if(typeof init_result === "string") - return init_result; + if(typeof init_result === "string") + throw init_result; + } catch (e) { + if(typeof e === "string") + return e; + throw e; + } return {}; case "initialise": diff --git a/web/js/workers/codec/OpusCodec.ts b/web/js/workers/codec/OpusCodec.ts index 91eca0e1..0af18483 100644 --- a/web/js/workers/codec/OpusCodec.ts +++ b/web/js/workers/codec/OpusCodec.ts @@ -1,6 +1,7 @@ import * as cworker from "./CodecWorker"; import {CodecType} from "tc-backend/web/codec/Codec"; import {CodecWorker} from "./CodecWorker"; +import {type} from "os"; declare global { interface Window { @@ -33,17 +34,10 @@ const runtime_initialize_promise = new Promise((resolve, reject) => { let message; if(error instanceof DOMException) message = "DOMException (" + error.name + "): " + error.code + " => " + error.message; - else { - abort_message = error; + else if(error instanceof Error) { + message = error.message; + } else { message = error; - if(error.indexOf("no binaryen method succeeded") != -1) { - for(const error of WASM_ERROR_MESSAGES) { - if(last_error_message.indexOf(error) != -1) { - message = "no native wasm support detected, but its required"; - break; - } - } - } } reject(message); @@ -51,21 +45,44 @@ const runtime_initialize_promise = new Promise((resolve, reject) => { }); }); -let abort_message: string = undefined; -let last_error_message: string; self.__init_em_module.push(Module => { Module['print'] = function() { - if(arguments.length == 1 && arguments[0] == abort_message) - return; /* we don't need to reprint the abort message! */ - + const message = arguments[0] as string; + if(message.startsWith("CompileError: WebAssembly.instantiate(): ")) { + /* Compile errors also get printed to error stream so no need to log them here */ + return; + } console.log(...arguments); }; Module['printErr'] = function() { - if(arguments.length == 1 && arguments[0] == abort_message) - return; /* we don't need to reprint the abort message! */ + const message = arguments[0] as string; + if(message.startsWith("wasm streaming compile failed: ")) { + const error_message = message.substr(31); + if(error_message.startsWith("TypeError: Failed to execute 'compile' on 'WebAssembly': ")) { + console.warn("Failed to compile opus native code: %s", error_message.substr(57)); + } else { + console.warn("Failed to prepare opus native code asynchronously: %s", error_message); + } + return; + } else if(message === "falling back to ArrayBuffer instantiation") { + /* + We suppress this message, because it comes directly after "wasm streaming compile failed:". + So if we want to print multiple lines we just have to edit the lines above. + */ + return; + } else if(message.startsWith("failed to asynchronously prepare wasm:")) { + /* + Will be handled via abort + */ + return; + } else if(message.startsWith("CompileError: WebAssembly.instantiate():")) { + /* + Will be handled via abort already + */ + return; + } - last_error_message = arguments[0]; for(const suppress of WASM_ERROR_MESSAGES) if((arguments[0] as string).indexOf(suppress) != -1) return; @@ -76,6 +93,18 @@ self.__init_em_module.push(Module => { Module['locateFile'] = file => "../../wasm/" + file; }); +self.addEventListener("unhandledrejection", event => { + if(event.reason instanceof Error) { + if(event.reason.name === "RuntimeError" && event.reason.message.startsWith("abort(CompileError: WebAssembly.instantiate():")) { + /* + We already handled that error via the Module['printErr'] callback. + */ + event.preventDefault(); + return; + } + } +}); + enum OpusType { VOIP = 2048, AUDIO = 2049, From 3044e035bc8aaabe53cc6978452fc5cecaeca832 Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Wed, 1 Apr 2020 01:17:36 +0200 Subject: [PATCH 18/23] Removing unused imports from generate module declarations --- client/js/.keepalive | 0 shared/generate_declarations.sh | 12 +- shared/js/ui/modal/ModalPoke.ts | 2 +- shared/tsconfig/dtsconfig_app.json | 2 +- tools/dtsgen/declarator.ts | 2 +- tools/dtsgen/declare_fixup.ts | 15 ++ tools/dtsgen/import_organizer.ts | 248 +++++++++++++++++++++++++++ tools/dtsgen/index.ts | 8 +- tools/dtsgen/out.d/module_a.d.ts | 8 - tools/dtsgen/out.d/test_01.d.ts | 5 - tools/dtsgen/out.d/test_02.d.ts | 8 - tools/dtsgen/test_modular/test_03.ts | 8 + tools/dtsgen/test_modular/test_04.ts | 6 + webpack.config.ts | 8 - 14 files changed, 292 insertions(+), 40 deletions(-) delete mode 100644 client/js/.keepalive create mode 100644 tools/dtsgen/declare_fixup.ts create mode 100644 tools/dtsgen/import_organizer.ts delete mode 100644 tools/dtsgen/out.d/module_a.d.ts delete mode 100644 tools/dtsgen/out.d/test_01.d.ts delete mode 100644 tools/dtsgen/out.d/test_02.d.ts create mode 100644 tools/dtsgen/test_modular/test_03.ts create mode 100644 tools/dtsgen/test_modular/test_04.ts diff --git a/client/js/.keepalive b/client/js/.keepalive deleted file mode 100644 index e69de29b..00000000 diff --git a/shared/generate_declarations.sh b/shared/generate_declarations.sh index 8029651f..99d87c07 100755 --- a/shared/generate_declarations.sh +++ b/shared/generate_declarations.sh @@ -7,8 +7,8 @@ source ../scripts/resolve_commands.sh function generate_declaration() { echo "Generating declarations for project $1 ($2)" - if [[ -e "${2}" ]]; then - rm "${2}"; _exit_code=$? + if [[ -d "${2}" ]]; then + rm -r "${2}"; _exit_code=$? if [[ $_exit_code -ne 0 ]]; then echo "Failed to remove old declaration file ($2): $_exit_code!" echo "This could be critical later!" @@ -23,12 +23,12 @@ function generate_declaration() { } #Generate the loader definitions first -app_declaration="declarations/exports_app.d.ts" -loader_declaration_app="declarations/exports_loader_app.d.ts" -loader_declaration_certaccept="declarations/exports_loader_certaccept.d.ts" +app_declaration="../declarations/shared-app/" +loader_declaration_app="../declarations/loader/" +# loader_declaration_certaccept="declarations/exports_loader_certaccept.d.ts" generate_declaration dtsconfig_app.json ${app_declaration} generate_declaration dtsconfig_loader_app.json ${loader_declaration_app} -generate_declaration dtsconfig_loader_certaccept.json ${loader_declaration_certaccept} +# generate_declaration dtsconfig_loader_certaccept.json ${loader_declaration_certaccept} exit 0 \ No newline at end of file diff --git a/shared/js/ui/modal/ModalPoke.ts b/shared/js/ui/modal/ModalPoke.ts index 2fbfe011..95a6cd26 100644 --- a/shared/js/ui/modal/ModalPoke.ts +++ b/shared/js/ui/modal/ModalPoke.ts @@ -6,7 +6,7 @@ import * as moment from "moment"; let global_modal: PokeModal; -interface ServerEntry { +export interface ServerEntry { source: ConnectionHandler; add_message(invoker: PokeInvoker, message: string); } diff --git a/shared/tsconfig/dtsconfig_app.json b/shared/tsconfig/dtsconfig_app.json index 317d73b6..ef6184a4 100644 --- a/shared/tsconfig/dtsconfig_app.json +++ b/shared/tsconfig/dtsconfig_app.json @@ -6,6 +6,6 @@ "workers/**/*.ts" ], "base_directory": "shared/js/", - "target_directory": "../declarations/shared-app", + "target_directory": "../../declarations/shared-app", "modular": true } \ No newline at end of file diff --git a/tools/dtsgen/declarator.ts b/tools/dtsgen/declarator.ts index 29858933..1d72180a 100644 --- a/tools/dtsgen/declarator.ts +++ b/tools/dtsgen/declarator.ts @@ -452,6 +452,6 @@ generators[SyntaxKind.ImportDeclaration] = (settings, stack, node: ts.ImportDecl node.decorators, node.modifiers, node.importClause, - ts.createStringLiteral(specifier.text + ".d") + ts.createStringLiteral(specifier.text) ); }; \ No newline at end of file diff --git a/tools/dtsgen/declare_fixup.ts b/tools/dtsgen/declare_fixup.ts new file mode 100644 index 00000000..b39eea28 --- /dev/null +++ b/tools/dtsgen/declare_fixup.ts @@ -0,0 +1,15 @@ +import * as ts from "typescript"; +import {SyntaxKind} from "typescript"; + +let has_export; +const visit = (node: ts.Node) => has_export = has_export || (node.modifiers || [] as any).filter(e => e.kind === SyntaxKind.ExportKeyword).length !== 0 || ts.forEachChild(node, visit); + +export function fix_declare_global(nodes: ts.Node[]) : ts.Node[] { + has_export = false; + + // nodes.forEach(visit); /* for a "deep" check */ + nodes.forEach(e => has_export = has_export || (e.modifiers || [] as any).filter(e => e.kind === SyntaxKind.ExportKeyword).length !== 0); + if(has_export) return nodes; + + return []; +} \ No newline at end of file diff --git a/tools/dtsgen/import_organizer.ts b/tools/dtsgen/import_organizer.ts new file mode 100644 index 00000000..b498527d --- /dev/null +++ b/tools/dtsgen/import_organizer.ts @@ -0,0 +1,248 @@ +import * as ts from "typescript"; +import {SyntaxKind} from "typescript"; + +interface RequiredType { + identifier: string; +} + +class ImportsParserData { + readonly source_file: ts.SourceFile; + required_type: RequiredType[]; + depth: number; + + constructor(sf: ts.SourceFile) { + this.source_file = sf; + this.required_type = []; + this.depth = 0; + } + + has_type(name: string) { + return this.required_type.findIndex(e => e.identifier === name) !== -1; + } +} + +export function remove_unused(source_file: ts.SourceFile, nodes: ts.Node[]) : ts.Node[] { + const data = new ImportsParserData(source_file); + + for(const node of nodes) + gather_required_types(node, data); + + //console.log(data.required_type); + const result2d = nodes.map(e => ts.transform(e, [ctx => node => eliminate_imports(node, ctx, data)])).map(e => e.transformed); + const result = []; + for(const entry of result2d) + result.push(...entry); + return result; +} + +function eliminate_imports(node: ts.Node, ctx: ts.TransformationContext, data: ImportsParserData) : ts.Node | undefined { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + const import_decl = node as ts.ImportDeclaration; + const clause = import_decl.importClause; + if(!clause.namedBindings) return node; + + let new_binding; + if(clause.namedBindings.kind === SyntaxKind.NamedImports) { + const bindings = clause.namedBindings as ts.NamedImports; + const elements = bindings.elements.filter(e => data.has_type(e.name.text)); + if(!elements.length) return ts.createIdentifier(""); + + new_binding = ts.createNamedImports(elements); + } else if(clause.namedBindings.kind === SyntaxKind.NamespaceImport) { + const binding = clause.namedBindings as ts.NamespaceImport; + if(!data.has_type(binding.name.text)) + return ts.createIdentifier(""); + new_binding = binding; + } else + throw "unknown named binding"; + + return ts.createImportDeclaration(import_decl.decorators, import_decl.modifiers, new_binding, import_decl.moduleSpecifier); + default: + return ts.visitEachChild(node, e => eliminate_imports(e, ctx, data), ctx); + } +} + +const import_parsers: {[key: number]:(node: ts.Node, data: ImportsParserData) => void} = {}; +function gather_required_types(node: ts.Node, data: ImportsParserData) { + if(!node) return; + //console.log("%d %s", data.depth, SyntaxKind[node.kind]); + + if(import_parsers[node.kind]) { + import_parsers[node.kind](node, data); + return; + } + + data.depth++; + node.forEachChild(e => gather_required_types(e, data)); + data.depth--; +} + + +import_parsers[SyntaxKind.Parameter] = (node: ts.ParameterDeclaration, data) => { + if(!node.type) return; + + analyze_type_node(node.type, data); +}; + +import_parsers[SyntaxKind.TypeAliasDeclaration] = (node: ts.TypeAliasDeclaration, data) => { + (node.typeParameters || []).forEach(e => gather_required_types(e, data)); + if(node.type) analyze_type_node(node.type, data); + if(node.decorators) node.decorators.forEach(e => analyze_type_node(e.expression, data)); +}; + + +import_parsers[SyntaxKind.HeritageClause] = (node: ts.HeritageClause, data) => { + const heritage = node as ts.HeritageClause; + for(const type of heritage.types) + analyze_type_node(type, data); +}; + +import_parsers[SyntaxKind.TypeParameter] = (node: ts.TypeParameterDeclaration, data) => { + if(node.constraint) analyze_type_node(node.constraint, data); + if(node.default) analyze_type_node(node.default, data); +}; + +import_parsers[SyntaxKind.FunctionDeclaration] = (node: ts.FunctionDeclaration, data) => { + if(node.type) + analyze_type_node(node.type, data); + (node.typeParameters || []).forEach(e => gather_required_types(e, data)); + for(const param of node.parameters) + gather_required_types(param, data); +}; + +import_parsers[SyntaxKind.MethodSignature] = (node: ts.MethodSignature, data) => { + if(node.type) + analyze_type_node(node.type, data); + (node.typeParameters || []).forEach(e => gather_required_types(e, data)); + for(const param of node.parameters) + gather_required_types(param, data); +}; + +import_parsers[SyntaxKind.ClassDeclaration] = (node: ts.ClassDeclaration, data) => { + for(const e of node.heritageClauses || []) + gather_required_types(e, data); + + for(const e of node.typeParameters || []) + gather_required_types(e, data); + + for(const e of node.members || []) + gather_required_types(e, data); +}; + +import_parsers[SyntaxKind.PropertySignature] = (node: ts.PropertySignature, data) => { + analyze_type_node(node.type, data); +}; + +import_parsers[SyntaxKind.PropertyDeclaration] = (node: ts.PropertyDeclaration, data) => { + analyze_type_node(node.type, data); +}; + +import_parsers[SyntaxKind.MethodDeclaration] = (node: ts.MethodDeclaration, data) => { + for(const e of node.parameters || []) + gather_required_types(e, data); + for(const e of node.typeParameters || []) + gather_required_types(e, data); + analyze_type_node(node.type, data); +}; + +function analyze_type_node(node: ts.TypeNode | ts.LeftHandSideExpression, data: ImportsParserData) { + if(!node) return; + + //console.log("T: %s", SyntaxKind[node.kind]); + switch (node.kind) { + case SyntaxKind.AnyKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.ThisType: + case SyntaxKind.ThisKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.StringLiteral: + case SyntaxKind.LiteralType: + case SyntaxKind.NumberKeyword: + case SyntaxKind.ObjectKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.NeverKeyword: + case SyntaxKind.UndefinedKeyword: + /* no special export type */ + break; + + case SyntaxKind.UnionType: + const union = node as ts.UnionTypeNode; + union.types.forEach(e => analyze_type_node(e, data)); + break; + + case SyntaxKind.IntersectionType: + const intersection = node as ts.IntersectionTypeNode; + intersection.types.forEach(e => analyze_type_node(e, data)); + break; + + case SyntaxKind.TypeReference: + const ref = node as ts.TypeReferenceNode; + if(ref.typeName.kind === SyntaxKind.Identifier) { + data.required_type.push({ + identifier: ref.typeName.text + }); + } else if(ref.typeName.kind === SyntaxKind.QualifiedName) { + let left: ts.Identifier | ts.QualifiedName = ref.typeName.left; + while(left.kind !== SyntaxKind.Identifier) + left = left.left; + data.required_type.push({ + identifier: left.text + }); + } else + throw "invalid type name"; + for(const e of ref.typeArguments || []) + analyze_type_node(e, data); + break; + + case SyntaxKind.Identifier: + data.required_type.push({ + identifier: (node as ts.Identifier).text + }); + break; + + case SyntaxKind.TypeLiteral: + const lit = node as ts.TypeLiteralNode; + for(const member of lit.members) + gather_required_types(member, data); + break; + + case SyntaxKind.ArrayType: + const array = node as ts.ArrayTypeNode; + analyze_type_node(array.elementType, data); + break; + + case SyntaxKind.FunctionType: + const fn = node as ts.FunctionTypeNode; + for(const param of fn.parameters || []) + gather_required_types(param, data); + + for(const type of fn.typeParameters || []) + gather_required_types(type, data); + break; + + case SyntaxKind.TypeOperator: + const to = node as ts.TypeOperatorNode; + analyze_type_node(to.type, data); + break; + + case SyntaxKind.ExpressionWithTypeArguments: + analyze_type_node((node as ts.ExpressionWithTypeArguments).expression, data); + break; + + case SyntaxKind.IndexedAccessType: + const ia = node as ts.IndexedAccessTypeNode; + analyze_type_node(ia.indexType, data); + analyze_type_node(ia.objectType, data); + break; + + case SyntaxKind.ParenthesizedType: + const parenthesized = node as ts.ParenthesizedTypeNode; + analyze_type_node(parenthesized.type, data); + break; + + default: + throw "Unknown type " + SyntaxKind[node.kind] + ". Extend me :)"; + } +} diff --git a/tools/dtsgen/index.ts b/tools/dtsgen/index.ts index f6d0deb4..1489c6c8 100644 --- a/tools/dtsgen/index.ts +++ b/tools/dtsgen/index.ts @@ -5,6 +5,8 @@ import * as glob from "glob"; import * as path from "path"; import * as mkdirp from "mkdirp"; import {removeSync} from "fs-extra"; +import * as import_organizer from "./import_organizer"; +import * as declare_fixup from "./declare_fixup"; let source_files: string[] = []; let exclude_files: string[] = []; @@ -110,10 +112,12 @@ source_files.forEach(file => { ); console.log("Compile %s (%s)", _file, relpath); - const result = decl.print(source, decl.generate(source, { + const decl_nodes = decl.generate(source, { remove_private: false, module_mode: module_mode - })); + }); + + const result = decl.print(source, declare_fixup.fix_declare_global(import_organizer.remove_unused(source, decl_nodes))); let fpath = path.join(base_path, target_directory, relpath); fpath = fpath.substr(0, fpath.lastIndexOf(".")) + ".d.ts"; diff --git a/tools/dtsgen/out.d/module_a.d.ts b/tools/dtsgen/out.d/module_a.d.ts deleted file mode 100644 index cecc643c..00000000 --- a/tools/dtsgen/out.d/module_a.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export declare class TestClass { - public say_hi(); -} -export declare function say_hello_a(); -export declare namespace X { - export class Y { - } -} diff --git a/tools/dtsgen/out.d/test_01.d.ts b/tools/dtsgen/out.d/test_01.d.ts deleted file mode 100644 index bb5f153c..00000000 --- a/tools/dtsgen/out.d/test_01.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as module_a from "./module_a.d"; -export declare class C extends module_a.TestClass { -} -export declare const say_a; -export declare function say_b(); diff --git a/tools/dtsgen/out.d/test_02.d.ts b/tools/dtsgen/out.d/test_02.d.ts deleted file mode 100644 index 23752014..00000000 --- a/tools/dtsgen/out.d/test_02.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as module_a from "./module_a.d"; -/* CLASS COMMENT!*/ -export declare class C extends module_a.TestClass { -} -/* Say a comment */ -export declare const say_a; -/* Say b comment */ -export declare function say_b(); diff --git a/tools/dtsgen/test_modular/test_03.ts b/tools/dtsgen/test_modular/test_03.ts new file mode 100644 index 00000000..40171465 --- /dev/null +++ b/tools/dtsgen/test_modular/test_03.ts @@ -0,0 +1,8 @@ +import * as a from "./module_a"; + +export declare class PokeModal { + //private source_map: a.TestClass[]; + private _awaiters_unique_ids: { + [unique_id: string]: ((resolved: a.TestClass) => any)[]; + }; +} \ No newline at end of file diff --git a/tools/dtsgen/test_modular/test_04.ts b/tools/dtsgen/test_modular/test_04.ts new file mode 100644 index 00000000..058d06c4 --- /dev/null +++ b/tools/dtsgen/test_modular/test_04.ts @@ -0,0 +1,6 @@ +declare global { + interface X { + + } +} +export = {}; \ No newline at end of file diff --git a/webpack.config.ts b/webpack.config.ts index 412ca812..a5b51b30 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -78,14 +78,6 @@ module.exports = { }; } } - /* - { - "transform": "../../tools/trgen/ttsc_transformer.js", - "type": "program", - "target_file": "../generated/messages_script.json", - "verbose": true - } - */ } ] }, From 39d109e9afe52e7a4d7db64354e7f2af2f30070f Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Wed, 1 Apr 2020 15:36:37 +0200 Subject: [PATCH 19/23] Made the shared app workable for the native client --- client/js/index.ts | 4 + file.ts | 405 ++++++++++--------------- loader/app/index.ts | 5 +- loader/app/loader/loader.ts | 15 +- loader/app/{ => targets}/app.ts | 83 +++-- loader/app/{ => targets}/certaccept.ts | 2 +- loader/webpack.config.js | 11 +- package.json | 6 +- shared/backend.d/audio/player.d.ts | 4 - shared/js/main.ts | 3 +- web/js/index.ts | 4 +- webpack-client.config.ts | 28 ++ webpack-web.config.ts | 115 +++++++ webpack.config.ts | 21 +- 14 files changed, 378 insertions(+), 328 deletions(-) create mode 100644 client/js/index.ts rename loader/app/{ => targets}/app.ts (86%) rename loader/app/{ => targets}/certaccept.ts (98%) create mode 100644 webpack-client.config.ts create mode 100644 webpack-web.config.ts diff --git a/client/js/index.ts b/client/js/index.ts new file mode 100644 index 00000000..6805f46c --- /dev/null +++ b/client/js/index.ts @@ -0,0 +1,4 @@ +declare const __webpack_require__; +window["shared-require"] = __webpack_require__; +/* firstly assign the shared-require */ +setTimeout(() => require("tc-shared/main"), 0); \ No newline at end of file diff --git a/file.ts b/file.ts index 5a0a578f..c355595f 100644 --- a/file.ts +++ b/file.ts @@ -38,60 +38,24 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [ "local-path": "./shared/html/" }, - { /* javascript loader */ - "type": "js", - "search-pattern": /.*\.js$/, - "build-target": "dev", - "path": "loader/", - "local-path": "./shared/loader/" - }, - { /* javascript loader for releases */ + { /* javascript files as manifest.json */ "type": "js", - "search-pattern": /.*loader_[\S]+.min.js$/, - "build-target": "rel", - - "path": "loader/", - "local-path": "./shared/generated/" - }, - - { /* shared javascript files (WebRTC adapter) */ - "type": "js", - "search-pattern": /.*\.js$/, + "search-pattern": /.*$/, "build-target": "dev|rel", - "path": "adapter/", - "local-path": "./shared/adapter/" - }, - - { /* shared javascript files (development mode only) */ - "type": "js", - "search-pattern": /.*\.js$/, - "search-exclude": /(.*\/)?workers\/.*/, - "build-target": "dev", - "path": "js/", - "local-path": "./shared/js/" + "local-path": "./dist/" }, - { /* shared javascript mapping files (development mode only) */ + { /* loader javascript file */ "type": "js", - "search-pattern": /.*\.(js.map|ts)$/, - "search-exclude": /(.*\/)?workers\/.*/, - "build-target": "dev", - - "path": "js/", - "local-path": "./shared/js/", - "req-parm": ["--mappings"] - }, - - { /* shared generated worker codec */ - "type": "js", - "search-pattern": /(WorkerPOW.js)$/, + "search-pattern": /.*$/, "build-target": "dev|rel", - "path": "js/workers/", - "local-path": "./shared/js/workers/" + "path": "js/", + "local-path": "./loader/dist/" }, + { /* shared developer single css files */ "type": "css", "search-pattern": /.*\.css$/, @@ -156,14 +120,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [ "path": "img/", "local-path": "./shared/img/" - }, - { /* own webassembly files */ - "type": "wasm", - "search-pattern": /.*\.(wasm)/, - "build-target": "dev|rel", - - "path": "wat/", - "local-path": "./shared/wat/" } ]; @@ -204,28 +160,7 @@ const APP_FILE_LIST_CLIENT_SOURCE: ProjectResource[] = [ "path": "js/", "local-path": "./client/js/" - }, - - /* release specific */ - { /* web merged javascript files (shared inclusive) */ - "client-only": true, - "type": "js", - "search-pattern": /.*\.js$/, - "build-target": "rel", - - "path": "js/", - "local-path": "./client/generated/" - }, - { /* Add the shared generated files. Exclude the shared file because we're including it already */ - "client-only": true, - "type": "js", - "search-pattern": /.*\.js$/, - "search-exclude": /shared\.js(.map)?$/, - "build-target": "rel", - - "path": "js/", - "local-path": "./shared/generated/" - }, + } ]; const APP_FILE_LIST_WEB_SOURCE: ProjectResource[] = [ @@ -238,42 +173,6 @@ const APP_FILE_LIST_WEB_SOURCE: ProjectResource[] = [ "path": "wasm/", "local-path": "./asm/generated/" }, - { /* generated assembly javascript files */ - "web-only": true, - "type": "js", - "search-pattern": /.*\.(js)/, - "build-target": "dev|rel", - - "path": "wasm/", - "local-path": "./asm/generated/" - }, - { /* web generated worker codec */ - "web-only": true, - "type": "js", - "search-pattern": /(WorkerCodec.js)$/, - "build-target": "dev|rel", - - "path": "js/workers/", - "local-path": "./web/js/workers/" - }, - { /* web javascript files (development mode only) */ - "web-only": true, - "type": "js", - "search-pattern": /.*\.js$/, - "build-target": "dev", - - "path": "js/", - "local-path": "./web/js/" - }, - { /* web merged javascript files (shared inclusive) */ - "web-only": true, - "type": "js", - "search-pattern": /client(\.min)?\.js$/, - "build-target": "rel", - - "path": "js/", - "local-path": "./web/generated/" - }, { /* web css files */ "web-only": true, "type": "css", @@ -303,6 +202,7 @@ const APP_FILE_LIST_WEB_SOURCE: ProjectResource[] = [ } ]; +//TODO: This isn't needed anymore const APP_FILE_LIST_WEB_TEASPEAK: ProjectResource[] = [ /* special web.teaspeak.de only auth files */ { /* login page and api */ @@ -347,6 +247,7 @@ const APP_FILE_LIST_WEB_TEASPEAK: ProjectResource[] = [ } ]; +//FIXME: This isn't working right now const CERTACCEPT_FILE_LIST: ProjectResource[] = [ { /* html files */ "type": "html", @@ -444,7 +345,6 @@ const CLIENT_APP_FILE_LIST = [ ...APP_FILE_LIST_CLIENT_SOURCE ]; -/* const WEB_APP_FILE_LIST = [ ...APP_FILE_LIST_SHARED_SOURCE, ...APP_FILE_LIST_SHARED_VENDORS, @@ -452,146 +352,147 @@ const WEB_APP_FILE_LIST = [ ...APP_FILE_LIST_WEB_TEASPEAK, ...CERTACCEPT_FILE_LIST, ]; -*/ -const WEB_APP_FILE_LIST = [ - ...APP_FILE_LIST_SHARED_VENDORS, - { /* shared html and php files */ - "type": "html", - "search-pattern": /^.*([a-zA-Z]+)\.(html|php|json)$/, - "build-target": "dev|rel", - - "path": "./", - "local-path": "./shared/html/" - }, - { /* javascript files as manifest.json */ - "type": "js", - "search-pattern": /.*$/, - "build-target": "dev|rel", - - "path": "js/", - "local-path": "./dist/" - }, - { /* loader javascript file */ - "type": "js", - "search-pattern": /.*$/, - "build-target": "dev|rel", - - "path": "js/", - "local-path": "./loader/dist/" - }, - { /* shared developer single css files */ - "type": "css", - "search-pattern": /.*\.css$/, - "build-target": "dev", - - "path": "css/", - "local-path": "./shared/css/" - }, - { /* shared css mapping files (development mode only) */ - "type": "css", - "search-pattern": /.*\.(css.map|scss)$/, - "build-target": "dev", - - "path": "css/", - "local-path": "./shared/css/", - "req-parm": ["--mappings"] - }, - { /* shared release css files */ - "type": "css", - "search-pattern": /.*\.css$/, - "build-target": "rel", - - "path": "css/", - "local-path": "./shared/generated/" - }, - { /* shared release css files */ - "type": "css", - "search-pattern": /.*\.css$/, - "build-target": "rel", - - "path": "css/loader/", - "local-path": "./shared/css/loader/" - }, - { /* shared release css files */ - "type": "css", - "search-pattern": /.*\.css$/, - "build-target": "dev|rel", - - "path": "css/theme/", - "local-path": "./shared/css/theme/" - }, - { /* shared sound files */ - "type": "wav", - "search-pattern": /.*\.wav$/, - "build-target": "dev|rel", - - "path": "audio/", - "local-path": "./shared/audio/" - }, - { /* shared data sound files */ - "type": "json", - "search-pattern": /.*\.json/, - "build-target": "dev|rel", - - "path": "audio/", - "local-path": "./shared/audio/" - }, - { /* shared image files */ - "type": "img", - "search-pattern": /.*\.(svg|png)/, - "build-target": "dev|rel", - - "path": "img/", - "local-path": "./shared/img/" - }, - { /* own webassembly files */ - "type": "wasm", - "search-pattern": /.*\.(wasm)/, - "build-target": "dev|rel", - - "path": "wat/", - "local-path": "./shared/wat/" - }, - /* web specific */ - { /* generated assembly files */ - "web-only": true, - "type": "wasm", - "search-pattern": /.*\.(wasm)/, - "build-target": "dev|rel", - - "path": "wasm/", - "local-path": "./asm/generated/" - }, - { /* web css files */ - "web-only": true, - "type": "css", - "search-pattern": /.*\.css$/, - "build-target": "dev|rel", - - "path": "css/", - "local-path": "./web/css/" - }, - { /* web html files */ - "web-only": true, - "type": "html", - "search-pattern": /.*\.(php|html)/, - "build-target": "dev|rel", - - "path": "./", - "local-path": "./web/html/" - }, - { /* translations */ - "web-only": true, /* Only required for the web client */ - "type": "i18n", - "search-pattern": /.*\.(translation|json)/, - "build-target": "dev|rel", - - "path": "i18n/", - "local-path": "./shared/i18n/" - } -] as any; +//const WEB_APP_FILE_LIST = [ +// ...APP_FILE_LIST_SHARED_VENDORS, +// { /* shared html and php files */ +// "type": "html", +// "search-pattern": /^.*([a-zA-Z]+)\.(html|php|json)$/, +// "build-target": "dev|rel", +// +// "path": "./", +// "local-path": "./shared/html/" +// }, +// { /* javascript files as manifest.json */ +// "type": "js", +// "search-pattern": /.*$/, +// "build-target": "dev|rel", +// +// "path": "js/", +// "local-path": "./dist/" +// }, +// { /* loader javascript file */ +// "type": "js", +// "search-pattern": /.*$/, +// "build-target": "dev|rel", +// +// "path": "js/", +// "local-path": "./loader/dist/" +// }, +// { /* shared developer single css files */ +// "type": "css", +// "search-pattern": /.*\.css$/, +// "build-target": "dev", +// +// "path": "css/", +// "local-path": "./shared/css/" +// }, +// { /* shared css mapping files (development mode only) */ +// "type": "css", +// "search-pattern": /.*\.(css.map|scss)$/, +// "build-target": "dev", +// +// "path": "css/", +// "local-path": "./shared/css/", +// "req-parm": ["--mappings"] +// }, +// { /* shared release css files */ +// "type": "css", +// "search-pattern": /.*\.css$/, +// "build-target": "rel", +// +// "path": "css/", +// "local-path": "./shared/generated/" +// }, +// { /* shared release css files */ +// "type": "css", +// "search-pattern": /.*\.css$/, +// "build-target": "rel", +// +// "path": "css/loader/", +// "local-path": "./shared/css/loader/" +// }, +// { /* shared release css files */ +// "type": "css", +// "search-pattern": /.*\.css$/, +// "build-target": "dev|rel", +// +// "path": "css/theme/", +// "local-path": "./shared/css/theme/" +// }, +// { /* shared sound files */ +// "type": "wav", +// "search-pattern": /.*\.wav$/, +// "build-target": "dev|rel", +// +// "path": "audio/", +// "local-path": "./shared/audio/" +// }, +// { /* shared data sound files */ +// "type": "json", +// "search-pattern": /.*\.json/, +// "build-target": "dev|rel", +// +// "path": "audio/", +// "local-path": "./shared/audio/" +// }, +// { /* shared image files */ +// "type": "img", +// "search-pattern": /.*\.(svg|png)/, +// "build-target": "dev|rel", +// +// "path": "img/", +// "local-path": "./shared/img/" +// }, +// { /* own webassembly files */ +// "type": "wasm", +// "search-pattern": /.*\.(wasm)/, +// "build-target": "dev|rel", +// +// "path": "wat/", +// "local-path": "./shared/wat/" +// }, +// +// +// /* web specific */ +// { /* generated assembly files */ +// "web-only": true, +// "type": "wasm", +// "search-pattern": /.*\.(wasm)/, +// "build-target": "dev|rel", +// +// "path": "wasm/", +// "local-path": "./asm/generated/" +// }, +// { /* web css files */ +// "web-only": true, +// "type": "css", +// "search-pattern": /.*\.css$/, +// "build-target": "dev|rel", +// +// "path": "css/", +// "local-path": "./web/css/" +// }, +// { /* web html files */ +// "web-only": true, +// "type": "html", +// "search-pattern": /.*\.(php|html)/, +// "build-target": "dev|rel", +// +// "path": "./", +// "local-path": "./web/html/" +// }, +// { /* translations */ +// "web-only": true, /* Only required for the web client */ +// "type": "i18n", +// "search-pattern": /.*\.(translation|json)/, +// "build-target": "dev|rel", +// +// "path": "i18n/", +// "local-path": "./shared/i18n/" +// } +//] as any; //@ts-ignore declare module "fs-extra" { @@ -857,9 +758,11 @@ namespace server { return; } else if(url.query["type"] === "file") { let p = path.join(url.query["path"] as string, url.query["name"] as string).replace(/\\/g, "/"); + if(!p.startsWith("/")) p = "/" + p; if(p.endsWith(".html")) { - const np = await generator.search_http_file(files, p, options.search_options); - if(np) p = np; + const np = await generator.search_http_file(files, p.substr(0, p.length - 5) + ".php", options.search_options); + console.log("%s => %s", p, np); + if(np) p = p.substr(0, p.length - 5) + ".php"; } serve_file(p, url.query, response); return; diff --git a/loader/app/index.ts b/loader/app/index.ts index 403f20b7..31333ab9 100644 --- a/loader/app/index.ts +++ b/loader/app/index.ts @@ -1,5 +1,6 @@ -import * as loader from "./app"; +import * as loader from "./targets/app"; import * as loader_base from "./loader/loader"; export = loader_base; -loader.run(); \ No newline at end of file +/* let the loader register himself at the window first */ +setTimeout(loader.run, 0); \ No newline at end of file diff --git a/loader/app/loader/loader.ts b/loader/app/loader/loader.ts index 4faaebcc..2b45ace1 100644 --- a/loader/app/loader/loader.ts +++ b/loader/app/loader/loader.ts @@ -118,7 +118,13 @@ export function register_task(stage: Stage, task: Task) { if(current_stage > stage) { if(config.error) console.warn("Register loading task, but it had already been finished. Executing task anyways!"); - task.function().catch(error => { + + const promise = task.function(); + if(!promise) { + console.error("Loading task %s hasn't returned a promise!", task.name); + return; + } + promise.catch(error => { if(config.error) { console.error("Failed to execute delayed loader task!"); console.log(" - %s: %o", task.name, error); @@ -160,7 +166,12 @@ export async function execute() { for(const task of current_tasks) { try { if(config.verbose) console.debug("Executing loader %s (%d)", task.name, task.priority); - promises.push(task.function().catch(error => { + const promise = task.function(); + if(!promise) { + console.error("Loading task %s hasn't returned a promise!", task.name); + continue; + } + promises.push(promise.catch(error => { errors.push({ task: task, error: error diff --git a/loader/app/app.ts b/loader/app/targets/app.ts similarity index 86% rename from loader/app/app.ts rename to loader/app/targets/app.ts index 40966be4..17e4f95a 100644 --- a/loader/app/app.ts +++ b/loader/app/targets/app.ts @@ -1,4 +1,4 @@ -import * as loader from "./loader/loader"; +import * as loader from "../loader/loader"; declare global { interface Window { @@ -36,51 +36,30 @@ interface Manifest { }[]}; } +interface BuildDefinitions { + development: boolean, + version: string +} +declare global { + const __build: BuildDefinitions; +} + /* all javascript loaders */ const loader_javascript = { detect_type: async () => { - //TODO: Detect real version! - loader.set_version({ - backend: "-", - ui: ui_version(), - debug_mode: true, - type: "web" - }); - window.native_client = false; - return; - if(window.require) { - const request = new Request("js/proto.js"); - let file_path = request.url; - if(!file_path.startsWith("file://")) - throw "Invalid file path (" + file_path + ")"; - file_path = file_path.substring(process.platform === "win32" ? 8 : 7); - - const fs = node_require('fs'); - if(fs.existsSync(file_path)) { - //type = Type.CLIENT_DEBUG; - } else { - //type = Type.CLIENT_RELEASE; - } + if(window.native_client) { + loader.set_version({ + backend: "-", + ui: ui_version(), + debug_mode: __build.development, + type: "native" + }); } else { - /* test if js/proto.js is available. If so we're in debug mode */ - const request = new XMLHttpRequest(); - request.open('GET', "js/proto.js?_ts=" + Date.now(), true); - - await new Promise((resolve, reject) => { - request.onreadystatechange = () => { - if (request.readyState === 4){ - if (request.status === 404) { - //type = Type.WEB_RELEASE; - } else { - //type = Type.WEB_DEBUG; - } - resolve(); - } - }; - request.onerror = () => { - reject("Failed to detect app type"); - }; - request.send(); + loader.set_version({ + backend: "-", + ui: ui_version(), + debug_mode: __build.development, + type: "web" }); } }, @@ -125,7 +104,12 @@ const loader_javascript = { if(manifest.version !== 1) throw "invalid manifest version"; - await loader.scripts.load_multiple(manifest.chunks["shared-app"].map(e => "js/" + e.file), { + const chunk_name = loader.version().type === "web" ? "shared-app" : "client-app"; + if(!Array.isArray(manifest.chunks[chunk_name])) { + loader.critical_error("Missing entry chunk in manifest.json", "Chunk " + chunk_name + " is missing."); + throw "missing entry chunk"; + } + await loader.scripts.load_multiple(manifest.chunks[chunk_name].map(e => "js/" + e.file), { cache_tag: undefined, max_parallel_requests: -1 }); @@ -417,18 +401,21 @@ export function run() { window["Module"] = (window["Module"] || {}) as any; /* TeaClient */ if(node_require) { + window.native_client = true; + const path = node_require("path"); const remote = node_require('electron').remote; - module.paths.push(path.join(remote.app.getAppPath(), "/modules")); - module.paths.push(path.join(path.dirname(remote.getGlobal("browser-root")), "js")); - //TODO: HERE! - const connector = node_require("renderer"); + const render_entry = path.join(remote.app.getAppPath(), "/modules/", "renderer"); + const render = node_require(render_entry); + loader.register_task(loader.Stage.INITIALIZING, { name: "teaclient initialize", - function: connector.initialize, + function: render.initialize, priority: 40 }); + } else { + window.native_client = false; } if(!loader.running()) { diff --git a/loader/app/certaccept.ts b/loader/app/targets/certaccept.ts similarity index 98% rename from loader/app/certaccept.ts rename to loader/app/targets/certaccept.ts index f3da64e9..794240c6 100644 --- a/loader/app/certaccept.ts +++ b/loader/app/targets/certaccept.ts @@ -1,4 +1,4 @@ -import * as loader from "./loader/loader"; +import * as loader from "../loader/loader"; let is_debug = false; diff --git a/loader/webpack.config.js b/loader/webpack.config.js index baddf2d6..660424df 100644 --- a/loader/webpack.config.js +++ b/loader/webpack.config.js @@ -1,7 +1,9 @@ +const webpack = require("webpack"); const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const isDevelopment = process.env.NODE_ENV === 'development'; +let isDevelopment = process.env.NODE_ENV === 'development'; +isDevelopment = true; module.exports = { entry: path.join(__dirname, "app/index.ts"), devtool: 'inline-source-map', @@ -10,6 +12,12 @@ module.exports = { new MiniCssExtractPlugin({ filename: isDevelopment ? '[name].css' : '[name].[hash].css', chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }), + new webpack.DefinePlugin({ + __build: { + development: isDevelopment, + version: '0000' //TODO! + } }) ], module: { @@ -51,6 +59,7 @@ module.exports = { }, resolve: { extensions: ['.tsx', '.ts', '.js', ".scss"], + alias: { } }, output: { filename: 'loader.js', diff --git a/package.json b/package.json index 6ea49564..be69b873 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,11 @@ "rebuild-structure-web-dev": "php files.php generate web dev", "minify-web-rel-file": "terser --compress --mangle --ecma 6 --keep_classnames --keep_fnames --output", "start": "npm run compile-file-helper && node file.js ndevelop", - "build": "webpack --config webpack.config.js", + "build-web": "webpack --config webpack-web.config.js", + "watch-web": "webpack --watch --config webpack-web.config.js", + "build-client": "webpack --config webpack-client.config.js", + "watch-client": "webpack --watch --config webpack-client.config.js", "build-loader": "webpack --config loader/webpack.config.js", - "watch": "webpack --watch", "watch-loader": "webpack --watch --config loader/webpack.config.js" }, "author": "TeaSpeak (WolverinDEV)", diff --git a/shared/backend.d/audio/player.d.ts b/shared/backend.d/audio/player.d.ts index ef63390d..13bf0c60 100644 --- a/shared/backend.d/audio/player.d.ts +++ b/shared/backend.d/audio/player.d.ts @@ -3,17 +3,13 @@ import {Device} from "tc-shared/audio/player"; export function initialize() : boolean; export function initialized() : boolean; -export function context() : AudioContext; export function get_master_volume() : number; export function set_master_volume(volume: number); -export function destination() : AudioNode; - export function on_ready(cb: () => any); export function available_devices() : Promise<Device[]>; export function set_device(device_id: string) : Promise<void>; - export function current_device() : Device; export function initializeFromGesture(); \ No newline at end of file diff --git a/shared/js/main.ts b/shared/js/main.ts index 8895e7f6..995aa23e 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -29,6 +29,7 @@ import * as ppt from "tc-backend/ppt"; /* required import for init */ require("./proto").initialize(); require("./ui/elements/ContextDivider").initialize(); +require("./connection/CommandHandler"); /* else it might not get bundled because only the backends are accessing it */ const js_render = window.jsrender || $; const native_client = window.require !== undefined; @@ -233,7 +234,7 @@ interface Window { } */ -function handle_connect_request(properties: bipc.connect.ConnectRequestData, connection: ConnectionHandler) { +export function handle_connect_request(properties: bipc.connect.ConnectRequestData, connection: ConnectionHandler) { const profile_uuid = properties.profile || (profiles.default_profile() || {id: 'default'}).id; const profile = profiles.find_profile(profile_uuid) || profiles.default_profile(); const username = properties.username || profile.connect_username(); diff --git a/web/js/index.ts b/web/js/index.ts index ab15445c..9ba3f4c6 100644 --- a/web/js/index.ts +++ b/web/js/index.ts @@ -1,4 +1,4 @@ const webrtc_adapter = require("webrtc-adapter"); +/* typescript keep alive */ let _x = (webrtc_adapter || "").toString(); const tc = require("tc-shared/main"); - -console.log(webrtc_adapter); \ No newline at end of file +export = tc; \ No newline at end of file diff --git a/webpack-client.config.ts b/webpack-client.config.ts new file mode 100644 index 00000000..279fd6b1 --- /dev/null +++ b/webpack-client.config.ts @@ -0,0 +1,28 @@ +import * as path from "path"; +const config = require("./webpack.config"); + +let isDevelopment = process.env.NODE_ENV === 'development'; +isDevelopment = true; + +config.entry = { + "client-app": "./client/js/index.ts" +}; + +config.resolve.alias = { + "tc-shared": path.resolve(__dirname, "shared/js"), + /* backend hasn't declared but its available via "require()" */ + "tc-backend": path.resolve(__dirname, "shared/backend.d"), +}; + +config.externals = [ + { + "tc-loader": "window loader" + }, + (context, request: string, callback) => { + if (request.startsWith("tc-backend/")) + return callback(null, `window["backend-loader"].require("${request}")`); + callback(); + } +]; + +export = config; \ No newline at end of file diff --git a/webpack-web.config.ts b/webpack-web.config.ts new file mode 100644 index 00000000..a5b51b30 --- /dev/null +++ b/webpack-web.config.ts @@ -0,0 +1,115 @@ +import * as ts from "typescript"; +import trtransformer, {Config} from "./tools/trgen/ts_transformer"; + +const path = require('path'); +const webpack = require("webpack"); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const ManifestGenerator = require("./webpack/ManifestPlugin"); +const WorkerPlugin = require('worker-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); + +let isDevelopment = process.env.NODE_ENV === 'development'; +isDevelopment = true; +module.exports = { + entry: { + "shared-app": "./web/js/index.ts" + }, + devtool: isDevelopment ? "inline-source-map" : undefined, + mode: isDevelopment ? "development" : "production", + plugins: [ + new CleanWebpackPlugin(), + new MiniCssExtractPlugin({ + filename: isDevelopment ? '[name].css' : '[name].[hash].css', + chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' + }), + new ManifestGenerator({ + file: path.join(__dirname, "dist/manifest.json") + }), + new WorkerPlugin(), + //new BundleAnalyzerPlugin() + /* + new CircularDependencyPlugin({ + //exclude: /a\.js|node_modules/, + failOnError: true, + allowAsyncCycles: false, + cwd: process.cwd(), + }) + */ + new webpack.optimize.AggressiveSplittingPlugin({ + minSize: 1024 * 8, + maxSize: 1024 * 128 + }) + ], + module: { + rules: [ + { + test: /\.s[ac]ss$/, + loader: [ + isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: true, + sourceMap: isDevelopment + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: isDevelopment + } + } + ] + }, + { + test: /\.tsx?$/, + exclude: /node_modules/, + + loader: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + getCustomTransformers: (prog: ts.Program) => { + return { + before: [trtransformer(prog, {})] + }; + } + } + } + ] + }, + { + test: /\.was?t$/, + loader: [ + "./webpack/WatLoader.js" + ] + } + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js', ".scss"], + alias: { + "tc-shared": path.resolve(__dirname, "shared/js"), + "tc-backend/web": path.resolve(__dirname, "web/js"), + "tc-backend": path.resolve(__dirname, "web/js"), + "tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"), + //"tc-backend": path.resolve(__dirname, "shared/backend.d"), + }, + }, + externals: { + "tc-loader": "window loader" + }, + output: { + filename: '[contenthash].js', + path: path.resolve(__dirname, 'dist'), + publicPath: "js/" + }, + optimization: { + splitChunks: { }, + minimize: !isDevelopment, + minimizer: [new TerserPlugin()] + } +}; \ No newline at end of file diff --git a/webpack.config.ts b/webpack.config.ts index a5b51b30..e3719ab2 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -12,10 +12,9 @@ const { CleanWebpackPlugin } = require('clean-webpack-plugin'); let isDevelopment = process.env.NODE_ENV === 'development'; isDevelopment = true; -module.exports = { - entry: { - "shared-app": "./web/js/index.ts" - }, +export = { + entry: {}, /* will be individually set */ + devtool: isDevelopment ? "inline-source-map" : undefined, mode: isDevelopment ? "development" : "production", plugins: [ @@ -37,11 +36,11 @@ module.exports = { cwd: process.cwd(), }) */ - new webpack.optimize.AggressiveSplittingPlugin({ + isDevelopment ? undefined : new webpack.optimize.AggressiveSplittingPlugin({ minSize: 1024 * 8, maxSize: 1024 * 128 }) - ], + ].filter(e => !!e), module: { rules: [ { @@ -91,19 +90,13 @@ module.exports = { }, resolve: { extensions: ['.tsx', '.ts', '.js', ".scss"], - alias: { - "tc-shared": path.resolve(__dirname, "shared/js"), - "tc-backend/web": path.resolve(__dirname, "web/js"), - "tc-backend": path.resolve(__dirname, "web/js"), - "tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"), - //"tc-backend": path.resolve(__dirname, "shared/backend.d"), - }, + alias: { }, /* will be individually set */ }, externals: { "tc-loader": "window loader" }, output: { - filename: '[contenthash].js', + filename: isDevelopment ? '[name].js' : '[contenthash].js', path: path.resolve(__dirname, 'dist'), publicPath: "js/" }, From b297147f985334b5fc3f2d6d0b9b31cf591eadad Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Wed, 1 Apr 2020 15:40:45 +0200 Subject: [PATCH 20/23] Some small error fixes --- shared/js/main.ts | 4 +--- shared/js/proto.ts | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/shared/js/main.ts b/shared/js/main.ts index 995aa23e..d365dc2a 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -615,6 +615,4 @@ loader.register_task(loader.Stage.LOADED, { } }, priority: 20 -}); - -export = {}; \ No newline at end of file +}); \ No newline at end of file diff --git a/shared/js/proto.ts b/shared/js/proto.ts index c21b8a2c..4a3f7bd7 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -65,12 +65,9 @@ declare global { let remarkable: typeof window.remarkable; - class webkitAudioContext extends AudioContext {} - class webkitOfflineAudioContext extends OfflineAudioContext {} - interface Window { - readonly webkitAudioContext: typeof webkitAudioContext; - readonly AudioContext: typeof webkitAudioContext; + readonly webkitAudioContext: typeof AudioContext; + readonly AudioContext: typeof OfflineAudioContext; readonly OfflineAudioContext: typeof OfflineAudioContext; readonly webkitOfflineAudioContext: typeof webkitOfflineAudioContext; readonly RTCPeerConnection: typeof RTCPeerConnection; From c460d05ee5af12f60cc98522d581759da69db6ea Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Wed, 1 Apr 2020 21:47:33 +0200 Subject: [PATCH 21/23] Improved the webpack script and using constants for build version detection --- file.ts | 11 --- loader/app/index.ts | 8 +- loader/app/loader/loader.ts | 43 ++++----- loader/app/targets/app.ts | 70 +++++++-------- loader/exports/loader.d.ts | 10 ++- loader/webpack.config.js | 71 --------------- package.json | 4 +- shared/js/FileManager.ts | 1 + shared/js/MessageFormatter.ts | 2 +- shared/js/log.ts | 2 +- shared/js/main.ts | 4 +- shared/js/settings.ts | 2 +- shared/js/ui/frames/MenuBar.ts | 8 +- shared/js/ui/modal/ModalAbout.ts | 6 +- shared/js/ui/modal/ModalKeySelect.ts | 2 +- shared/js/ui/modal/ModalNewcomer.ts | 4 +- shared/js/ui/modal/ModalSettings.ts | 2 +- shared/js/utils/helpers.ts | 4 +- tools/trgen/ts_generator.ts | 84 ++++++++++++------ tools/trgen/ts_transformer.ts | 11 ++- webpack-client.config.ts | 29 +++---- webpack-web.config.ts | 125 +++------------------------ webpack.config.ts | 55 +++++++++--- webpack/ManifestPlugin.ts | 41 ++++++--- webpack/build-definitions.d.ts | 16 ++++ 25 files changed, 260 insertions(+), 355 deletions(-) delete mode 100644 loader/webpack.config.js create mode 100644 webpack/build-definitions.d.ts diff --git a/file.ts b/file.ts index c355595f..a011f464 100644 --- a/file.ts +++ b/file.ts @@ -37,8 +37,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [ "path": "./", "local-path": "./shared/html/" }, - - { /* javascript files as manifest.json */ "type": "js", "search-pattern": /.*$/, @@ -47,15 +45,6 @@ const APP_FILE_LIST_SHARED_SOURCE: ProjectResource[] = [ "path": "js/", "local-path": "./dist/" }, - { /* loader javascript file */ - "type": "js", - "search-pattern": /.*$/, - "build-target": "dev|rel", - - "path": "js/", - "local-path": "./loader/dist/" - }, - { /* shared developer single css files */ "type": "css", "search-pattern": /.*\.css$/, diff --git a/loader/app/index.ts b/loader/app/index.ts index 31333ab9..c020e98c 100644 --- a/loader/app/index.ts +++ b/loader/app/index.ts @@ -1,6 +1,8 @@ import * as loader from "./targets/app"; import * as loader_base from "./loader/loader"; -export = loader_base; -/* let the loader register himself at the window first */ -setTimeout(loader.run, 0); \ No newline at end of file +window["loader"] = loader_base; + /* let the loader register himself at the window first */ +setTimeout(loader.run, 0); + +export {}; \ No newline at end of file diff --git a/loader/app/loader/loader.ts b/loader/app/loader/loader.ts index 2b45ace1..c492fbfd 100644 --- a/loader/app/loader/loader.ts +++ b/loader/app/loader/loader.ts @@ -1,5 +1,3 @@ -import {AppVersion} from "tc-loader"; -import {LoadSyntaxError, script_name} from "./utils"; import * as script_loader from "./script_loader"; import * as style_loader from "./style_loader"; import * as template_loader from "./template_loader"; @@ -75,38 +73,35 @@ const tasks: {[key:number]:Task[]} = {}; /* test if all files shall be load from cache or fetch again */ function loader_cache_tag() { - const app_version = (() => { - const version_node = document.getElementById("app_version"); - if(!version_node) return undefined; - - const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; - if(!version) return undefined; - - if(!version || version == "unknown" || version.replace(/0+/, "").length == 0) - return undefined; - - return version; - })(); - if(config.verbose) console.log("Found current app version: %o", app_version); - - if(!app_version) { - /* TODO add warning */ + if(__build.mode === "debug") { cache_tag = "?_ts=" + Date.now(); return; } + const cached_version = localStorage.getItem("cached_version"); - if(!cached_version || cached_version != app_version) { + if(!cached_version || cached_version !== __build.version) { register_task(Stage.LOADED, { priority: 0, name: "cached version updater", function: async () => { - localStorage.setItem("cached_version", app_version); + localStorage.setItem("cached_version", __build.version); } }); } - cache_tag = "?_version=" + app_version; + cache_tag = "?_version=" + __build.version; } +export type ModuleMapping = { + application: string, + modules: { + "id": string, + "context": string, + "resource": string + }[] +}; +const module_mapping_: ModuleMapping[] = []; +export function module_mapping() : ModuleMapping[] { return module_mapping_; } + export function get_cache_version() { return cache_tag; } export function finished() { @@ -259,12 +254,6 @@ export function hide_overlay() { }); } -/* versions management */ -let version_: AppVersion; -export function version() : AppVersion { return version_; } -export function set_version(version: AppVersion) { version_ = version; } - - /* critical error handler */ export type ErrorHandler = (message: string, detail: string) => void; let _callback_critical_error: ErrorHandler; diff --git a/loader/app/targets/app.ts b/loader/app/targets/app.ts index 17e4f95a..6a1d810d 100644 --- a/loader/app/targets/app.ts +++ b/loader/app/targets/app.ts @@ -31,38 +31,20 @@ interface Manifest { version: number; chunks: {[key: string]: { - hash: string, - file: string - }[]}; -} - -interface BuildDefinitions { - development: boolean, - version: string -} -declare global { - const __build: BuildDefinitions; + files: { + hash: string, + file: string + }[], + modules: { + id: string, + context: string, + resource: string + }[] + }}; } /* all javascript loaders */ const loader_javascript = { - detect_type: async () => { - if(window.native_client) { - loader.set_version({ - backend: "-", - ui: ui_version(), - debug_mode: __build.development, - type: "native" - }); - } else { - loader.set_version({ - backend: "-", - ui: ui_version(), - debug_mode: __build.development, - type: "web" - }); - } - }, load_scripts: async () => { if(!window.require) { await loader.scripts.load(["vendor/jquery/jquery.min.js"], { cache_tag: cache_tag() }); @@ -101,15 +83,19 @@ const loader_javascript = { loader.critical_error("Failed to load manifest.json", error); throw "failed to load manifest.json"; } - if(manifest.version !== 1) + if(manifest.version !== 2) throw "invalid manifest version"; - const chunk_name = loader.version().type === "web" ? "shared-app" : "client-app"; - if(!Array.isArray(manifest.chunks[chunk_name])) { + const chunk_name = __build.entry_chunk_name; + if(typeof manifest.chunks[chunk_name] !== "object") { loader.critical_error("Missing entry chunk in manifest.json", "Chunk " + chunk_name + " is missing."); throw "missing entry chunk"; } - await loader.scripts.load_multiple(manifest.chunks[chunk_name].map(e => "js/" + e.file), { + loader.module_mapping().push({ + application: chunk_name, + modules: manifest.chunks[chunk_name].modules + }); + await loader.scripts.load_multiple(manifest.chunks[chunk_name].files.map(e => "js/" + e.file), { cache_tag: undefined, max_parallel_requests: -1 }); @@ -154,7 +140,7 @@ const loader_style = { ["vendor/highlight/styles/darcula.css", ""], /* empty string means not required */ ], options); - if(loader.version().debug_mode) { + if(__build.mode === "debug") { await loader_style.load_style_debug(); } else { await loader_style.load_style_release(); @@ -293,12 +279,6 @@ loader.register_task(loader.Stage.INITIALIZING, { priority: 20 }); -loader.register_task(loader.Stage.INITIALIZING, { - name: "app type test", - function: loader_javascript.detect_type, - priority: 20 -}); - loader.register_task(loader.Stage.JAVASCRIPT, { name: "javascript", function: loader_javascript.load_scripts, @@ -398,9 +378,14 @@ loader.register_task(loader.Stage.SETUP, { }); export function run() { - window["Module"] = (window["Module"] || {}) as any; + window["Module"] = (window["Module"] || {}) as any; /* Why? */ + /* TeaClient */ if(node_require) { + if(__build.target !== "client") { + loader.critical_error("App seems not to be compiled for the client.", "This app has been compiled for " + __build.target); + return; + } window.native_client = true; const path = node_require("path"); @@ -415,6 +400,11 @@ export function run() { priority: 40 }); } else { + if(__build.target !== "web") { + loader.critical_error("App seems not to be compiled for the web.", "This app has been compiled for " + __build.target); + return; + } + window.native_client = false; } diff --git a/loader/exports/loader.d.ts b/loader/exports/loader.d.ts index d947c9df..3620bc10 100644 --- a/loader/exports/loader.d.ts +++ b/loader/exports/loader.d.ts @@ -61,7 +61,15 @@ export enum Stage { DONE } -export function version() : AppVersion; +export type ModuleMapping = { + application: string, + modules: { + "id": string, + "context": string, + "resource": string + }[] +}; +export function module_mapping() : ModuleMapping; export function finished(); export function running(); diff --git a/loader/webpack.config.js b/loader/webpack.config.js deleted file mode 100644 index 660424df..00000000 --- a/loader/webpack.config.js +++ /dev/null @@ -1,71 +0,0 @@ -const webpack = require("webpack"); -const path = require('path'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); - -let isDevelopment = process.env.NODE_ENV === 'development'; -isDevelopment = true; -module.exports = { - entry: path.join(__dirname, "app/index.ts"), - devtool: 'inline-source-map', - mode: "development", - plugins: [ - new MiniCssExtractPlugin({ - filename: isDevelopment ? '[name].css' : '[name].[hash].css', - chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' - }), - new webpack.DefinePlugin({ - __build: { - development: isDevelopment, - version: '0000' //TODO! - } - }) - ], - module: { - rules: [ - { - test: /\.s[ac]ss$/, - loader: [ - //isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, - 'style-loader', - { - loader: 'css-loader', - options: { - modules: true, - sourceMap: isDevelopment - } - }, - { - loader: 'sass-loader', - options: { - sourceMap: isDevelopment - } - } - ] - }, - { - test: /\.tsx?$/, - exclude: /node_modules/, - - loader: [ - { - loader: 'ts-loader', - options: { - transpileOnly: true - } - } - ] - }, - ], - }, - resolve: { - extensions: ['.tsx', '.ts', '.js', ".scss"], - alias: { } - }, - output: { - filename: 'loader.js', - path: path.resolve(__dirname, 'dist'), - library: "loader", - libraryTarget: "window" //"var" | "assign" | "this" | "window" | "self" | "global" | "commonjs" | "commonjs2" | "commonjs-module" | "amd" | "amd-require" | "umd" | "umd2" | "jsonp" | "system" - }, - optimization: { } -}; \ No newline at end of file diff --git a/package.json b/package.json index be69b873..9069e3a7 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,7 @@ "build-web": "webpack --config webpack-web.config.js", "watch-web": "webpack --watch --config webpack-web.config.js", "build-client": "webpack --config webpack-client.config.js", - "watch-client": "webpack --watch --config webpack-client.config.js", - "build-loader": "webpack --config loader/webpack.config.js", - "watch-loader": "webpack --watch --config loader/webpack.config.js" + "watch-client": "webpack --watch --config webpack-client.config.js" }, "author": "TeaSpeak (WolverinDEV)", "license": "ISC", diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index a1167ffe..3c9897d7 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -1,4 +1,5 @@ import * as log from "tc-shared/log"; +import * as hex from "tc-shared/crypto/hex"; import {LogCategory} from "tc-shared/log"; import {ChannelEntry} from "tc-shared/ui/channel"; import {ConnectionHandler} from "tc-shared/ConnectionHandler"; diff --git a/shared/js/MessageFormatter.ts b/shared/js/MessageFormatter.ts index 6aa6fe2f..e022a08b 100644 --- a/shared/js/MessageFormatter.ts +++ b/shared/js/MessageFormatter.ts @@ -126,7 +126,7 @@ export namespace bbcode { }, name: tr("Open URL in Browser"), type: contextmenu.MenuEntryType.ENTRY, - visible: loader.version().type === "native" && false // Currently not possible + visible: __build.target === "client" && false // Currently not possible }, contextmenu.Entry.HR(), { callback: () => copy_to_clipboard(url), name: tr("Copy URL to clipboard"), diff --git a/shared/js/log.ts b/shared/js/log.ts index 21c4768a..e05fa71c 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -250,7 +250,7 @@ export class Group { loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { name: "log enabled initialisation", - function: async () => initialize(loader.version().debug_mode ? LogType.TRACE : LogType.INFO), + function: async () => initialize(__build.mode === "debug" ? LogType.TRACE : LogType.INFO), priority: 150 }); diff --git a/shared/js/main.ts b/shared/js/main.ts index d365dc2a..f6c9b165 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -144,7 +144,7 @@ async function initialize_app() { try { //Initialize main template const main = $("#tmpl_main").renderTag({ multi_session: !settings.static_global(Settings.KEY_DISABLE_MULTI_SESSION), - app_version: loader.version().ui + app_version: __build.version }).dividerfy(); $("body").append(main); @@ -589,7 +589,7 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { try { await initialize(); - if(loader.version().type == "web") { + if(__build.target == "web") { loader.register_task(loader.Stage.LOADED, task_certificate_callback); } else { loader.register_task(loader.Stage.LOADED, task_teaweb_starter); diff --git a/shared/js/settings.ts b/shared/js/settings.ts index 10ad216a..86e56622 100644 --- a/shared/js/settings.ts +++ b/shared/js/settings.ts @@ -515,4 +515,4 @@ export class ServerSettings extends SettingsBase { } } -export let settings: Settings; \ No newline at end of file +export let settings: Settings = null; \ No newline at end of file diff --git a/shared/js/ui/frames/MenuBar.ts b/shared/js/ui/frames/MenuBar.ts index 0ef579ad..24f4b976 100644 --- a/shared/js/ui/frames/MenuBar.ts +++ b/shared/js/ui/frames/MenuBar.ts @@ -370,7 +370,7 @@ export function initialize() { return true; }}; - if(loader.version().type !== "web") { + if(__build.target !== "web") { menu.append_hr(); item = menu.append_item(tr("Quit")); @@ -532,7 +532,7 @@ export function initialize() { { const menu = driver_.append_item(tr("Help")); - if(loader.version().type !== "web") { + if(__build.target !== "web") { item = menu.append_item(tr("Check for updates")); item.click(() => native_actions.check_native_update()); @@ -546,7 +546,7 @@ export function initialize() { item = menu.append_item(tr("Visit TeaSpeak forum")); item.click(() => window.open('https://forum.teaspeak.de/', '_blank')); - if(loader.version().type !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) { + if(__build.target !== "web" && typeof(native_actions.show_dev_tools) === "function" && native_actions.show_dev_tools()) { menu.append_hr(); item = menu.append_item(tr("Open developer tools")); item.click(() => native_actions.open_dev_tools()); @@ -556,7 +556,7 @@ export function initialize() { } menu.append_hr(); - item = menu.append_item(loader.version().type === "web" ? tr("About TeaWeb") : tr("About TeaClient")); + item = menu.append_item(__build.target === "web" ? tr("About TeaWeb") : tr("About TeaClient")); item.click(() => spawnAbout()) } diff --git a/shared/js/ui/modal/ModalAbout.ts b/shared/js/ui/modal/ModalAbout.ts index 693c5068..828a36b5 100644 --- a/shared/js/ui/modal/ModalAbout.ts +++ b/shared/js/ui/modal/ModalAbout.ts @@ -27,9 +27,9 @@ export function spawnAbout() { header: tr("About"), body: () => { let tag = $("#tmpl_about").renderTag({ - client: loader.version().type !== "web", + client: __build.target !== "web", - version_client: loader.version().type === "web" ? app_version || "in-dev" : "loading...", + version_client: __build.target === "web" ? app_version || "in-dev" : "loading...", version_ui: app_version || "in-dev", version_timestamp: !!app_version ? format_date(Date.now()) : "--" @@ -43,7 +43,7 @@ export function spawnAbout() { connectModal.htmlTag.find(".modal-body").addClass("modal-about"); connectModal.open(); - if(loader.version().type !== "web") { + if(__build.target !== "web") { (window as any).native.client_version().then(version => { connectModal.htmlTag.find(".version-client").text(version); }).catch(error => { diff --git a/shared/js/ui/modal/ModalKeySelect.ts b/shared/js/ui/modal/ModalKeySelect.ts index 5bc0cce7..aa4bce5d 100644 --- a/shared/js/ui/modal/ModalKeySelect.ts +++ b/shared/js/ui/modal/ModalKeySelect.ts @@ -34,7 +34,7 @@ export function spawnKeySelect(callback: (key?: KeyEvent) => void) { button_save.on('click', () => { - if(loader.version().type !== "web") { + if(__build.version !== "web") { /* Because pressing the close button is also a mouse action */ if(current_key_age + 1000 > Date.now() && current_key.key_code == "MOUSE2") current_key = last_key; diff --git a/shared/js/ui/modal/ModalNewcomer.ts b/shared/js/ui/modal/ModalNewcomer.ts index 2a93ac9d..ba699aae 100644 --- a/shared/js/ui/modal/ModalNewcomer.ts +++ b/shared/js/ui/modal/ModalNewcomer.ts @@ -24,9 +24,9 @@ const last_step: {[key: string]:string} = (() => { export function openModalNewcomer() : Modal { let modal = createModal({ - header: tra("Welcome to the {}", loader.version().type === "web" ? "TeaSpeak - Web client" : "TeaSpeak - Client"), + header: tra("Welcome to the {}", __build.version === "web" ? "TeaSpeak - Web client" : "TeaSpeak - Client"), body: () => $("#tmpl_newcomer").renderTag({ - is_web: loader.version().type === "web" + is_web: __build.version === "web" }).children(), footer: null, diff --git a/shared/js/ui/modal/ModalSettings.ts b/shared/js/ui/modal/ModalSettings.ts index 6e3c6c58..5ba660f8 100644 --- a/shared/js/ui/modal/ModalSettings.ts +++ b/shared/js/ui/modal/ModalSettings.ts @@ -331,7 +331,7 @@ function settings_general_language(container: JQuery, modal: Modal) { } container.find(".button-restart").on('click', () => { - if(loader.version().type === "web") { + if(__build.target === "web") { location.reload(); } else { createErrorModal(tr("Not implemented"), tr("Client restart isn't implemented.<br>Please do it manually!")).open(); diff --git a/shared/js/utils/helpers.ts b/shared/js/utils/helpers.ts index 9528f4d8..35e9f0db 100644 --- a/shared/js/utils/helpers.ts +++ b/shared/js/utils/helpers.ts @@ -1,6 +1,8 @@ +import * as sha1 from "../crypto/sha"; + export function hashPassword(password: string) : Promise<string> { return new Promise<string>((resolve, reject) => { - sha.sha1(password).then(result => { + sha1.sha1(password).then(result => { resolve(btoa(String.fromCharCode.apply(null, new Uint8Array(result)))); }); }); diff --git a/tools/trgen/ts_generator.ts b/tools/trgen/ts_generator.ts index 4ee1f70e..c65f6d79 100644 --- a/tools/trgen/ts_generator.ts +++ b/tools/trgen/ts_generator.ts @@ -64,7 +64,7 @@ function _generate(config: Configuration, node: ts.Node, result: TranslationEntr node.forEachChild(n => _generate(config, n, result)); } -function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] { +function create_unique_check(config: Configuration, source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] { const nodes: ts.Node[] = [], blocked_nodes: ts.Statement[] = []; const node_path = (node: ts.Node) => { @@ -90,10 +90,10 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression /* initialization */ { - const declarations = ts.createElementAccess(variable, ts.createLiteral("declared")); + const declarations = ts.createElementAccess(variable, ts.createLiteral(config.variables.declarations)); nodes.push(ts.createAssignment(declarations, ts.createBinary(declarations, SyntaxKind.BarBarToken, ts.createAssignment(declarations, ts.createObjectLiteral())))); - declarations_file = ts.createElementAccess(variable, ts.createLiteral("declared_files")); + declarations_file = ts.createElementAccess(variable, ts.createLiteral(config.variables.declare_files)); nodes.push(ts.createAssignment(declarations_file, ts.createBinary(declarations_file, SyntaxKind.BarBarToken, ts.createAssignment(declarations_file, ts.createObjectLiteral())))); variable = declarations; @@ -126,8 +126,8 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression const for_variable_name = ts.createLoopVariable(); const for_variable_path = ts.createLoopVariable(); const for_declaration = ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createObjectBindingPattern([ - ts.createBindingElement(undefined, "name", for_variable_name, undefined), - ts.createBindingElement(undefined, "path", for_variable_path, undefined)]) + ts.createBindingElement(undefined, config.optimized ? "n": "name", for_variable_name, undefined), + ts.createBindingElement(undefined, config.optimized ? "p": "path", for_variable_path, undefined)]) , undefined, undefined)]); let for_block: ts.Statement; @@ -151,8 +151,8 @@ function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression let block = ts.createForOf(undefined, for_declaration, ts.createArrayLiteral( [...variables.map(e => ts.createObjectLiteral([ - ts.createPropertyAssignment("name", ts.createLiteral(e.name)), - ts.createPropertyAssignment("path", ts.createLiteral(node_path(e.node))) + ts.createPropertyAssignment(config.optimized ? "n": "name", ts.createLiteral(e.name)), + ts.createPropertyAssignment(config.optimized ? "p": "path", ts.createLiteral(node_path(e.node))) ])) ]) , for_block); @@ -166,52 +166,69 @@ export function transform(config: Configuration, context: ts.TransformationConte const cache: VolatileTransformConfig = {} as any; cache.translations = []; + config.variables = (config.variables || {}) as any; + config.variables.base = config.variables.base || (config.optimized ? "__tr" : "_translations"); + config.variables.declare_files = config.variables.declare_files || (config.optimized ? "f" : "declare_files"); + config.variables.declarations = config.variables.declarations || (config.optimized ? "d" : "definitions"); + //Initialize nodes const extra_nodes: ts.Node[] = []; { cache.nodes = {} as any; if(config.use_window) { const window = ts.createIdentifier("window"); - let translation_map = ts.createPropertyAccess(window, ts.createIdentifier("_translations")); + let translation_map = ts.createPropertyAccess(window, ts.createIdentifier(config.variables.base)); const new_translations = ts.createAssignment(translation_map, ts.createObjectLiteral()); let translation_map_init: ts.Expression = ts.createBinary(translation_map, ts.SyntaxKind.BarBarToken, new_translations); translation_map_init = ts.createParen(translation_map_init); + extra_nodes.push(translation_map_init); cache.nodes = { - translation_map: translation_map, - translation_map_init: translation_map_init + translation_map: translation_map + }; + } else if(config.module) { + cache.nodes = { + translation_map: ts.createIdentifier(config.variables.base) }; - } else { - const variable_name = "_translations"; - const variable_map = ts.createIdentifier(variable_name); + extra_nodes.push(ts.createVariableDeclarationList([ + ts.createVariableDeclaration(config.variables.base, undefined, ts.createObjectLiteral()) + ], ts.NodeFlags.Const), ts.createToken(SyntaxKind.SemicolonToken)); + } else { + const variable_map = ts.createIdentifier(config.variables.base); const inline_if = ts.createBinary(ts.createBinary(ts.createTypeOf(variable_map), SyntaxKind.ExclamationEqualsEqualsToken, ts.createLiteral("undefined")), ts.SyntaxKind.BarBarToken, ts.createAssignment(variable_map, ts.createObjectLiteral())); cache.nodes = { translation_map: variable_map, - translation_map_init: variable_map }; - //ts.createVariableDeclarationList([ts.createVariableDeclaration(variable_name)], ts.NodeFlags.Let) extra_nodes.push(inline_if); } } + const used_names = [config.variables.declarations, config.variables.declare_files]; const generated_names: { name: string, node: ts.Node }[] = []; + let generator_base = 0; cache.name_generator = (config, node, message) => { const characters = "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - let name = ""; - while(name.length < 8) { - const char = characters[Math.floor(Math.random() * characters.length)]; - name = name + char; - if(name[0] >= '0' && name[0] <= '9') - name = name.substr(1) || ""; - } + let name; + do { + name = ""; + + if(config.module) { + name = "_" + generator_base++; + } else { + /* Global namespace. We've to generate a random name so no duplicates happen */ + while(name.length < 8) { + const char = characters[Math.floor(Math.random() * characters.length)]; + name = name + char; + if(name[0] >= '0' && name[0] <= '9') + name = name.substr(1) || ""; + } + } + } while(used_names.findIndex(e => e === name) !== -1); - //FIXME - //if(generated_names.indexOf(name) != -1) - // return cache.name_generator(config, node, message); generated_names.push({name: name, node: node}); return name; }; @@ -221,7 +238,10 @@ export function transform(config: Configuration, context: ts.TransformationConte return replace_processor(config, cache, node, source_file); } source_file = ts.visitNode(source_file, visit); - extra_nodes.push(...create_unique_check(source_file, cache.nodes.translation_map_init, generated_names)); + if(!config.module) { + /* we don't need a unique check because we're just in our scope */ + extra_nodes.push(...create_unique_check(config, source_file, cache.nodes.translation_map, generated_names)); + } source_file = ts.updateSourceFileNode(source_file, [...(extra_nodes as any[]), ...source_file.statements], source_file.isDeclarationFile, source_file.referencedFiles, source_file.typeReferenceDirectives, source_file.hasNoDefaultLib, source_file.referencedFiles); @@ -262,7 +282,7 @@ export function replace_processor(config: Configuration, cache: VolatileTransfor console.log("Message: %o", object.text || object.getText(source_file)); const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText(source_file))); - const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name); + const variable_init = ts.createPropertyAccess(cache.nodes.translation_map, variable_name); const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name); const new_variable = ts.createAssignment(variable, call); @@ -284,6 +304,15 @@ export interface Configuration { use_window?: boolean; replace_cache?: boolean; verbose?: boolean; + + optimized?: boolean; + module?: boolean; + + variables?: { + base: string, + declarations: string, + declare_files: string + } } export interface TransformResult { @@ -294,7 +323,6 @@ export interface TransformResult { interface VolatileTransformConfig { nodes: { translation_map: ts.Expression; - translation_map_init: ts.Expression; }; name_generator: (config: Configuration, node: ts.Node, message: string) => string; diff --git a/tools/trgen/ts_transformer.ts b/tools/trgen/ts_transformer.ts index d5a7bae6..8b2fed37 100644 --- a/tools/trgen/ts_transformer.ts +++ b/tools/trgen/ts_transformer.ts @@ -9,6 +9,7 @@ import {TranslationEntry} from "./generator"; export interface Config { target_file?: string; verbose?: boolean; + optimized?: boolean; } let process_config: Config; @@ -37,6 +38,7 @@ export default function(program: ts.Program, config?: Config) : (context: ts.Tra return ctx => transformer(ctx) as any; } +let processed = []; const translations: TranslationEntry[] = []; const transformer = (context: ts.TransformationContext) => (rootNode: ts.Node) => { @@ -51,10 +53,17 @@ const transformer = (context: ts.TransformationContext) => } else if(rootNode.kind == ts.SyntaxKind.SourceFile) { const file = rootNode as ts.SourceFile; + if(processed.findIndex(e => e === file.fileName) !== -1) { + console.log("Skipping %s (already processed)", file.fileName); + return rootNode; + } + processed.push(file.fileName); console.log("Processing " + file.fileName); const result = ts_generator.transform({ use_window: false, - replace_cache: true + replace_cache: true, + module: true, + optimized: process_config.optimized }, context, file); translations.push(...result.translations); return result.node; diff --git a/webpack-client.config.ts b/webpack-client.config.ts index 279fd6b1..13b0a549 100644 --- a/webpack-client.config.ts +++ b/webpack-client.config.ts @@ -1,28 +1,21 @@ import * as path from "path"; -const config = require("./webpack.config"); +import * as config_base from "./webpack.config"; -let isDevelopment = process.env.NODE_ENV === 'development'; -isDevelopment = true; - -config.entry = { +const config = config_base.config(); +Object.assign(config.entry, { "client-app": "./client/js/index.ts" -}; +}); -config.resolve.alias = { +Object.assign(config.resolve.alias, { "tc-shared": path.resolve(__dirname, "shared/js"), /* backend hasn't declared but its available via "require()" */ "tc-backend": path.resolve(__dirname, "shared/backend.d"), -}; +}); -config.externals = [ - { - "tc-loader": "window loader" - }, - (context, request: string, callback) => { - if (request.startsWith("tc-backend/")) - return callback(null, `window["backend-loader"].require("${request}")`); - callback(); - } -]; +config.externals.push((context, request: string, callback) => { + if (request.startsWith("tc-backend/")) + return callback(null, `window["backend-loader"].require("${request}")`); + callback(); +}); export = config; \ No newline at end of file diff --git a/webpack-web.config.ts b/webpack-web.config.ts index a5b51b30..e72c008d 100644 --- a/webpack-web.config.ts +++ b/webpack-web.config.ts @@ -1,115 +1,16 @@ -import * as ts from "typescript"; -import trtransformer, {Config} from "./tools/trgen/ts_transformer"; +import * as path from "path"; +import * as config_base from "./webpack.config"; -const path = require('path'); -const webpack = require("webpack"); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const ManifestGenerator = require("./webpack/ManifestPlugin"); -const WorkerPlugin = require('worker-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const config = config_base.config(); +Object.assign(config.entry, { + "shared-app": "./web/js/index.ts" +}); -let isDevelopment = process.env.NODE_ENV === 'development'; -isDevelopment = true; -module.exports = { - entry: { - "shared-app": "./web/js/index.ts" - }, - devtool: isDevelopment ? "inline-source-map" : undefined, - mode: isDevelopment ? "development" : "production", - plugins: [ - new CleanWebpackPlugin(), - new MiniCssExtractPlugin({ - filename: isDevelopment ? '[name].css' : '[name].[hash].css', - chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' - }), - new ManifestGenerator({ - file: path.join(__dirname, "dist/manifest.json") - }), - new WorkerPlugin(), - //new BundleAnalyzerPlugin() - /* - new CircularDependencyPlugin({ - //exclude: /a\.js|node_modules/, - failOnError: true, - allowAsyncCycles: false, - cwd: process.cwd(), - }) - */ - new webpack.optimize.AggressiveSplittingPlugin({ - minSize: 1024 * 8, - maxSize: 1024 * 128 - }) - ], - module: { - rules: [ - { - test: /\.s[ac]ss$/, - loader: [ - isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - modules: true, - sourceMap: isDevelopment - } - }, - { - loader: 'sass-loader', - options: { - sourceMap: isDevelopment - } - } - ] - }, - { - test: /\.tsx?$/, - exclude: /node_modules/, +Object.assign(config.resolve.alias, { + "tc-shared": path.resolve(__dirname, "shared/js"), + "tc-backend/web": path.resolve(__dirname, "web/js"), + "tc-backend": path.resolve(__dirname, "web/js"), + "tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"), +}); - loader: [ - { - loader: 'ts-loader', - options: { - transpileOnly: true, - getCustomTransformers: (prog: ts.Program) => { - return { - before: [trtransformer(prog, {})] - }; - } - } - } - ] - }, - { - test: /\.was?t$/, - loader: [ - "./webpack/WatLoader.js" - ] - } - ], - }, - resolve: { - extensions: ['.tsx', '.ts', '.js', ".scss"], - alias: { - "tc-shared": path.resolve(__dirname, "shared/js"), - "tc-backend/web": path.resolve(__dirname, "web/js"), - "tc-backend": path.resolve(__dirname, "web/js"), - "tc-generated/codec/opus": path.resolve(__dirname, "asm/generated/TeaWeb-Worker-Codec-Opus.js"), - //"tc-backend": path.resolve(__dirname, "shared/backend.d"), - }, - }, - externals: { - "tc-loader": "window loader" - }, - output: { - filename: '[contenthash].js', - path: path.resolve(__dirname, 'dist'), - publicPath: "js/" - }, - optimization: { - splitChunks: { }, - minimize: !isDevelopment, - minimizer: [new TerserPlugin()] - } -}; \ No newline at end of file +export = config; \ No newline at end of file diff --git a/webpack.config.ts b/webpack.config.ts index e3719ab2..e8c3262d 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,5 +1,6 @@ import * as ts from "typescript"; -import trtransformer, {Config} from "./tools/trgen/ts_transformer"; +import * as fs from "fs"; +import trtransformer from "./tools/trgen/ts_transformer"; const path = require('path'); const webpack = require("webpack"); @@ -10,10 +11,32 @@ const WorkerPlugin = require('worker-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); -let isDevelopment = process.env.NODE_ENV === 'development'; +export let isDevelopment = process.env.NODE_ENV === 'development'; isDevelopment = true; -export = { - entry: {}, /* will be individually set */ + +const generate_definitions = () => { + const git_rev = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString(); + let version; + if(git_rev.indexOf("/") === -1) + version = git_rev; + else + version = fs.readFileSync(path.join(__dirname, ".git", git_rev.substr(5).trim())).toString().substr(0, 7); + + return { + "__build": { + target: JSON.stringify("web"), + mode: JSON.stringify(isDevelopment ? "debug" : "release"), + version: JSON.stringify(version), + timestamp: Date.now(), + entry_chunk_name: JSON.stringify("shared-app") + } as BuildDefinitions + } as any; +}; + +export const config = () => { return { + entry: { + "loader": "./loader/app/index.ts" + }, devtool: isDevelopment ? "inline-source-map" : undefined, mode: isDevelopment ? "development" : "production", @@ -24,7 +47,8 @@ export = { chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css' }), new ManifestGenerator({ - file: path.join(__dirname, "dist/manifest.json") + file: path.join(__dirname, "dist/manifest.json"), + base: __dirname }), new WorkerPlugin(), //new BundleAnalyzerPlugin() @@ -39,7 +63,8 @@ export = { isDevelopment ? undefined : new webpack.optimize.AggressiveSplittingPlugin({ minSize: 1024 * 8, maxSize: 1024 * 128 - }) + }), + new webpack.DefinePlugin(generate_definitions()) ].filter(e => !!e), module: { rules: [ @@ -73,7 +98,9 @@ export = { transpileOnly: true, getCustomTransformers: (prog: ts.Program) => { return { - before: [trtransformer(prog, {})] + before: [trtransformer(prog, { + optimized: true + })] }; } } @@ -90,19 +117,21 @@ export = { }, resolve: { extensions: ['.tsx', '.ts', '.js', ".scss"], - alias: { }, /* will be individually set */ - }, - externals: { - "tc-loader": "window loader" + alias: { }, }, + externals: [ + {"tc-loader": "window loader"} + ] as any[], output: { filename: isDevelopment ? '[name].js' : '[contenthash].js', path: path.resolve(__dirname, 'dist'), publicPath: "js/" }, optimization: { - splitChunks: { }, + splitChunks: { + + }, minimize: !isDevelopment, minimizer: [new TerserPlugin()] } -}; \ No newline at end of file +}}; \ No newline at end of file diff --git a/webpack/ManifestPlugin.ts b/webpack/ManifestPlugin.ts index c7381fa4..c4ba6475 100644 --- a/webpack/ManifestPlugin.ts +++ b/webpack/ManifestPlugin.ts @@ -1,8 +1,10 @@ import * as webpack from "webpack"; import * as fs from "fs"; +import * as path from "path"; interface Options { file?: string; + base: string; } class ManifestGenerator { @@ -10,32 +12,51 @@ class ManifestGenerator { readonly options: Options; constructor(options: Options) { - this.options = options || {}; + this.options = options || { base: __dirname }; } apply(compiler: webpack.Compiler) { compiler.hooks.afterCompile.tap(this.constructor.name, compilation => { const chunks_data = {}; for(const chunk_group of compilation.chunkGroups) { - console.log(chunk_group.options.name); const js_files = []; + const modules = []; + for(const chunk of chunk_group.chunks) { if(chunk.files.length !== 1) throw "expected only one file per chunk"; - const file = chunk.files[0]; - console.log("Chunk: %s - %s - %s", chunk.id, chunk.hash, file); - //console.log(chunk); - //console.log(" - %s - %o", chunk.id, chunk); js_files.push({ hash: chunk.hash, - file: file - }) + file: chunk.files[0] + }); + + + for(const module of chunk._modules) { + if(!module.type.startsWith("javascript/")) + continue; + + if(!module.resource || !module.context) + continue; + + if(module.context !== path.dirname(module.resource)) + throw "invalid context/resource relation"; + + modules.push({ + id: module.id, + context: path.relative(this.options.base, module.context).replace(/\\/g, "/"), + resource: path.basename(module.resource) + }); + } } - chunks_data[chunk_group.options.name] = js_files; + + chunks_data[chunk_group.options.name] = { + files: js_files, + modules: modules + }; } this.manifest_content = { - version: 1, + version: 2, chunks: chunks_data }; }); diff --git a/webpack/build-definitions.d.ts b/webpack/build-definitions.d.ts new file mode 100644 index 00000000..fd641c6f --- /dev/null +++ b/webpack/build-definitions.d.ts @@ -0,0 +1,16 @@ +declare global { + interface BuildDefinitions { + target: "web" | "client"; + mode: "release" | "debug"; + + /* chunk for the loader to load initially */ + entry_chunk_name: string; + + version: string; + timestamp: number; + } + + const __build: BuildDefinitions; +} + +export {}; \ No newline at end of file From cafdf637cf79c343532b23ee7c0eeee986d4ce04 Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Thu, 2 Apr 2020 17:40:09 +0200 Subject: [PATCH 22/23] Updated the build scripts for an successfully client build --- file.ts | 20 ++---- loader/app/loader/loader.ts | 2 + package.json | 9 +-- scripts/build.sh | 93 +++++++++++++++++++++++++++ scripts/client_build.sh | 103 ------------------------------ scripts/deploy_ui_files.sh | 53 +++++++-------- scripts/git_index.sh | 6 +- scripts/web_build.sh | 117 ---------------------------------- scripts/web_package.sh | 12 ++-- shared/css/generate_packed.sh | 12 ++-- shared/generate_packed.sh | 56 ---------------- tsbaseconfig.json | 24 +++++++ webpack-client.config.ts | 2 +- webpack-web.config.ts | 2 +- webpack.config.ts | 19 +++--- 15 files changed, 183 insertions(+), 347 deletions(-) create mode 100644 scripts/build.sh delete mode 100755 scripts/client_build.sh delete mode 100755 scripts/web_build.sh delete mode 100755 shared/generate_packed.sh create mode 100644 tsbaseconfig.json diff --git a/file.ts b/file.ts index a011f464..ca3bd5c3 100644 --- a/file.ts +++ b/file.ts @@ -750,7 +750,6 @@ namespace server { if(!p.startsWith("/")) p = "/" + p; if(p.endsWith(".html")) { const np = await generator.search_http_file(files, p.substr(0, p.length - 5) + ".php", options.search_options); - console.log("%s => %s", p, np); if(np) p = p.substr(0, p.length - 5) + ".php"; } serve_file(p, url.query, response); @@ -1007,18 +1006,12 @@ async function main_develop(node: boolean, target: "client" | "web", port: numbe } async function git_tag() { - const exec = util.promisify(cp.exec); - - /* check if we've any uncommited changes */ - { - let { stdout, stderr } = await exec("git diff-index HEAD -- . ':!asm/libraries/' ':!package-lock.json' ':!vendor/'"); - if(stderr) throw stderr; - if(stdout) return "0000000"; - } - - let { stdout, stderr } = await exec("git rev-parse --short HEAD"); - if(stderr) throw stderr; - return stdout.substr(0, 7); + const git_rev = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString(); + let version; + if(git_rev.indexOf("/") === -1) + return git_rev.substr(0, 7); + else + return fs.readFileSync(path.join(__dirname, ".git", git_rev.substr(5).trim())).toString().substr(0, 7); } async function main_generate(target: "client" | "web", mode: "rel" | "dev", dest_path: string, args: any[]) { @@ -1219,4 +1212,5 @@ main(process.argv.slice(2)).then(ignore_exit => { }).catch(error => { console.error("Failed to execute application. Exception reached execution root!"); console.error(error); + process.exit(1); }); \ No newline at end of file diff --git a/loader/app/loader/loader.ts b/loader/app/loader/loader.ts index c492fbfd..5f1a5991 100644 --- a/loader/app/loader/loader.ts +++ b/loader/app/loader/loader.ts @@ -259,6 +259,8 @@ export type ErrorHandler = (message: string, detail: string) => void; let _callback_critical_error: ErrorHandler; let _callback_critical_called: boolean = false; export function critical_error(message: string, detail?: string) { + document.getElementById("loader-overlay").classList.add("started"); + if(_callback_critical_called) { console.warn("[CRITICAL] %s", message); if(typeof(detail) === "string") diff --git a/package.json b/package.json index 9069e3a7..8ce13420 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,10 @@ "main": "main.js", "directories": {}, "scripts": { - "compile-sass": "sass --update .:.", - "compile-file-helper": "tsc file.ts", - "build-worker-codec": "tsc -p web/js/workers/tsconfig_worker_codec.json", - "build-worker-pow": "tsc -p shared/js/workers/tsconfig_worker_pow.json", - "build-worker": "npm run build-worker-codec; npm run build-worker-pow;", + "compile-sass": "sass --update shared/css/:shared/css/ web/css/:web/css/ client/css/:client/css/", + "compile-project-base": "tsc -p tsbaseconfig.json", "dtsgen": "node tools/dtsgen/index.js", "trgen": "node tools/trgen/index.js", - "ttsc": "ttsc", "sass": "sass", "csso": "csso", "rebuild-structure-web-dev": "php files.php generate web dev", @@ -59,7 +55,6 @@ "terser": "^4.2.1", "terser-webpack-plugin": "latest", "ts-loader": "^6.2.2", - "ttypescript": "^1.5.10", "typescript": "3.6.5", "wabt": "^1.0.13", "webpack": "^4.42.1", diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 00000000..26ea18e1 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC1090 +source "$(dirname "$0")/resolve_commands.sh" +cd "$(dirname "$0")/../" || { echo "Failed to enter the base directory"; exit 1; } + + +if [[ $# -lt 2 ]]; then + echo "Invalid argument count!" + exit 1 +fi + +if [[ "$1" == "client" ]]; then + build_target="client" +elif [[ "$1" == "web" ]]; then + build_target="web" +else + echo "Invalid option $2" + echo 'Available options are: "web" or "client"' + exit 1 +fi + +if [[ "$2" == "development" ]] || [[ "$2" == "dev" ]] || [[ "$2" == "debug" ]]; then + build_type="development" +elif [[ "$2" == "release" ]] || [[ "$2" == "rel" ]]; then + build_type="release" +else + if [[ $# -lt 2 ]]; then + echo "Invalid argument count!" + else + echo "Invalid option $2" + fi + echo 'Available options are: "development" or "dev", "release" or "rel"' + exit 1 +fi + +echo "Generating required project build files" +npm run compile-project-base; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to generate project build files" + exit 1 +fi + +echo "Generating required build tooks" +./tools/build_trgen.sh; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to build build_typescript translation generator" + exit 1 +fi + +echo "Generating style files" +npm run compile-sass; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to generate style files" + exit 1 +fi + +echo "Compile vendor XBBCode" +execute_ttsc -p ./vendor/xbbcode/tsconfig.json; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to build the XBBCode vendor" + exit 1 +fi + +if [[ "$build_type" == "release" ]]; then # Compile everything for release mode + echo "Packing generated css files" + ./shared/css/generate_packed.sh; _exit_code=$? + if [[ $_exit_code -ne 0 ]]; then + echo "Failed to package generated css files" + exit 1 + fi + + NODE_ENV=production npm run build-$build_target; _exit_code=$? + if [[ $_exit_code -ne 0 ]]; then + echo "Failed to build the $build_target applcation" + exit 1 + fi +elif [[ "$build_type" == "development" ]]; then + NODE_ENV=development npm run build-$build_target; _exit_code=$? + if [[ $_exit_code -ne 0 ]]; then + echo "Failed to build the $build_target applcation" + exit 1 + fi +fi + +echo "Generating environment" +node file.js generate $build_target ${build_type}; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to generate environment" + exit 1 +fi + +echo "$build_target builded successfully!" \ No newline at end of file diff --git a/scripts/client_build.sh b/scripts/client_build.sh deleted file mode 100755 index a47bb8a0..00000000 --- a/scripts/client_build.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash - -source `dirname $0`/resolve_commands.sh -BASEDIR=$(dirname "$0") -cd "$BASEDIR/../" - -source_path="client-api/environment/ui-files/raw" -if [[ "$1" == "development" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "debug" ]]; then - type="development" -elif [[ "$1" == "release" ]] || [[ "$1" == "rel" ]]; then - type="release" -else - if [[ $# -lt 1 ]]; then - echo "Invalid argument count!" - else - echo "Invalid option $1" - fi - echo 'Available options are: "development" or "dev", "release" or "rel"' - exit 1 -fi - -echo "Generating file helper script" -npm run compile-file-helper -if [[ $? -ne 0 ]]; then - echo "Failed to generate file helper" - exit 1 -fi - -echo "Generating style files" -npm run compile-sass -if [[ $? -ne 0 ]]; then - echo "Failed to generate style files" - exit 1 -fi - -echo "Generating web workers" -npm run build-worker-codec -if [[ $? -ne 0 ]]; then - echo "Failed to build web worker codec" - exit 1 -fi -npm run build-worker-pow -if [[ $? -ne 0 ]]; then - echo "Failed to build web worker pow" - exit 1 -fi - -#Lets build some tools -#dtsgen should be already build by build_declarations.sh -./tools/build_trgen.sh -if [[ $? -ne 0 ]]; then - echo "Failed to build typescript translation generator" - exit 1 -fi - -#Now lets build the declarations -echo "Building declarations" -./scripts/build_declarations.sh force -if [[ $? -ne 0 ]]; then - echo "Failed to generate declarations" - exit 1 -fi - -if [[ "$type" == "release" ]]; then #Compile everything for release mode - #Compile the shared source first - echo "Building shared source" - ./shared/generate_packed.sh - if [[ $? -ne 0 ]]; then - echo "Failed to build shared source" - exit 1 - fi - - #Now compile the web client itself - echo "Building client UI" - ./client/generate_packed.sh - if [[ $? -ne 0 ]]; then - echo "Failed to build web client" - exit 1 - fi -elif [[ "$type" == "development" ]]; then - echo "Building shared source" - execute_ttsc -p ./shared/tsconfig/tsconfig.json - if [[ $? -ne 0 ]]; then - echo "Failed to compile shared sources" - exit 1 - fi - - echo "Building client UI source" - execute_ttsc -p ./client/tsconfig/tsconfig.json - if [[ $? -ne 0 ]]; then - echo "Failed to compile web sources" - exit 1 - fi -fi - -echo "Generating environment" -node file.js generate client ${type} -if [[ $? -ne 0 ]]; then - echo "Failed to generate environment" - exit 1 -fi - -echo "Successfully build!" \ No newline at end of file diff --git a/scripts/deploy_ui_files.sh b/scripts/deploy_ui_files.sh index 31042ad1..b82e314a 100755 --- a/scripts/deploy_ui_files.sh +++ b/scripts/deploy_ui_files.sh @@ -1,12 +1,11 @@ #!/usr/bin/env bash -# ./scripts/deploy_ui_files.sh http://dev.clientapi.teaspeak.de/api.php test 1.1.0 +# Example usage: ./scripts/deploy_ui_files.sh http://dev.clientapi.teaspeak.de/api.php test 1.1.0 TMP_FILE_NAME="TeaSpeakUI.tar.gz" TMP_DIR_NAME="tmp" -BASEDIR=$(dirname "$0") -cd "$BASEDIR/../" +cd "$(dirname "$0")/../" || { echo "failed to enter base directory"; exit 1; } if [[ "$#" -ne 3 ]]; then echo "Illegal number of parameters (url | channel | required version)" @@ -18,6 +17,7 @@ if [[ ! -d client-api/environment/ui-files/ ]]; then exit 1 fi +# shellcheck disable=SC2154 if [[ "${teaclient_deploy_secret}" == "" ]]; then echo "Missing deploy secret!" exit 1 @@ -26,33 +26,36 @@ fi if [[ -e "${TMP_FILE_NAME}" ]]; then echo "Temp file already exists!" echo "Deleting it!" - rm ${TMP_FILE_NAME} - if [[ $? -ne 0 ]]; then + + if ! rm ${TMP_FILE_NAME}; then echo "Failed to delete file" exit 1 fi fi GIT_HASH=$(git rev-parse --verify --short HEAD) -APPLICATION_VERSION=$(cat package.json | python -c "import sys, json; print(json.load(sys.stdin)['version'])") +APPLICATION_VERSION=$(< package.json python -c "import sys, json; print(json.load(sys.stdin)['version'])") echo "Git hash ${GIT_HASH} on version ${APPLICATION_VERSION} on channel $2" #Packaging the app -cd client-api/environment/ui-files/ +cd client-api/environment/ui-files/ || { + echo "Missing UI files directory" + exit 1 +} if [[ -e ${TMP_DIR_NAME} ]]; then - rm -r ${TMP_DIR_NAME} - if [[ $? -ne 0 ]]; then + if ! rm -r ${TMP_DIR_NAME}; then echo "Failed to remove temporary directory!" exit 1 fi fi cp -rL raw ${TMP_DIR_NAME} -for file in $(find ${TMP_DIR_NAME} -name '*.php'); do +while IFS= read -r -d '' file +do echo "Evaluating php file $file" __cur_dir=$(pwd) - cd $(dirname ${file}) - RESULT=$(php "$(basename ${file})" 2> /dev/null) + cd "$(dirname "${file}")" || { echo "Failed to enter php file directory"; exit 1; } + php_result=$(php "$(basename "${file}")" 2> /dev/null) CODE=$? if [[ ${CODE} -ne 0 ]]; then echo "Failed to evaluate php file $file!" @@ -60,14 +63,14 @@ for file in $(find ${TMP_DIR_NAME} -name '*.php'); do exit 1 fi - cd ${__cur_dir} - echo "${RESULT}" > "${file::-4}.html" -done + cd "${__cur_dir}" || { echo "failed to enter original dir"; exit 1; } + echo "${php_result}" > "${file::-4}.html" +done < <(find "${TMP_DIR_NAME}" -name '*.php' -print0) -cd ${TMP_DIR_NAME} -tar chvzf ${TMP_FILE_NAME} * -if [[ $? -ne 0 ]]; then - echo "Failed to pack file" +cd ${TMP_DIR_NAME} || { echo "failed to enter the temp dir"; exit 1; } +tar chvzf ${TMP_FILE_NAME} ./*; _exit_code=$? +if [[ $_exit_code -ne 0 ]]; then + echo "Failed to pack file ($_exit_code)" exit 1 fi mv ${TMP_FILE_NAME} ../../../../ @@ -84,16 +87,16 @@ RESP=$(curl \ -F "version=$APPLICATION_VERSION" \ -F "git_ref=$GIT_HASH" \ -F "secret=${teaclient_deploy_secret}" \ - -F "file=@`pwd`/TeaSpeakUI.tar.gz" \ - $1 + -F "file=@$(pwd)/TeaSpeakUI.tar.gz" \ + "$1" ) echo "$RESP" -SUCCESS=$(echo ${RESP} | python -c "import sys, json; print(json.load(sys.stdin)['success'])") +SUCCESS=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['success'])") if [[ ! "${SUCCESS}" == "True" ]]; then - ERROR=$(echo ${RESP} | python -c "import sys, json; print(json.load(sys.stdin)['error'])" 2>/dev/null) - if [[ $? -ne 0 ]]; then - ERROR=$(echo ${RESP} | python -c "import sys, json; print(json.load(sys.stdin)['msg'])" 2>/dev/null) + ERROR=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['error'])" 2>/dev/null); _exit_code=$? + if [[ $_exit_code -ne 0 ]]; then + ERROR=$(echo "${RESP}" | python -c "import sys, json; print(json.load(sys.stdin)['msg'])" 2>/dev/null) fi echo "Failed to deploy build!" echo "${ERROR}" diff --git a/scripts/git_index.sh b/scripts/git_index.sh index 7c4a963f..6dc166df 100755 --- a/scripts/git_index.sh +++ b/scripts/git_index.sh @@ -14,13 +14,13 @@ if [[ "$response" != "" ]]; then exit 1 else if [[ "$1" == "sort-tag" ]]; then - echo "$(git rev-parse --short HEAD)" + git rev-parse --short HEAD fi if [[ "$1" == "name" ]]; then - echo "$(git rev-parse --short HEAD)" + git rev-parse --short HEAD fi if [[ "$1" == "file-name" ]]; then - echo "$(git rev-parse --short HEAD)" + git rev-parse --short HEAD fi exit 0 fi \ No newline at end of file diff --git a/scripts/web_build.sh b/scripts/web_build.sh deleted file mode 100755 index f4cd94ee..00000000 --- a/scripts/web_build.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env bash - -source `dirname $0`/resolve_commands.sh -BASEDIR=$(dirname "$0") -cd "$BASEDIR/../" - -if [[ "$1" == "development" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "debug" ]]; then - source_path="web/environment/development" - type="development" -elif [[ "$1" == "release" ]] || [[ "$1" == "rel" ]]; then - source_path="web/environment/release" - type="release" -else - if [[ $# -lt 1 ]]; then - echo "Invalid argument count!" - else - echo "Invalid option $1" - fi - echo 'Available options are: "development" or "dev", "release" or "rel"' - exit 1 -fi - -echo "Generating file helper script" -npm run compile-file-helper -if [[ $? -ne 0 ]]; then - echo "Failed to generate file helper" - exit 1 -fi - -echo "Compile vendor XBBCode" -execute_ttsc -p ./vendor/xbbcode/tsconfig.json -if [[ $? -ne 0 ]]; then - echo "Failed to build the XBBCode vendor" - exit 1 -fi - -execute_ttsc ./vendor/emoji-picker/src/jquery.lsxemojipicker.ts -if [[ $? -ne 0 ]]; then - echo "Failed to build the lsxemojipicker vendor" - exit 1 -fi - -echo "Generating style files" -npm run compile-sass -if [[ $? -ne 0 ]]; then - echo "Failed to generate style files" - exit 1 -fi - -echo "Generating web workers" -npm run build-worker-codec -if [[ $? -ne 0 ]]; then - echo "Failed to build web worker codec" - exit 1 -fi -npm run build-worker-pow -if [[ $? -ne 0 ]]; then - echo "Failed to build web worker pow" - exit 1 -fi - -#Lets build some tools -#dtsgen should be already build by build_declarations.sh -./tools/build_trgen.sh -if [[ $? -ne 0 ]]; then - echo "Failed to build typescript translation generator" - exit 1 -fi - -#Now lets build the declarations -echo "Building declarations" -./scripts/build_declarations.sh -if [[ $? -ne 0 ]]; then - echo "Failed to generate declarations" - exit 1 -fi - -if [[ "$type" == "release" ]]; then #Compile everything for release mode - #Compile the shared source first - echo "Building shared source" - ./shared/generate_packed.sh - if [[ $? -ne 0 ]]; then - echo "Failed to build shared source" - exit 1 - fi - - #Now compile the web client itself - echo "Building web client" - ./web/generate_packed.sh - if [[ $? -ne 0 ]]; then - echo "Failed to build web client" - exit 1 - fi -elif [[ "$type" == "development" ]]; then - echo "Building shared source" - execute_ttsc -p ./shared/tsconfig/tsconfig.json - if [[ $? -ne 0 ]]; then - echo "Failed to compile shared sources" - exit 1 - fi - - echo "Building web client source" - execute_ttsc -p ./web/tsconfig/tsconfig.json - if [[ $? -ne 0 ]]; then - echo "Failed to compile web sources" - exit 1 - fi -fi - -echo "Generating environment" -node file.js generate web ${type} -if [[ $? -ne 0 ]]; then - echo "Failed to generate environment" - exit 1 -fi - -echo "Successfully build!" \ No newline at end of file diff --git a/scripts/web_package.sh b/scripts/web_package.sh index b9542cd6..091e6d9e 100755 --- a/scripts/web_package.sh +++ b/scripts/web_package.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -BASEDIR=$(dirname "$0") -cd "$BASEDIR/../" +cd "$(dirname "$0")/../" || { echo "Failed to enter base directory"; exit 1; } if [[ "$1" == "development" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "dev" ]]; then source_path="web/environment/development" @@ -36,19 +35,18 @@ fi if [[ -e ${NAME} ]]; then echo "Found old file. Deleting it." - rm -r ${NAME} + rm -r "${NAME}" fi current_path=$(pwd) -cd "$source_path" -zip -9 -r ${NAME} * +cd "$source_path" || { echo "Failed to enter source path"; exit 1; } -if [[ $? -ne 0 ]]; then +if zip -9 -r "${NAME}" ./*; then echo "Failed to package environment!" exit 1 fi -cd "$current_path" +cd "$current_path" || { echo "Failed to reenter source path"; exit 1; } mv "${source_path}/${NAME}" . echo "Release package successfully packaged!" diff --git a/shared/css/generate_packed.sh b/shared/css/generate_packed.sh index f92f1fa7..4e2db23f 100755 --- a/shared/css/generate_packed.sh +++ b/shared/css/generate_packed.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cd $(dirname $0) +cd "$(dirname "$0")" || exit 1 #find css/static/ -name '*.css' -exec cat {} \; | npm run csso -- --output `pwd`/generated/static/base.css @@ -62,20 +62,20 @@ files=( target_file=`pwd`/../generated/static/base.css if [[ ! -d $(dirname ${target_file}) ]]; then - echo "Creating target path ($(dirname ${target_file}))" - mkdir -p $(dirname ${target_file}) + echo "Creating target path ($(dirname "${target_file}"))" + mkdir -p $(dirname "${target_file}") if [[ $? -ne 0 ]]; then echo "Failed to create target path!" exit 1 fi fi -echo "/* Auto generated merged CSS file */" > ${target_file} +echo "/* Auto generated merged CSS file */" > "${target_file}" for file in "${files[@]}"; do if [[ ${file} =~ css/* ]]; then file="./${file:4}" fi - cat ${file} >> ${target_file} + cat "${file}" >> "${target_file}" done -cat ${target_file} | npm run csso -- --output `pwd`/../generated/static/base.css \ No newline at end of file +cat "${target_file}" | npm run csso -- --output "$(pwd)/../generated/static/base.css" \ No newline at end of file diff --git a/shared/generate_packed.sh b/shared/generate_packed.sh deleted file mode 100755 index 9bd70797..00000000 --- a/shared/generate_packed.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -BASEDIR=$(dirname "$0") -cd "$BASEDIR" -source ../scripts/resolve_commands.sh -# The app loader -execute_ttsc -p tsconfig/tsconfig_packed_loader_app.json -if [[ $? -ne 0 ]]; then - echo "Failed to generate packed loader file!" - exit 1 -fi - -npm run minify-web-rel-file `pwd`/generated/loader_app.min.js `pwd`/generated/loader_app.js -if [[ $? -ne 0 ]]; then - echo "Failed to minimize packed loader file!" - exit 1 -fi - -# The popup certaccept loader -execute_ttsc -p tsconfig/tsconfig_packed_loader_certaccept.json -if [[ $? -ne 0 ]]; then - echo "Failed to generate packed loader file!" - exit 1 -fi - -npm run minify-web-rel-file `pwd`/generated/loader_certaccept.min.js `pwd`/generated/loader_certaccept.js -if [[ $? -ne 0 ]]; then - echo "Failed to minimize packed loader file!" - exit 1 -fi - -# The main shared source -execute_ttsc -p tsconfig/tsconfig_packed.json -if [[ $? -ne 0 ]]; then - echo "Failed to generate packed file!" - exit 1 -fi - -# The certaccept source -execute_ttsc -p tsconfig/tsconfig_packed_certaccept.json -if [[ $? -ne 0 ]]; then - echo "Failed to generate packed certaccept file!" - exit 1 -fi - -npm run minify-web-rel-file `pwd`/generated/certaccept.min.js `pwd`/generated/certaccept.js -if [[ $? -ne 0 ]]; then - echo "Failed to minimize the certaccept file!" - exit 1 -fi - -# Create packed CSS file -./css/generate_packed.sh - -echo "Packed file generated!" -exit 0 \ No newline at end of file diff --git a/tsbaseconfig.json b/tsbaseconfig.json new file mode 100644 index 00000000..2b8684bd --- /dev/null +++ b/tsbaseconfig.json @@ -0,0 +1,24 @@ +/* TSC config for the core files of the application, like the file manager and webpack */ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "sourceMap": true, + "lib": ["es6", "dom"], + "removeComments": false, + }, + "include": [ + "webpack.config.ts", + "webpack-client.config.ts", + "webpack-web.config.ts", + + "webpack/build-definitions.d.ts", + "webpack/ManifestPlugin.ts", + "webpack/WatLoader.ts", + + "file.ts" + ], + "exclude": [ + "node_modules", + ] +} \ No newline at end of file diff --git a/webpack-client.config.ts b/webpack-client.config.ts index 13b0a549..4866bbcf 100644 --- a/webpack-client.config.ts +++ b/webpack-client.config.ts @@ -1,7 +1,7 @@ import * as path from "path"; import * as config_base from "./webpack.config"; -const config = config_base.config(); +const config = config_base.config("client"); Object.assign(config.entry, { "client-app": "./client/js/index.ts" }); diff --git a/webpack-web.config.ts b/webpack-web.config.ts index e72c008d..214bd42c 100644 --- a/webpack-web.config.ts +++ b/webpack-web.config.ts @@ -1,7 +1,7 @@ import * as path from "path"; import * as config_base from "./webpack.config"; -const config = config_base.config(); +const config = config_base.config("web"); Object.assign(config.entry, { "shared-app": "./web/js/index.ts" }); diff --git a/webpack.config.ts b/webpack.config.ts index e8c3262d..bda1799b 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -12,9 +12,8 @@ const TerserPlugin = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); export let isDevelopment = process.env.NODE_ENV === 'development'; -isDevelopment = true; - -const generate_definitions = () => { +console.log("Webpacking for %s (%s)", isDevelopment ? "development" : "production", process.env.NODE_ENV || "NODE_ENV not specified"); +const generate_definitions = (target: string) => { const git_rev = fs.readFileSync(path.join(__dirname, ".git", "HEAD")).toString(); let version; if(git_rev.indexOf("/") === -1) @@ -24,16 +23,16 @@ const generate_definitions = () => { return { "__build": { - target: JSON.stringify("web"), + target: JSON.stringify(target), mode: JSON.stringify(isDevelopment ? "debug" : "release"), version: JSON.stringify(version), timestamp: Date.now(), - entry_chunk_name: JSON.stringify("shared-app") + entry_chunk_name: JSON.stringify(target === "web" ? "shared-app" : "client-app") } as BuildDefinitions } as any; }; -export const config = () => { return { +export const config = (target: "web" | "client") => { return { entry: { "loader": "./loader/app/index.ts" }, @@ -64,7 +63,7 @@ export const config = () => { return { minSize: 1024 * 8, maxSize: 1024 * 128 }), - new webpack.DefinePlugin(generate_definitions()) + new webpack.DefinePlugin(generate_definitions(target)) ].filter(e => !!e), module: { rules: [ @@ -123,7 +122,11 @@ export const config = () => { return { {"tc-loader": "window loader"} ] as any[], output: { - filename: isDevelopment ? '[name].js' : '[contenthash].js', + filename: (chunkData) => { + if(chunkData.chunk.name === "loader") + return "loader.js"; + return isDevelopment ? '[name].js' : '[contenthash].js'; + }, path: path.resolve(__dirname, 'dist'), publicPath: "js/" }, From 7c98e5ec7dcb26fb91b5b7a131b87d53fd13b681 Mon Sep 17 00:00:00 2001 From: WolverinDEV <git@teaspeak.de> Date: Thu, 2 Apr 2020 17:58:33 +0200 Subject: [PATCH 23/23] Updated the gitignore files and some small changes --- .gitignore | 39 ++++++++++++++++++----------- asm/.gitignore | 4 ++- asm/download_compiled_files.sh | 0 client/generate_packed.sh | 0 {tools/dtsgen => loader}/.gitignore | 0 scripts/build_declarations.sh | 0 scripts/cleanup.sh | 0 scripts/deploy_ui_files.sh | 0 scripts/git_index.sh | 0 scripts/resolve_commands.sh | 0 scripts/travis_deploy.sh | 0 scripts/web_package.sh | 0 shared/css/generate_packed.sh | 0 shared/generate_declarations.sh | 0 shared/generate_i18n_gtranslate.py | 0 shared/generate_translations.sh | 0 tools/.gitignore | 8 ++++++ tools/build_dtsgen.sh | 0 tools/build_trgen.sh | 0 tools/trgen/.gitignore | 2 -- tools/trgen/bin/tsc.sh | 0 web/generate_packed.sh | 0 22 files changed, 36 insertions(+), 17 deletions(-) mode change 100755 => 100644 asm/download_compiled_files.sh mode change 100755 => 100644 client/generate_packed.sh rename {tools/dtsgen => loader}/.gitignore (100%) mode change 100755 => 100644 scripts/build_declarations.sh mode change 100755 => 100644 scripts/cleanup.sh mode change 100755 => 100644 scripts/deploy_ui_files.sh mode change 100755 => 100644 scripts/git_index.sh mode change 100755 => 100644 scripts/resolve_commands.sh mode change 100755 => 100644 scripts/travis_deploy.sh mode change 100755 => 100644 scripts/web_package.sh mode change 100755 => 100644 shared/css/generate_packed.sh mode change 100755 => 100644 shared/generate_declarations.sh mode change 100755 => 100644 shared/generate_i18n_gtranslate.py mode change 100755 => 100644 shared/generate_translations.sh create mode 100644 tools/.gitignore mode change 100755 => 100644 tools/build_dtsgen.sh mode change 100755 => 100644 tools/build_trgen.sh delete mode 100644 tools/trgen/.gitignore mode change 100755 => 100644 tools/trgen/bin/tsc.sh mode change 100755 => 100644 web/generate_packed.sh diff --git a/.gitignore b/.gitignore index ed6e9cce..d12c83bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,31 @@ -asm/generated/ -node_modules/ -auth/certs/ -auth/js/auth.js.map - -.sass-cache/ +# Global ignorings .idea/ +node_modules/ +.sass-cache/ -vendor/emoji-picker/**/*.js -vendor/emoji-picker/**/*.js.map +/auth/certs/ +/auth/js/auth.js.map -TeaSpeakUI.tar.gz -TeaWeb-*.zip +/.idea/ -todo.txt +/vendor/emoji-picker/**/*.js +/vendor/emoji-picker/**/*.js.map +!/vendor/emoji-picker/webpack.config.js -tmp/ +# Some build output +/dist/ +/declarations/ -file.js -file.js.map \ No newline at end of file +# Don't add the created packages to git +/TeaSpeakUI.tar.gz +/TeaWeb-*.zip + +/todo.txt +/tmp/ + +# All out config files are .ts files +/*.js +/*.js.map + +/webpack/*.js +/webpack/*.js.map \ No newline at end of file diff --git a/asm/.gitignore b/asm/.gitignore index 706d8e4c..13e1dc53 100644 --- a/asm/.gitignore +++ b/asm/.gitignore @@ -1,4 +1,6 @@ generated/ build_/ libraries/opus/build_ -libraries/opus/out \ No newline at end of file +libraries/opus/out +cmake-build-*/ +libraries/opus/* \ No newline at end of file diff --git a/asm/download_compiled_files.sh b/asm/download_compiled_files.sh old mode 100755 new mode 100644 diff --git a/client/generate_packed.sh b/client/generate_packed.sh old mode 100755 new mode 100644 diff --git a/tools/dtsgen/.gitignore b/loader/.gitignore similarity index 100% rename from tools/dtsgen/.gitignore rename to loader/.gitignore diff --git a/scripts/build_declarations.sh b/scripts/build_declarations.sh old mode 100755 new mode 100644 diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh old mode 100755 new mode 100644 diff --git a/scripts/deploy_ui_files.sh b/scripts/deploy_ui_files.sh old mode 100755 new mode 100644 diff --git a/scripts/git_index.sh b/scripts/git_index.sh old mode 100755 new mode 100644 diff --git a/scripts/resolve_commands.sh b/scripts/resolve_commands.sh old mode 100755 new mode 100644 diff --git a/scripts/travis_deploy.sh b/scripts/travis_deploy.sh old mode 100755 new mode 100644 diff --git a/scripts/web_package.sh b/scripts/web_package.sh old mode 100755 new mode 100644 diff --git a/shared/css/generate_packed.sh b/shared/css/generate_packed.sh old mode 100755 new mode 100644 diff --git a/shared/generate_declarations.sh b/shared/generate_declarations.sh old mode 100755 new mode 100644 diff --git a/shared/generate_i18n_gtranslate.py b/shared/generate_i18n_gtranslate.py old mode 100755 new mode 100644 diff --git a/shared/generate_translations.sh b/shared/generate_translations.sh old mode 100755 new mode 100644 diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 00000000..21013c90 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,8 @@ +/dtsgen/**/*.js +/dtsgen/**/*.js.map + +# Output directory for some local tests +/dtsgen/test/ + +/trgen/**/*.js +/trgen/**/*.js.map \ No newline at end of file diff --git a/tools/build_dtsgen.sh b/tools/build_dtsgen.sh old mode 100755 new mode 100644 diff --git a/tools/build_trgen.sh b/tools/build_trgen.sh old mode 100755 new mode 100644 diff --git a/tools/trgen/.gitignore b/tools/trgen/.gitignore deleted file mode 100644 index 20efd1ec..00000000 --- a/tools/trgen/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/*.js -**/*.js.map \ No newline at end of file diff --git a/tools/trgen/bin/tsc.sh b/tools/trgen/bin/tsc.sh old mode 100755 new mode 100644 diff --git a/web/generate_packed.sh b/web/generate_packed.sh old mode 100755 new mode 100644